iOS

ARC转换总结+避免循环引用

参考

上面的文章写得已经非常全面了,不过还是有些东西需要补充一下。

循环引用

循环引用和ARC没有直接关系,但是在转换的过程中遇到了相关问题,所以就着重说明一下。

循环引用的原因

任何一种语言,都必须有它的内存管理方式。比如C语言中,我们用malloc申请一块内存,放入数据。当这块内存不在需要时,就调用free将其释放掉。这是一种比较原始的方式,当同一块内存在多个地方被用到时,到底应该由谁来释放呢?你只能小心翼翼的处理这种问题,除此之外没有别的办法。

参考

上面的文章写得已经非常全面了,不过还是有些东西需要补充一下。

循环引用

循环引用和ARC没有直接关系,但是在转换的过程中遇到了相关问题,所以就着重说明一下。

循环引用的原因

任何一种语言,都必须有它的内存管理方式。比如C语言中,我们用malloc申请一块内存,放入数据。当这块内存不在需要时,就调用free将其释放掉。这是一种比较原始的方式,当同一块内存在多个地方被用到时,到底应该由谁来释放呢?你只能小心翼翼的处理这种问题,除此之外没有别的办法。

Objective-C相较于C的一大优势就是内存管理,提出了引用计数的概念。引用计数是针对对象的,即NSObject,不是NSObject就没必要谈引用计数。

在Objective-C中,凡是继承自NSObject的类都提供了两个方法,retain和release。当我们调用一个对象的retain时,这个对象的内存计数加1;反之,当我们调用release时, 对象的内存计数减1,只有当对象内存计数为0时,这个对象才真正会被释放,此时,对象的delloc方法会自动被调用,来做些内存回收前的工作。

那么问题就来了。比如有A和B两个对象,A持有B,B同时也持有A,按照上面的规则,A只有B释放之后才有可能释放,同样B只有A释放后才可能释放,当双方都在等待对方释放的时候, 循环引用就形成了。两个对象都永远不会被释放,这样就造成内存泄露。

1
2
3
4
5
6
7
8
9
10
11
12
//ARC code
@interface A : NSObject

@property (nonatomic,strong) B* b;

@end

@interface B : NSObject

@property (nonatomic,strong) A* a;

@end

解决办法也很简单,只要不让A和B同时保持对方的强引用即可。 注意,B对A的引用是weak。知道为什么我们要把Delegate对象设成weak了吧?

1
2
3
4
5
6
7
8
9
10
11
12
//ARC code
@interface A : NSObject

@property (nonatomic,strong) B* b;

@end

@interface B : NSObject

@property (nonatomic,weak) A* a;

@end

当Block遇到ARC

Block的概念这里就不详细解释了。我想说的是,Block就是一个对象,它能访问上下文变量,这就要保证上下文所属的对象在Block运行时必须不被释放,所以Block默认会对当前上下文进行强引用。这时如果当前对象也对Block有强引用,那么就会造成循环引用。比如下面的代码,self强引用tableView,tableView强引用block;block又强引用self,所以问题就产生了。

1
2
3
4
5
6
[self.tableView addPullToRefreshWithActionHandler:^{
self.isRefresh = YES;
self.hideHud = YES;
self.currentPage = 0;
[self queryCarFault];
}];

如何破局,ARC之后有一个__weak的关键字。这样Block就不会强引用BBWarningRecordTableViewController的对象了。

1
2
3
4
5
6
7
__weak BBWarningRecordTableViewController* weakSelf = self;
[self.tableView addPullToRefreshWithActionHandler:^{
weakSelf.isRefresh = YES;
weakSelf.hideHud = YES;
weakSelf.currentPage = 0;
[weakSelf queryCarFault];
}];

从上面的代码可以看到,我们只是加了一行代码

1
__weak BBWarningRecordTableViewController* weakSelf = self;

但是有一次我发现类似的语句不止一种写法

1
2
3
4
5
6
7
8
9
10
// 不知道这行代码的使用场景的同学你该去自习看看ARC的注意事项和Block的使用了
// AFNetworking的写法
__weak __typeof(&*self)weakSelf = self;

// 我之前一直这么写的
__weak __typeof(self) weakSelf = self;
// 或者这么写
__weak XxxViewController *weakSelf = self;
// 或者这么写
__weak id weakSelf = self;

这四种写法居然都是对的,第三种在我看来是最不好的,因为每个地方都要单独写。而其余几种写法都可以封装成一个宏,当项目中很多地方都要写类似代码时,用宏肯定是最佳的实践。