我在__weak self
内阅读了很多关于使用dispatch_async
的帖子,现在我对此感到困惑。
如果我有:
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
。
答案 0 :(得分:132)
假设, self 是指向UIViewController
的对象指针。
需要考虑的事项:
UIViewController
是“UIKit”对象。 UIKit对象不应该在非主线程上发送方法,即 - 那些方法只能在主线程上执行!
已排入队列的块 - 无论是同步还是异步 - 最终将执行 - 无论如何!好吧,除非程序在此之前终止。
当复制块时(例如,异步调度时),已释放将保留 em>当块被销毁时(完成后)。
捕获的可保留弱指针不会被保留而不会被释放。
在您的方案中,您在主队列中调度的块中捕获 self ,您无需担心会发生不良事件。
由于 self 将在异步调度的块中捕获, self 将隐式时,>保留,释放。
这意味着, self 的生命周期将扩展直到块完成后。请注意,您的第二个块将在主线程上调度,并确保 self 在执行该块时仍处于活动状态。
上面的“延长寿命”可能是您程序的理想功能。
如果显式不希望延长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.
}
});
另请参阅:Transitioning to ARC Release Notes
UIKit
对象不应该在非主线程上发送方法!由于UIKit
个对象只能在主线程上执行方法,因此可能会发生另一个细微的错误。
如果一个块捕获异步调度的UIKit
对象,并在非主线程上执行,则可能违反此规则。然后可能会发生该块保存 last 对该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
});
});
在您的方案中, last strong 引用只能在主线程上执行的块中。因此,您可以避免这种微妙的错误。 ;)
在您的设置中,您永远不会有保留周期。如果可保留对象A强引用另一个可保留对象B,并且对象B强引用A,则会发生保留周期。请注意,“块”也是可保留对象。
一个带有循环引用的人为例子:
typedef void(^my_completion_block_t)(NSArray* result);
@interface UsersViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray* users;
@end
这里,我们有一个属性 completion ,其值类型为Block。也就是说,我们得到一个名为_completion
的ivar,其类型为Block。
客户端可以设置一个完成处理程序,当某个操作完成时应该调用该处理程序。假设,该操作从远程服务器获取用户列表。计划是在操作完成后设置属性 users :
粗心的方法会不小心引入循环引用:
“UsersViewController.m”中的某个地方
self.completion = ^(NSArray* users){
self.users = users;
}
[self fetchUsers]; // start asynchronous task
在这里, self 拥有对ivar _completion
的强引用,这是一个块。块本身捕获 self ,这会导致在调度块被复制时保留 self 。这是一个经典的参考周期。
为了避免这种循环引用,我们有几个选择:
使用 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];
使用 self 的__block
限定指针,并在完成后最终在块中设置nil
:
UsersViewController* __block blockSelf = self;
self.completion = ^(NSArray* users) {
blockSelf.users = users;
blockSelf = nil;
}
[usersViewController fetchUsers];
答案 1 :(得分:26)
这种所谓的强弱舞蹈的一个例子:
func doSomeThingAsynchronously() {
DispatchQueue.global().async {
// Do task in default queue
DispatchQueue.main.async { [weak self] in
// Do task in main queue
guard let self = self else { return }
self.updateView()
}
}
}
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()
}
}
}
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()
})
}
}
热门的开源项目 Alamofire
使用此方法。
使用 [弱自我] 和后卫 strongSelf = self else {return}成语延长对象生命周期。
有关详细信息,请查看swift-style-guide