《招一个靠谱的iOS》6-10

本人参考GitHub《招聘一个靠谱的iOS》面试题参考答案(上)
6. @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的
7. @protocol 和 category 中如何使用 @property
8. runtime 如何实现 weak 属性
9. @property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?
10. weak属性需要在dealloc中置nil么?

6. @property的本质是什么?ivar、getter、setter是如何生成并添加到这个类中的?

(1)@property的本质是什么?

属性(property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter),即@property = ivar + setter + getter
属性(property)作为Objective-C的一项特性,主要的作用就在于封装对象中的数据。Objective-C对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中,“获取方法”(getter)用于读取变量值,而“设置方法”(setter)用于写入变量值。这个概念已经定型,并且经由“属性”这一特性而成为Objective-C 2.0的一部分。而在正规的Objective-C编码风格中,存取方法有着严格的命名规范。正因为有了这种严格的命名规范,所以Objective-C这门语言才能根据名称自动创建出存取方法。
属性也可以当做一种关键字,其表示:编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量。
property在runtime中是objc_property_t定义如下:
typedef struct objc_property *objc_property_t;
而objc_property是一个结构体,包括name和attributes,定义如下:

struct property_t {
    const char *name;
    const char *attributes;
};

attributes本质是objc_property_attribute_t,定义了property的一些属性,定义如下:

// Defines a property attribute
typedef struct {
    const char *name;     /**The name of the attribute**/
    const char *value;    /**The value of the attribute (usually empty) */
} objc_property_attribute_t;

attribute的具体内容包括:类型,原子性,内存语义和对应的实例变量。
例如:我们定义一个string的property@property (nonatomic, copy) NSString *string;,通过property_getAttributes(property)获取到attributes并打印出来之后的结果为T@"NSString",C,N,V_string,其中T就代表类型,可参阅Type Encodings,C就代表Copy,N代表nonatomic,V就代表对应的实例变量。

objc_property_t中尾部_t的来源:
_t的意思显然就是type。关于为什么要加_t。一个类型后面加了_t说明了这是一个POSIX或GNU保留类型,防止namespace pollution的。不然标准库里新加了什么类型说不定就和用户已经定义的类型重名了。所以POSIX规定自己扩展的类型都加_t,这样只要用户定义类型的时候不加_t就不会冲突。

General Information(B.2.12)

Defined Types
The requirement that additional types defined in this section end in "_t" was prompted by the problem of name space pollution. It is difficult to define a type (where that type is not one defined by POSIX.1-2008) in one header file and use it in another without adding symbols to the name space of the program. To allow implementors to provide their own types, all conforming applications are required to avoid symbols ending in "_t", which permits the implementor to provide additional types. Because a major use of types is in the definition of structure members, which can (and in many cases must) be added to the structures defined in POSIX.1-2008, the need for additional types is compelling.
The types, such as ushort and ulong, which are in common usage, are not defined in POSIX.1-2008 (although ushort_twould be permitted as an extension). They can be added to sys/types.husing a feature test macro (see POSIX.1 Symbols). A suggested symbol for these is _SYSIII. Similarly, the types like u_short would probably be best controlled by _BSD.

ivar、getter、setter是如何生成并添加到这个类中的?

“自动合成”(autosynthesis)
完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这个过程由编译器在编译期执行,所以编译器里看不到“合成方法的源代码”。除了生成方法代码getter、setter之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线作为实例变量的名字。

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

上述代码写出来的类与下面这种写法等效:

@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end

在本例中,会生成两个实例变量,其名称分别为_firstName与_lastName。也可以在类的实现代码里通过@synthesis语法来指定实例变量的名字。

@implementation Person
@synthesis firstName = _myFirstName;
@synthesis lastName = _myLastName;
@end

自动合成的工作原理是大致生成了五个东西:
1.OBJC_IVAR_ $类名$属性名:该属性的“偏移量”(offset),这个偏移量是“硬编码”(hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
2.setter与getter方法对应的实现函数
3.ivar_list:实例变量(成员变量)列表
4.method_list:方法列表
5.prop_list:属性列表
也就是说,我们每次添加一个属性,系统都会在ivar_list中添加一个实例变量(成员变量)的描述,在method_list中增加setter与getter方法的描述,在prop_list中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出stter于getter方法对应的实现。在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了能够读取正确字节数,系统对对象偏移量的指针进行了类型强转。

7. @protocol和category中如何使用@property

1.在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守协议的对象能实现该属性;
2.category使用@property只会生成setter和getter方法的声明,如果需要给category增加属性的实现,需要借助于运行时(runtime)的两个函数:objc_setAssociatedObject()和objc_getAssociatedObject()。

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)四个参数分别为:源对象, 关键字, 关联对象, 关联策略。

  • 源对象:指要给哪个对象设置关联对象;
  • 关键字:给源对象的添加的属性名称(关联的key),类型为静态指针,在实际应用时,会传一个静态变量的地址或get方法。
  • 关联对象:属性的值
  • 关联策略:是一组枚举(enum)。对应属性特指的assign,weak,strong,_unsafe_unretain。
关联策略 等价属性 说明
OBJC_ASSOCIATION_ASSIGN = 0 @property(assign) or @property(unsafe_unretained) 弱引用关联对象
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1 @property(strong, nonatomic) 强引用关联对象,且未非原子操作
OBJC_ASSOCIATION_COPY_NONATOMIC = 3 @property(copy, nonatomic) 复制关联对象,且为非原子操作
OBJC_ASSOCIATION_RETAIN = 01401 @property(strong, atomic) 强引用关联对象,且为原子操作
OBJC_ASSOCIATION_COPY = 01403 @property(copy, atomic) 复制关联对象,且未原子操作
(1)在protocol中使用@property:

