MST

星途 面试题库

面试题:Objective-C内存管理框架下深度剖析内存泄漏及优化策略

在复杂的Objective-C项目中,内存管理涉及多种机制,如ARC(自动引用计数)和MRC(手动引用计数)。深入分析在ARC环境下,由于对象之间的循环引用导致内存泄漏的底层原理,并结合runtime机制提出至少两种有效的优化策略及实际代码示例。
22.1万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

循环引用导致内存泄漏的底层原理

在ARC环境下,对象的引用计数由编译器自动管理。当一个对象的引用计数降为0时,ARC会自动释放该对象占用的内存。然而,当两个或多个对象相互强引用,形成循环时,它们的引用计数永远不会降为0,从而导致内存泄漏。

例如,假设有两个类ClassAClassBClassA中有一个ClassB类型的属性,ClassB中有一个ClassA类型的属性,并且两个属性都被声明为strong(强引用):

@interface ClassA : NSObject
@property (nonatomic, strong) ClassB *classB;
@end

@interface ClassB : NSObject
@property (nonatomic, strong) ClassA *classA;
@end

当在代码中创建这两个类的实例并相互引用时:

ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];
a.classB = b;
b.classA = a;

此时,ab相互强引用,它们的引用计数都至少为1。即使超出了它们的作用域,由于循环引用的存在,它们的引用计数不会降为0,内存无法释放。

优化策略及代码示例

  1. 使用weakunowned弱引用
    • 原理weakunowned修饰的属性不会增加对象的引用计数。weak引用的对象在被释放后,指向该对象的weak指针会自动被设置为nilunowned引用的对象在被释放后,指向该对象的unowned指针不会被设置为nil,可能导致野指针,因此使用unowned时需确保对象生命周期的一致性。
    • 示例
      • 修改ClassAClassB的属性声明,将其中一个改为weak
@interface ClassA : NSObject
@property (nonatomic, strong) ClassB *classB;
@end

@interface ClassB : NSObject
@property (nonatomic, weak) ClassA *classA;
@end
 - 使用代码:
ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];
a.classB = b;
b.classA = a;
// 当a和b超出作用域时,由于b对a是weak引用,不会形成循环引用,内存会被正常释放
  1. 利用block时使用__weak__block
    • 原理:在block中使用对象时,如果block被对象持有,而block又强引用对象,可能导致循环引用。使用__weak修饰对象,可以避免block对对象的强引用;__block在ARC下一般用于在block中修改外部变量,但使用不当也可能导致循环引用,不过合理使用可以打破循环。
    • 示例
      • 假设ClassA中有一个block属性,且block中使用了self
@interface ClassA : NSObject
@property (nonatomic, copy) void (^block)(void);
@end

@implementation ClassA
- (void)setupBlock {
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf) {
            // 使用strongSelf进行操作,防止在block执行过程中self被释放
            NSLog(@"Do something with self: %@", strongSelf);
        }
    };
}
@end
  • 在这个示例中,__weak typeof(self) weakSelf = self;创建了一个弱引用weakSelfblock中使用weakSelf避免了对self的强引用,从而打破了可能的循环引用。如果需要在block中确保self在执行期间不被释放,可以像示例中那样先将weakSelf提升为强引用strongSelf