NSArray 或 NSMutableArray 使用 ARC 可能存在内存泄漏

ben*_*nck 4 memory-leaks objective-c nsmutablearray nsarray ios

BKObject 是一个自定义对象,我想将多个 BKObject 放入一个数组中。

BK视图控制器:

#import <UIKit/UIKit.h>
#import "BKObject.h"

@interface BKViewController : UIViewController

@property (strong, nonatomic) NSArray *data;
@property (weak, nonatomic) BKObject *tmpObject;

@end
Run Code Online (Sandbox Code Playgroud)

BKViewController.m:

#import "BKViewController.h"

@implementation BKViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSMutableArray *arr = [[NSMutableArray alloc] init];
    for(NSInteger i = 0; i < 100000; i++){
        [arr addObject:[[BKObject alloc] initWithName:@""]];
    }

    self.data = [NSArray arrayWithArray:arr];

    __weak BKObject *weakMutableObject = arr[0];
    [arr removeAllObjects];
    NSLog(@"%@", weakMutableObject); // print out the object, why?

    __weak BKObject *weakObject = self.data[0];
    self.data = nil;
    NSLog(@"%@", weakObject); // print out the object again, but why?


    self.tmpObject = [[BKObject alloc] initWithName:@""];
    NSLog(@"%@", self.tmpObject); // print null, very clear

}


@end
Run Code Online (Sandbox Code Playgroud)

我很好奇为什么前 2 条 NSLog 消息显示一个对象而不是 null(如最后一条 NSLog 中所示)。我正在使用最新的 Xcode 5.0.1 和 iOS 7 SDK。

Rob*_*ier 5

NSMutableArray *arr = [[NSMutableArray alloc] init];
for(NSInteger i = 0; i < 100000; i++){
    [arr addObject:[[BKObject alloc] initWithName:@""]];
}
Run Code Online (Sandbox Code Playgroud)

好的,此时,我们有一堆由数组保留的对象。

self.data = [NSArray arrayWithArray:arr];
Run Code Online (Sandbox Code Playgroud)

现在,我们有一堆由两个不同数组保留的对象。

__weak BKObject *weakMutableObject = arr[0];
[arr removeAllObjects];
NSLog(@"%@", weakMutableObject); // print out the object, why?
Run Code Online (Sandbox Code Playgroud)

因为 by 指向的对象arr[0]也被 保留self.data

__weak BKObject *weakObject = self.data[0];
self.data = nil;
NSLog(@"%@", weakObject); // print out the object again, but why?
Run Code Online (Sandbox Code Playgroud)

这个有点意思。“问题”是arrayWithArray:添加额外的保留/自动释放,这是免费的,因为它们是平衡的。您可以通过在不同点耗尽自动释放池来非常简单地证明这一点。

这显示了一个活动对象:

  __weak NSObject *weakObject;
  self.data = [NSArray arrayWithArray:arr]; // Note outside nested autorelease pool
  @autoreleasepool {
    ...    
    weakObject= self.data[0];
    self.data = nil;
  }
  NSLog(@"%@", weakObject); // print out the object
Run Code Online (Sandbox Code Playgroud)

这显示为零:

  __weak NSObject *weakObject;
  @autoreleasepool {
    self.data = [NSArray arrayWithArray:arr]; // Note inside nested autorelease pool
    ...   
    weakObject= self.data[0];
    self.data = nil;
  }
  NSLog(@"%@", weakObject); // print nil
Run Code Online (Sandbox Code Playgroud)

这里的教训是,您不应该假设对象将在自动释放块内的任何给定点释放。这并不是 ARC 的承诺。它仅承诺对象有效的最短时间系统的其他部分可以随意附加平衡的保留/自动释放对,这将延迟释放,直到池耗尽。