Person.h:

// 设置协议,添加property
#import <Foundation/Foundation.h>
@protocol PersonDelegate<NSObject>
@property (nonatomic, copy) NSString *name;
@end
@interface Person:NSObject

@end

Student.h:

// 遵守Person协议 并声明实例变量
#import <Foundation/Foundation.h>
#import <Person.h>
@interface Student : Person <PersonDelegate>
{
    NSString *_name;
}
@end

Student.m:

//实现setter和getter
// 有两种方式实现setter和getter方法:自动实现和手动实现
//---自动实现setter和getter方法,用synthesize---//
@implementation Student
@synthesize name;
@end

//--------手动-----//
@implementation Student
- (void)setName:(NSString *)name
{
    _name = name;
}
- (NSString *)name
{
    return _name;
}
@end
(2)在category中使用@property:
@interface NSObject (Extension)
@property (nonatomic, strong) NSString *title;
@end

#import <objc/runtime.h>
@implementation NSObject (Extension)
//-------@selector(title)可以换成_cmd,_cmd 代指当前方法的选择子,也就是 @selector(title)---/
- (NSString *)title
{
   return objc_getAssociatedObject(self, @selector(title));
}
- (void)setTitle:(NSString *)title
{
    objc_setAssociatedObject(self, @selector(title), title, OBJC_ASSOCIATION_RETAIN);
}

8. runtime如何实现weak属性

runtime对于weak对象会放入一个hash表中。
(1)初始化时:runtime会调用objc_initWeak(&weak, obj)函数初始化一个新的weak指针,指向weak修饰的属性变量(weak)的地址,并将该变量初始化为0(nil)。
(2)添加引用时:初始化后,会将“赋值对象(obj)”作为参数,调用objc_storeWeak函数,该函数的作用是更新指针指向,创建对应的弱引用表,objc_storeWeak(&weak, obj);用weak指向的对象(obj)内存地址作为key,将weak修饰的属性变量(weak)的内存地址作为value。
(3)释放时,此对象的引用计数器为0的时候会dealloc,调用objc_destroyWeak(&weak);释放weak修饰的属性变量(weak),该函数将0作为参数,调用objc_storeWeak(&weak, 0);函数,当objc_storeWeak第二个参数为0(nil)时,调用clearDeallocating函数将weak的地址从weak表中删除。
objc_storeWeak函数会把第二个参数——赋值对象obj的内存地址作为键值key,将第一个参数——weak修饰的属性变量weak的内存地址&weak作为value,注册到weak表中,如果第二个参数obj为0(nil),那么把变量weak的内存地址&weak从weak表中删除。即,objc_storeWeak(value, key),并且当key变为nil,将value置nil。

9. @property中有哪些属性关键字?||@property后面可以有哪些修饰符?

属性可以拥有的特质分为五类(第五类不常见):

  1. 原子性——atomic/nonatomic特质
    默认情况下,由编译器合成的方法会通过锁定机制确保其原子性(atomic),如果属性具备nonatomic特质,则不适用自旋锁。属性特质默认为atomic。
  2. 读写权限——readwrite、readonly
    readwrite可读可写,系统为属性生成setter和getter方法;readonly只读,系统为属性生成getter方法。
  3. 内存管理语义——assign、strong、weak、unsafe_unretained、copy
    属性默认为assign(基本数据类型)或strong(对象类型)
    assign一般用于修饰基本数据类型,也可以用于修饰对象属性,该对象被销毁时,属性不自动置为nil,易造成野指针;
    strong用于强引;
    weak用于修饰对象类型,不能修饰基本数据类型,对象被释放时,属性自动置为nil;
    copy用于修饰对象类型,不能修饰基本数据类型,一般用于有可变类型子类的父类赋值,防止旧值无意间变动;
    unsafe_unretained在ARC中与assign相同。
  4. 方法名——getter=name,setter=name
    getter的用法:
    @property (nonatomic, getter = isOn) BOOL on;on的getter方法不再是- (BOOL)on,而是- (BOOL)isOn。
    setter的用法:
    setter一般用于特殊情况下,比如:
    在数据转模型的过程中,服务器返回的字段如果以init开头,则需要定义一个init开头的属性,此时默认生成的setter和getter方法也会以init开头,而编译器会把所有以init开头的方法当成初始化方法,而初始化方法只能返回self类型,因此编译器会报错。
    此时就可以使用下面的方式,来避免编译器报错:
    @property (nonatomic, strong, getter = P_initBy, setter = setP_initBy:) NSString *initBy;
  5. 不常用的nonnull,null_resettable,nullable
    默认为nullable
    nullable表示修饰的属性或参数可以为空;
    nonnull表示修饰的属性或参数不能为空;
    null_resettable表示get方法不能返回为空,set方法可以为空。
    null_resettable用法:
    @property (nonatomic, strong, null_resettable) NSNumber *number;
- (NSNumber *)number
{
    if(_number == nil){
        _number1 = @11;
    }
    return _number;
}

10. weak属性需要在dealloc中置nil么?

不需要。
在ARC环境无论是强指针还是弱指针都无需在dealloc设置为nil,ARC会自动帮我们处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352