面试题答案
一键面试Objective-C 的自动引用计数(ARC)原理
ARC 是一种自动内存管理机制。编译器会在编译期自动在合适的位置插入 retain
、release
和 autorelease
等内存管理方法。当对象的引用计数变为 0 时,ARC 会自动调用对象的 dealloc
方法释放其所占用的内存。
ARC 环境下循环引用的产生
- 对象之间相互强引用:当两个或多个对象之间相互持有对方的强引用时,就会形成循环引用。因为每个对象的引用计数都不会降为 0,导致这些对象所占用的内存无法被释放。
常见的循环引用场景及解决方法
- 代理(Delegate)场景
- 产生原因:一般情况下,视图控制器(ViewController)持有视图(View)的强引用,而视图会将视图控制器设置为代理(delegate),如果代理属性使用
strong
修饰,就会形成循环引用。因为视图控制器持有视图,视图又强引用视图控制器。 - 解决方法:将代理属性声明为
weak
或者unsafe_unretained
。weak
修饰的属性不会增加对象的引用计数,并且当对象被释放时,指向该对象的weak
指针会自动被设置为nil
,避免野指针问题。unsafe_unretained
同样不会增加引用计数,但当对象被释放时,指针不会被置为nil
,可能会导致野指针,所以一般优先使用weak
。例如:
- 产生原因:一般情况下,视图控制器(ViewController)持有视图(View)的强引用,而视图会将视图控制器设置为代理(delegate),如果代理属性使用
@property (nonatomic, weak) id<MyViewDelegate> delegate;
- Block 场景
- 产生原因:如果在 block 内部使用了外部对象(如视图控制器中的属性),并且 block 被该对象持有(比如作为属性),默认情况下,block 会对其捕获的外部对象进行强引用。如果该外部对象又持有这个 block,就会形成循环引用。例如:
self.block = ^{
NSLog(@"%@", self.someProperty);
};
- **解决方法**:
- **使用 `__weak` 修饰符**:在 block 外部使用 `__weak` 修饰外部对象,然后在 block 内部使用该弱引用对象。这样 block 不会强引用外部对象,避免循环引用。例如:
__weak typeof(self) weakSelf = self;
self.block = ^{
NSLog(@"%@", weakSelf.someProperty);
};
- **使用 `__block` 修饰符(ARC 环境下不常用,可能仍有循环引用风险)**:`__block` 修饰的变量在 block 内部是可变的,并且不会像默认情况那样强引用对象,但在 ARC 下,`__block` 修饰的对象可能仍会被 block 强引用,所以需要谨慎使用。例如:
__block MyViewController *blockSelf = self;
self.block = ^{
NSLog(@"%@", blockSelf.someProperty);
blockSelf = nil;
};
手动管理内存(与 Core Foundation 框架交互)注意事项
- 对象所有权桥接:在 Core Foundation(CF)和 Objective-C 之间交互时,要注意对象所有权的转换。例如,使用
CFBridgingRetain
函数将 Objective-C 对象转换为 Core Foundation 对象并获取其所有权,使用CFBridgingRelease
函数将 Core Foundation 对象转换为 Objective-C 对象并释放 Core Foundation 对象的所有权。例如:
// 将 NSString 转换为 CFString 并获取所有权
CFStringRef cfString = CFBridgingRetain(@"Hello");
// 使用完后释放所有权并转换回 NSString
NSString *str = CFBridgingRelease(cfString);
- 引用计数匹配:对于 Core Foundation 对象,要确保
CFRetain
和CFRelease
的调用次数匹配。如果在获取对象所有权后没有正确释放,会导致内存泄漏。例如:
CFStringRef cfString = CFStringCreateWithCString(NULL, "World", kCFStringEncodingUTF8);
CFRetain(cfString);
// 操作 cfString
CFRelease(cfString);
- 内存管理规则一致性:在混合使用 Core Foundation 和 Objective-C 内存管理时,要保持内存管理规则的一致性。避免在同一个对象上同时使用 ARC 和手动引用计数管理,以免造成混乱和内存问题。