safe_unretained修饰
//代码
void test5()
{
// __unsafe_unretained Person *per = [[Person alloc] init];
__block Person *per = [[Person alloc] init];
per.age = 10;
per.block = [^{
NSLog(@"-------%d", per.age);
} copy];
[per release];
per = nil;
}
//打印
2019-01-18 16:42:49.970441+0800 MJ_TEST[2257:177470] -[Person dealloc]
2019-01-18 16:42:49.971587+0800 MJ_TEST[2257:177470] ----
Program ended with exit code: 0
分析:
<1>根据习惯,MRC环境下,我们通常会将block对象(等号右边)从栈区copy到堆区,以达到手动控制其内存销毁的目的;
<2>原理:调用alloc创建对象时,系统会自动将该实例对象引用计数置为1,而该对象又会随着block的copy而一起被copy到堆区,此时该对象的retainCount会加1(变成2),当对该对象发送release消息时,其retainCount自动减1(由2变成1),所以当程序结束时,Person实例对象retainCount为1(>0),其内存并不会被系统回收从而导致内存泄露;
那么,__unsafe_unretained修饰后,无论后面有多少次retain或者copy操作,Person实例对象的retainCount始终为1,所以程序结束前release时,其retainCount值变为0,此时内存被回收,而不会导致内存泄露的问题;
方案二:__block修饰
分析:为什么block可以?
<1>前述文章我们分析到,MRC环境下,__block修饰对象类型的auto局部变量,系统生成的__block对象并不会根据其内存成员变量Person指针变量(其实就是test5()方法中的per指针)是强指针类型而对Person实例对象([[Person alloc] init])进行retain操作(强引用);
<2>所以此时,__block的作用相当于__unsafe_unretained的作用,原理一样;
补充一个问题:在ARC环境下,弱指针不能通过"->"形式来访问对象的成员变量
原因:就是weakSelf很可能为为空(即有可能提前被释放了),所以必须使用强指针来访问
//代码
Person.m
- (void) test6
{
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"-------%d", strongSelf->_age);
};
}
main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
// test1();
// test2();
// test3();
// test4();
// test5();
Person *per = [[Person alloc] init];
[per test6];
}
NSLog(@"----");
return 0;
}
//打印
2019-01-18 17:43:41.010848+0800 MJ_TEST[2557:208583] -[Person dealloc]
2019-01-18 17:43:41.011346+0800 MJ_TEST[2557:208583] ----
Program ended with exit code: 0
//clang
Person.m
struct __Person__test6_block_impl_0 {
struct __block_impl impl;
struct __Person__test6_block_desc_0* Desc;
Person *const __weak weakSelf;
__Person__test6_block_impl_0(void *fp, struct __Person__test6_block_desc_0 *desc, Person *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
分析:block对象的内部成员变量weakSelf依然是weak类型,并不受block代码块内部的__strong转化,该转化只是为了骗取编译器通过编译而已;
三、结论
1)原因:block对象与OC对象相互持有(强引用)——OC对象有block属性,block代码块中用到了该实例对象;
2)危害:程序结束时,相互强应用(对象的引用计数>0)导致实例对象所占内存不能及时被系统回收——即内存泄露;
3)解决:
<1>ARC:__weak修饰(常用)、__unsafe_unretained(会引起野指针调用,不推荐)、__block(过于繁琐,不推荐);
<2>MRC:__unsafe_unretained和__block;
GitHub