面试题答案
一键面试协议中属性内存管理规则
- ARC环境:
- 在协议中定义属性时,通常使用
nonatomic
修饰,因为atomic
属性会带来性能开销。对于属性的内存管理特性,常用strong
或weak
。 - 如果使用
strong
,遵循协议的类在设置属性值时,会强引用该对象,增加其引用计数。例如,协议定义@property (nonatomic, strong) id myObject;
,当遵循该协议的类设置self.myObject = someObject;
时,someObject
的引用计数会增加。 - 如果使用
weak
,遵循协议的类设置属性值时,不会增加对象的引用计数,主要用于避免循环引用。比如在视图控制器之间传递数据时,父视图控制器持有子视图控制器,子视图控制器通过协议属性反向引用父视图控制器,使用weak
可以防止循环引用。
- 在协议中定义属性时,通常使用
- MRC环境:
- 同样一般使用
nonatomic
。属性内存管理特性有retain
、assign
等。 retain
类似于ARC中的strong
,当遵循协议的类设置属性值时,会发送retain
消息,增加对象的引用计数。例如@property (nonatomic, retain) id myObject;
,self.myObject = someObject;
时,someObject
引用计数加1。assign
则不会改变对象的引用计数,一般用于基本数据类型,如NSInteger
、CGFloat
等,也可用于对象类型,但要注意对象释放后指针成为野指针的问题。
- 同样一般使用
多类遵循协议且内存管理策略不同带来的问题
- ARC环境:
- 循环引用问题:如果不同类对协议属性使用了不恰当的内存管理策略,可能导致循环引用。比如类A和类B都遵循同一协议,类A将协议属性设为
strong
引用类B实例,类B又将协议属性设为strong
引用类A实例,这样就形成了循环引用,导致对象无法释放。 - 内存泄漏:如果一个类对协议属性过度强引用,而其他类期望属性是弱引用,可能导致对象不能及时释放,造成内存泄漏。
- 循环引用问题:如果不同类对协议属性使用了不恰当的内存管理策略,可能导致循环引用。比如类A和类B都遵循同一协议,类A将协议属性设为
- MRC环境:
- 过度释放问题:如果不同类对协议属性的内存管理策略不一致,可能会出现过度释放的情况。例如,一个类认为自己拥有对象的所有权(通过
retain
),而另一个类在不知情的情况下释放了该对象,当第一个类再次访问对象时就会导致程序崩溃(野指针访问)。 - 内存泄漏:与ARC类似,如果类对协议属性过度
retain
,而没有正确释放,就会导致内存泄漏。
- 过度释放问题:如果不同类对协议属性的内存管理策略不一致,可能会出现过度释放的情况。例如,一个类认为自己拥有对象的所有权(通过
解决方法
- ARC环境:
- 明确内存管理规则:在协议文档中清晰地说明属性的推荐内存管理策略,比如明确指出对于某个协议属性应该使用
weak
以避免循环引用。 - 代码审查:通过代码审查确保所有遵循协议的类都遵循统一的内存管理策略,避免出现不恰当的
strong
引用。 - 使用中间对象或代理:如果存在潜在的循环引用,可以引入中间对象或代理对象来打破循环。例如,使用一个独立的视图模型类来管理数据,视图控制器通过协议与视图模型交互,避免视图控制器之间直接循环引用。
- 明确内存管理规则:在协议文档中清晰地说明属性的推荐内存管理策略,比如明确指出对于某个协议属性应该使用
- MRC环境:
- 制定规范:制定严格的内存管理规范,明确每个遵循协议的类在设置和释放协议属性时的责任。例如,规定某个类在设置协议属性时负责
retain
,其他类在获取属性后不负责释放等。 - 引用计数跟踪工具:使用工具来跟踪对象的引用计数,例如
NSZombieEnabled
(在调试时),可以帮助发现过度释放或未释放的问题。 - 采用设计模式:例如使用观察者模式,一个对象观察另一个对象的状态变化,而不是直接持有强引用,从而避免循环引用和过度释放问题。
- 制定规范:制定严格的内存管理规范,明确每个遵循协议的类在设置和释放协议属性时的责任。例如,规定某个类在设置协议属性时负责