iOS开发经验多年,面试的时候也被问得最频繁的问题,常常只能回答是干嘛用的,没有深入了解,测试、验证。导致基于修饰词扩展出的问题很多都是不清不楚的。
如果有写的不对的地方,或者可以扩展的点,欢迎留言!!
1.OC属性的常用的属性修饰词有哪些?
答:nonatomic、atomic、strong、weak、assign、copy、readonly、retain,nullable、readwrite、nonnull、class、null_resettable、null_unspecified。
1.1 分别有什么作用?
- nonatomic:非原子操作、没有锁、不能保证对对象的互斥访问,用于支持多线程操作。如果用此修饰,当其他线程想访问就可以访问,并返回多线程操作下的结果。
- atomic:原子操作、使用自旋锁,保证setter/getter方法的操作完整性,不保证属性的线程安全。(原子操作:是指不会被线程调度机制打断的操作,这个操作是一个整体、CPU一旦开始执行,就会一直执行结束,在这期间CPU不会执行其他线程的操作),
- 原子操作指的是setter、getter操作,线程安全情况举例(举例:A线程先执行写操作,接着执行读操作,在写的同时B线程执行写的操作,等A执行完写操作,如果这时候B线程先进行写操作,A线程接着执行读操作,会出现A线程读取到的值,不是A线程写操作时候的值,造成了值异常,再举例:A、B线程都是对整型变量循环执行读操作、然后写操作(值+1),A、B线程先后触发、在循环内添加线程睡眠,如1秒,增加异常出现概率,结果打印后得知,出现写入同一个值的情况,而不是A、B线程循环次数的和的累加值。)
nonatomic与atomic对比总结: nonatomic,执行效率高、线程不安全;atomic,保证读写操作完整性,效率低。
-
strong:替代MRC(手动内存管理)模式下的retain,在ARC(自动内存管理)模式下使用。
-
weak:不增加引用对象的引用计数,不是引用对象的拥有者,当引用对象的引用计数降为0时,对象指针指向的内存区域会被释放,即使你还能访问到引用对象。
-
assign:修饰基本数据类型,调用setter方法时,是赋值操作;调用getter方法时,返回的具体的值。
-
copy:在执行赋值操作时,分配新的内存地址,拷贝赋值对象指针所指向的内存的值,修饰对象的指针指向新创建的内存。(当修饰的对象是可变的,想获取在用到的那个时刻的值,不受其他此对象的拥有者的影响。此场景下适用)。
-
readonly:支持只读操作,不支持写操作。
-
retain:修饰对象类型,@synthesizer生成的setter方法内会持有传入的对象,会保留持有的传入对象,传入对象的引用计数+1,之前所指向的对象的引用计数-1。
-
nullable:修饰的对象可为空。
-
readwrite:支持读写操作。
-
nonnull:修饰的对象不可为空。
-
class:用于修饰类属性的实例对象,常配合readonly一起使用,全局共享此(单例模式)实例(开源库示例:SDImageCache)
-
null_resettable:get方法:不能返回为空,set方法可以为空
-
null_unspecified:不确定是否为空
1.2 都是在什么场景下使用?
-
1.2.1 :对比 nonatomic 和 atomic ,在考虑效率与保证读写操作的完整性的特点后,偏向于使用nonatomic.
-
扩展1:既然使用nonatomic比较多,那什么场景下使用atomic? 答:在保证证读写操作的完整性要求高于效率的时候。
-
扩展2:既然使用atomic无法保证线程安全,那么如何处理可以保证线程安全? 答:atomic是在读写方法内加锁,要保证线程操作安全的话,在函数调用前后加解锁(在函数内部加锁的作用跟atomic一样,无法保证,另外,如果在函数内还执行其他线程操作,也无法保证线程安全,其他线程操作例如:线程睡眠一会,测试验证过),加锁方式:例如 @synchronized (self) {}
-
扩展3:iOS有哪些加锁的方式? 答:遵守NSLocking加锁协议的类有,NSLock、NSConditionLock、NSRecursiveLock、NSCondition; 其他的加锁方式有:OSSpinLock、os_unfair_lock、pthread_mutex、dispatch_semaphore、dispatch_queue(DISPATCH_QUEUE_SERIAL)、@synchronized、dispatch_barrier_async、dispatch_group。
此处锁是如何使用以及不同锁的底层原理及差异后续单独写一篇
-
-
1.2.2 :weak、assign、strong、retain,copy的用法?
-
- weak、strong、retain,copy常用来修饰对象,strong、retain、copy会保留持有这个对象;
-
-
而weak不持有对象,持有的本质是对象的引用计数值会+1; copy修饰的对象会拷贝赋值对象指针所指向的内存地址,指针指向创建新的内存区域,这是常规的理解。通过测试样例验证发现,执行完copy操作后,如果copy的(不可变对象)对象的值不发生改变,内部并没有拷贝原来的对象的内存区域,而是指向同一块内存区域,引用计数+1;当执行完copy操作后,所拷贝的对象的值发生了改变,才会执行常规的操作。这样做到方式应该是底层为节省资源而做的优化吧,着重注意是可变对象,还是不可变对象,处理逻辑有差异。再扩展,使用mutableCopy,不管操作后所拷贝的对象值有没有改变,都会创建一份新的内存地址用来指向。继续扩展,容器类(如NSArray,NSDictionary)、 非容器类(NSString),执行copy与mutableCopy,有啥不同,以及什么是浅拷贝,什么是深拷贝?
assign常用来修饰基本数据类型。Float,BooL,NSInteger等,分配的内存地址在栈区。
-
-
- 扩展1:assign可以修饰对象吗? 答:可以,修饰字符串,数组,字典,可以用字面量赋值初始化,如果使用alloc] init] 构造方法的话,编译器会报警告(Assigning retained object to unsafe property; object will be released after assignment),应当持有保留的对象使用不安全的修饰词时;分配后将释放对象。
-
- 扩展2:assign与weak的相同点与不同点?答:相同点:俩者都是弱引用,不同点:assign修饰基本数据类型,或者MRC模式下修饰delegate,weak修饰对象类型,ARC模式下Delegate用weak修饰。weak修饰的对象被释放的时候会置为nil, OC中给nil对象发消息不会报错,给已经释放的非空对象发消息会crash。
-
- 扩展3:strong、retain、copy的相同点与不同点?答:相同点:三者都是强引用。不同点:修饰block属性的时候,用copy属性,将存放在栈内存空间的block拷贝至堆区,要不然随着函数结束返回出栈,生命周期就结束了;如果用strong修饰block属性,相当于copy,而用retain修饰的话,相当于assign;
-