上一篇已经讲解了MRC与ARC的基本知识,本篇咱们讲解MRC-block与ARC-block的基本内容。多线程
在MRC时代,Block会隐式地对进入其做用域内的对象(或者说被Block捕获的指针指向的对象)加retain,来确保Block使用到该对象时,可以正确的访问。线程
这件事情在下面代码展现的状况中要更加额外当心。指针
MyViewController *myController = [[MyViewController alloc] init…];对象
// 隐式地调用[myController retain];形成循环引用内存
myController.completionHandler = ^(NSInteger result) {作用域
[myController dismissViewControllerAnimated:YES completion:nil];编译器
};it
[self presentViewController:myController animated:YES completion:^{内存管理
[myController release]; // 注意,这里调用[myController release];是在MRC中的一个常规写法,并不能解决上面循环引用的问题io
}];
在这段代码中,myController的completionHandler调用了myController的方法[dismissViewController...],这时completionHandler会对myController作retain操做。而咱们知道,myController对completionHandler也至少有一个retain(通常准确讲是copy),这时就出现了在内存管理中最糟糕的状况:循环引用!简单点说就是:myController retain了completionHandler,而completionHandler也retain了myController。循环引用致使了myController和completionHandler最终都不能被释放。咱们在delegate关系中,对delegate指针用weak就是为了不这种问题。
不过好在,编译器会及时地给咱们一个警告,提醒咱们可能会发生这类型的问题:
对这种状况,咱们通常用以下方法解决:给要进入Block的指针加一个__block修饰符。
这个__block在MRC时代有两个做用:
•说明变量可改
•说明指针指向的对象不作这个隐式的retain操做
一个变量若是不加__block,是不能在Block里面修改的,不过这里有一个例外:static的变量和全局变量不须要加__block就能够在Block中修改。
MyViewController * __block myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
};
//以后正常的release或者retain
在ARC引入后,没有了retain和release等操做,状况也发生了改变:在任何状况下,__block修饰符的做用只有上面的第一条:说明变量可改。即便加上了__block修饰符,一个被block捕获的强引用也依然是一个强引用。这样在ARC下,若是咱们还按照MRC下的写法,completionHandler对myController有一个强引用,而myController对completionHandler有一个强引用,这依然是循环引用,没有解决问题:(
因而咱们还须要对原代码作修改。简单的状况咱们能够这样写:
__block MyViewController * myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil; // 注意这里,保证了block结束myController强引用的解除
};
在completionHandler以后将myController指针置nil,保证了completionHandler对myController强引用的解除,不过也同时解除了myController对myController对象的强引用。这种方法过于简单粗暴了,在大多数状况下,咱们有更好的方法。
这个更好的方法就是使用weak。(或者为了考虑iOS4的兼容性用unsafe_unretained,具体用法和weak相同,考虑到如今iOS4设备可能已经绝迹了,这里就不讲这个方法了)(关于这个方法的本质咱们后面会谈到)
为了保证completionHandler这个Block对myController没有强引用,咱们能够定义一个临时的弱引用weakMyViewController来指向原myController的对象,并把这个弱引用传入到Block内,这样就保证了Block对myController持有的是一个弱引用,而不是一个强引用。如此,咱们继续修改代码:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyViewController = myController;
myController.completionHandler = ^(NSInteger result) {
[weakMyViewController dismissViewControllerAnimated:YES completion:nil];
};
这样循环引用的问题就解决了,可是却不幸地引入了一个新的问题:因为传入completionHandler的是一个弱引用,那么当myController指向的对象在completionHandler被调用前释放,那么completionHandler就不能正常的运做了。在通常的单线程环境中,这种问题出现的可能性不大,可是到了多线程环境,就很很差说了,因此咱们须要继续完善这个方法。
为了保证在Block内可以访问到正确的myController,咱们在block内新定义一个强引用strongMyController来指向weakMyController指向的对象,这样多了一个强引用,就能保证这个myController对象不会在completionHandler被调用前释放掉了。因而,咱们对代码再次作出修改:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if (strongMyController) {
// ...
[strongMyController dismissViewControllerAnimated:YES completion:nil];
// ...
}
else {
// Probably nothing...
}
};
到此,一个完善的解决方案就完成了:)
以上就是block在ARC与MRC的用处,但愿对你们有所帮助!!!