面试题答案
一键面试- 循环引用(Retain Cycles)
- 原因:两个或多个对象之间相互持有强引用,形成一个循环,导致对象无法被释放。例如,一个视图控制器(ViewController)持有一个视图(View),而这个视图又持有视图控制器作为其代理(delegate),如果两者都使用强引用,就会形成循环引用。
- 避免方法:将其中一个引用改为弱引用(weak)或无主引用(unowned)。在上述例子中,视图的代理属性通常应设置为弱引用,这样当视图控制器被释放时,视图对其的引用不会阻止视图控制器的释放。示例代码如下:
// View.h
@property (nonatomic, weak) id<ViewDelegate> delegate;
- NSTimer的强引用
- 原因:当使用
NSTimer
并将其添加到运行循环(RunLoop)中时,如果NSTimer
持有对某个对象的强引用,而这个对象又持有NSTimer
,就会造成循环引用。比如,在视图控制器中创建NSTimer
并设置其目标为视图控制器自身,NSTimer
会强引用视图控制器,而视图控制器又持有NSTimer
,导致视图控制器在dealloc
时NSTimer
无法释放,进而视图控制器也无法释放。 - 避免方法:
- 可以使用
weak
修饰符来打破循环引用。在视图控制器中创建一个弱引用自身的变量,然后将其作为NSTimer
的目标。示例代码如下:
- 可以使用
- 原因:当使用
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(updateUI) userInfo:nil repeats:YES];
- 当视图控制器即将被销毁时,手动使`NSTimer`失效。在视图控制器的`dealloc`方法中调用`[self.timer invalidate]; self.timer = nil;`。
3. Block的循环引用
- 原因:在Block内部使用对象时,如果Block被对象持有,而Block又强引用对象,就会形成循环引用。例如,在视图控制器中定义一个Block,在Block内部访问视图控制器的属性,而这个Block又被视图控制器持有,就会造成循环引用。
- 避免方法:
- 使用__weak
修饰符创建一个弱引用对象,在Block内部使用弱引用对象。示例代码如下:
__weak typeof(self) weakSelf = self;
self.completionBlock = ^{
NSLog(@"%@", weakSelf.title);
};
- 在Block内部先将弱引用对象赋给一个强引用对象,以防止在Block执行期间对象被释放。示例代码如下:
__weak typeof(self) weakSelf = self;
self.completionBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
NSLog(@"%@", strongSelf.title);
}
};
- Core Foundation对象的手动内存管理
- 原因:虽然ARC负责Objective - C对象的内存管理,但对于Core Foundation对象(如
CFStringRef
、CFArrayRef
等),需要手动调用相应的内存管理函数(如CFRelease
)。如果在ARC环境下使用Core Foundation对象,却忘记调用CFRelease
,就会导致内存泄漏。 - 避免方法:使用桥接转换(bridging casts)将Core Foundation对象转换为Objective - C对象,这样ARC就能管理其内存。例如,将
CFStringRef
转换为NSString *
:
- 原因:虽然ARC负责Objective - C对象的内存管理,但对于Core Foundation对象(如
CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, "Hello", kCFStringEncodingUTF8);
NSString *nsString = (__bridge_transfer NSString *)cfString;
// 此时nsString由ARC管理,无需手动调用CFRelease
- 集合类中对象的内存管理
- 原因:当向集合类(如
NSArray
、NSDictionary
、NSSet
)中添加对象时,如果添加的对象在集合类外部被意外释放,而集合类仍持有该对象的强引用,可能会导致悬空指针(dangling pointer)问题,并且当集合类本身被释放时,该对象也不会被正确释放,造成内存泄漏。 - 避免方法:确保添加到集合类中的对象在整个生命周期内都能被正确管理。如果需要从集合类中移除对象,在移除后确保对象在其他地方有合理的内存管理。例如,在从数组中移除对象后,检查该对象是否还被其他地方使用,若不再使用,可以设置为
nil
以确保其被释放。示例代码如下:
- 原因:当向集合类(如
NSMutableArray *array = [NSMutableArray arrayWithObject:self.someObject];
[self.someObject doSomething];
[self.array removeObject:self.someObject];
self.someObject = nil;