iOS开发面试整理之小知识篇(二)

  1. atomic和nonatomic区别

    @property(nonatomic, retain) UITextField *userName;
    @property(atomic, retain) UITextField *userName;
    @property(retain) UITextField *userName;
    
    1. atomic与nonatomicd的主要区别就是系统自动生成的getter/setter方法不一样
      1. atomic系统自动生成的getter/setter方法会进行加锁操作
      2. nonatomic系统自动生成的getter/setter方法不会进行加锁操作
    2. atomic
      1. 系统生成的getter/setter方法会进行加锁操作,注意:这个锁仅仅保证了getter和setter存取方法的线程安全.
      2. 因为getter/setter方法有加锁的缘故,故在别的线程来读写这个属性之前,会先执行完当前操作.
        1. 例如: 线程1调用了某一属性的setter方法并进行到了一半,线程2调用其getter方法,那么会执行完setter操作后,在执行getter操作,线程2会获取到线程1 setter后的完整的值.
      3. 当几个线程同时调用同一属性的setter、getter方法时,会get到一个完整的值,但get到的值不可控.
        1. 例如: 线程1 调用getter 线程2 调用setter 线程3 调用setter 这3个线程并行同时开始,线程1会get到一个值,但是这个值不可控,可能是线程2,线程3 set之前的原始值,可能是线程2 set的值,也可能是线程3 set的值
      4. 当一个线程正在get或set时,又有另一个线程同时在进行成员变量的release操作,可能会直接crash
        1. 假设有一个 atomic 的属性 “name”,如果线程 A 调[self setName:@"A"],线程 B 调[self setName:@"B"],线程 C 调[self name],那么所有这些不同线程上的操作都将依次顺序执行——也就是说,如果一个线程正在执行 getter/setter,其他线程就得等待。因此,属性 name 是读/写安全的。
        2. 但是,如果有另一个线程 D 同时在调[self.name release],那可能就会crash,因为 release 不受 getter/setter 操作的限制。简单来说就是,通过get、set来访问成员变量_name是安全的,但是拿到成员变量之后,即self.name,然后通过多线程对这个成员变量进行其他的操作,比如release,那么set、get方法就有可能崩溃
        3. 也就是说,这个atomic只能说是读/写安全的,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。
    3. atomic、nonatomic的特点:
      1. atomic
        1. 是默认的
        2. 会保证CPU能在别的线程来访问这个属性之前,先执行完当前流程
          1. 在getter和setter内部加了线程同步的锁,通过查看objc4的objc-accessors.mm的源码,用的是自旋锁spinlock_t
        3. 速度不快,耗性能,因为要保证操作整体完成
        4. iOS开发大部分情况不会出现多条线程同时调用get、set方法
        5. 它并不能保证使用属性的过程是线程安全的
      2. nonatomic
        1. 不是默认的
        2. 更快
        3. 线程不安全,如有两个线程访问同一个属性,会出现无法预料的结果
Table of Contents