本文是想各位同学们说明一下有关创建成员变量时,各个属性的意义以及一些偏底层的知识,大多数需要了解,因为面试官一般都会问到。首先说明一下定义一个成员变量时,@property是干什么的。
一、 @property
@property 其实就是在编译阶段由编译器自动帮我们生成 ivar 成员变量,getter 方法,setter 方法。
使用“自动合成”( autosynthesis)这个过程由编译器在编译阶段执行自动合成,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成 getter、setter 方法之外,编译器还要自动向类中添加成员变量(在属性名前面加下划线,以此作为实例变量的名字)。为了搞清属性是怎么实现的,反编译相关的代码,他大致生成了五个东西:
OBJC_IVAR_$类名$属性名称
// 该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表 示该变量距离存放对象的内存区域的起始地址有多远
实际流程:
每次增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在 prop_list 中增加一个属性的描述,计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现。
在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。
接下来说一道常见的笔试题
二、 readwrite,readonly,assign,retain,copy,nonatomic,atomic,strong,weak属性的作用分别是什么?
readwrite 此标记说明属性会被当成读写的,这也是默认属性。
属性 | 内容 |
---|---|
readwrite | 此标记说明属性会被当成读写的,也是默认属性 |
readonly | 此标记说明属性只可读,也就是不能设置,可以获取 |
assign | 不会使引用计数+1,也就是直接赋值 |
retain | 会使引用计数+1 |
copy | 建立一个索引计数为1的对象,在赋值时使用传入值的一份拷贝 |
nonatomic | 非原子性访问,多线程并发访问会提高性能 |
atomic | 原子性访问 |
strong | 打开ARC时才会使用,相当于retain |
weak | 打开ARC时才会使用,相当于assign,可以把对应的指针变量置为nil |
三、 什么情况使用 weak 关键字,相比 assign 有什么不同?
首先明白什么情况使用 weak 关键字?
在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如:
- delegate代理属性,代理属性也可使用
- assign自身已经对它进行一次强引用,没有必要再强引用一次,这时也会使用weak
- 自定义IBOutlet控件属性一般也使用weak,也可以使用strong,但是建议使用weak
weak和assign的不同点
weak 策略在属性所指的对象遭到摧毁时,系统会将 weak 修饰的属性对象的指针指向 nil,在 OC 给 nil 发消息是不会有什么问题的;如果使用 assign 策略在属性所指的对象遭到摧毁时,属性对象指针还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候在给此对象发送消息,很容造成程序奔溃assigin 可以用于修饰非 OC 对象,而 weak 必须用于 OC 对象。
四、 使用 atomic 一定是线程安全的吗?
答案很明显。不是,atomic 的本意是指属性的存取方法是线程安全的,并不保证整个对象是线程安全的。
声明一个 NSMutableArray 的原子属性 stuff,此时 self.stuff 和 self.stuff =othersulf 都是线程安全的。但是,使用[self.stuff objectAtIndex:index]就不是线程安全的,需要用互斥锁来保证线程安全性。