在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
。
在这里看最后的评论
假设self是UIViewController
的对象指针。
需要考虑的事项:
-
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:
-
使用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];
-
使用自己的
__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() } } }