在dispatch_async函数中使用weak self

我读了很多关于在dispatch_async里面使用__weak self的post,现在我有点困惑了。

如果我有 :

 self.myQueue = dispatch_queue_create("com.biview.core_data", NULL); dispatch_async(self.myQueue, ^(void){ if (!self.var1) { self.var1 = ...; } dispatch_async(dispatch_get_main_queue(), ^(void) { if ([self.var2 superview]) { [self.var2 removeFromSuperview]; } [self.Label setText:text]; }); }); 

我是否需要使用__weak self 。 因为我在某些情况下读到dispatch_async不需要__weak self

在这里看最后的评论

假设selfUIViewController的对象指针。

需要考虑的事项:

  • UIViewController是一个“UIKit”对象。 UIKit对象不能在非主线程上发送方法,那就是 – 这些方法只能在主线程上执行!

  • 一个已经排入队列的模块 – 无论是同步模式还是asynchronous模式 – 最终都会被执行 – 无论如何! 那么,除非在此之前程序终止。

  • 捕获的可保留的 指针将在块被复制(例如,asynchronous分派)时保留 ,并在块被销毁(完成后)再次释放

  • 捕获的可保留的指针不会被保留,也不会被释放。

在您的场景中,在主队列中分派的块中捕获自己的位置,您不必担心会发生不好的事情。

所以为什么? 而实际上会发生什么?

由于self会被asynchronous分派的block 捕获 ,所以self会被隐式保留 ,并在block完成后再次释放

这意味着, 自我的生命时间将延长到块完成后。 注意你的第二个块在主线程中被调度,并且保证当这个块被执行时自己仍然活着。

上面这个“延长的生命”,可能是你的程序的一个期望的function。

如果你明确地不想延长UIViewController对象的生命周期,而是希望块 – 当它最终执行时 – 检查这个UIViewController对象是否仍然存在,你可以使用self的__weak指针。 请注意,无论UIViewController是否仍处于活动状态或已被释放,块最终都会被执行。

如果在块被执行之前 UIViewController已经被释放你可能希望块做“没有”

 MyController* __weak weakSelf = self; dispatch_async(queue, ^{ MyController* strongSelf = weakSelf; if (strongSelf) { ... } else { // self has been deallocated in the meantime. } }); 

另请参阅: 转换到ARC版本说明

记住: UIKit对象不应该在非主线程上发送方法!

另外一个细微的错误可能会发生, UIKit对象只能在主线程上执行方法。

如果一个块捕获asynchronous调度的UIKit对象,并且在非主线程上执行,则可能违反此规定。 然后可能会发生这个块持有对该UIKit对象的最后一个强引用。 现在,当块最终被执行时,块将被销毁并且UIKit对象将被释放。 由于这是对UIKit对象的最后一个引用,因此它将被释放。 但是,这发生在块已经被执行的线程上 – 这不是主线程! 现在,坏的事情可以(通常会)发生,因为dealloc方法仍然是一个发送到UIKit对象的方法。

你可以通过派发一个捕获一个强指针指向该UIKit对象的块来避免这个错误,并发送一个虚拟的方法:

 UIViewController* strongUIKitPointer = ... dispatch_async(non_main_queue, ^{ ... // do something dispatch(dispatch_get_main_queue(), ^{ [strongUIKitPointer self]; // note: self is a method, too - doing nothing }); }); 

在你的场景中, 最后的强引用只能在主线程中执行的块中。 所以,你从这个微妙的错误是安全的。 ;)

编辑:

在你的设置,你永远不会有一个保留周期。 如果可保留对象A强烈引用另一个可保留对象B,并且对象B强烈引用A,则会发生保留循环。请注意,“Block”也是可保留对象。

循环引用的一个人为的例子:

 typedef void(^my_completion_block_t)(NSArray* result); @interface UsersViewController : UIViewController @property (nonatomic, copy) my_completion_block_t completion; @property (nonatomic) NSArray* users; @end 

在这里,我们有一个属性完成,其值types是一个块。 也就是说,我们得到一个名为_completion的ivar,其types是Block。

客户端可能会设置一个完成处理程序,当某个操作完成时应该调用它。 假设操作从远程服务器获取用户列表。 计划在操作完成后设置房产用户

粗心的做法会不小心引入循环引用:

在“UsersViewController.m”中的某处

 self.completion = ^(NSArray* users){ self.users = users; } [self fetchUsers]; // start asynchronous task 

在这里,我自己对伊娃_completion有很强的参考,这是一个块。 而块本身捕捉自我 ,当块被复制时,这会导致自我保持自我 。 这是一个经典的参考周期。

为了避免循环引用,我们有几个select:

  1. 使用self__weak限定指针

     UsersViewController* __weak weakSelf = self; self.completion = ^(NSArray* users) { UsersViewController* strongSelf = weakSelf; if (strongSelf) { strongSelf.users = users; } else { // the view controller does not exist anymore } } [usersViewController fetchUsers]; 
  2. 使用自己的__block限定指针,并最终在它完成时在块中将其设置nil

     UsersViewController* __block blockSelf = self; self.completion = ^(NSArray* users) { blockSelf.users = users; blockSelf = nil; } [usersViewController fetchUsers]; 

另请参阅: 转换到ARC版本说明

Swift更新:

这种所谓的强弱舞蹈就是一个例子:

 func doSomeThingAsynchronously() { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> () in // Do task in default queue dispatch_async(dispatch_get_main_queue(), { [weak self] () -> () in guard let strongSelf = self else { return } // Do task in main queue strongSelf.updateView() }) } } 

stream行的开源项目Alamofire使用这种方法。

使用[weak self]扩展对象的生命周期,并 guardSelf = self else {return}成语。

欲了解更多信息,请访问swift-style-guide

Swift 3更新:

 func doSomeThingAsynchronously() { DispatchQueue.global().async { // Do task in default queue DispatchQueue.main.async { [weak self] in // Do task in main queue guard let strongSelf = self else { return } strongSelf.updateView() } } }