MRC-block与ARC-block

上一篇已经讲解了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的用处,但愿对你们有所帮助!!!

相关文章
相关标签/搜索