假设 self 是一个指向 UIViewController
的对象指针。
需要考虑的事情:
UIViewController
是一个“UIKit”对象。UIKit 对象不应该在非主线程上发送方法,也就是说 - 这些方法必须只能在主线程上执行!
已经被加入队列(无论是同步还是异步)的块最终都将被执行,除非程序在此之前终止。
当拷贝块(例如,异步调度时)时,捕获的可保留的 strong 指针将被保留,并且在块销毁后(完成后)再次释放。
捕获的可保留的 weak 指针将不会被保留或释放。
在您的场景中,您在分派到 主队列 上的块中捕获了 self,因此不需要担心会发生什么不好的事情。
那么,为什么?实际会发生什么?
由于 self 将被捕获在分派 异步 的块中,因此 self 将被隐式保留,并在块完成后再次释放。
这意味着,self 的生命周期将延长至块完成之后。请注意,您的 第二个 块分派到 主线程 上,并且可以保证在执行该块时 self 仍然存活。
上面的“扩展寿命”可能是您程序中需要的一个特性。
如果您明确地不想延长 UIViewController
对象的生命周期,而是希望块 - 当它最终执行时 - 检查是否仍存在这个 UIViewController
对象,您可以使用 self 的 __weak 指针。请注意,无论 UIViewController
是否仍然存活或已在此期间被释放,块最终都将被执行。
如果您希望在块在执行之前已经释放了 UIViewController
,则可能希望使块什么也不做:
MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
MyController* strongSelf = weakSelf;
if (strongSelf) {
...
}
else {
}
});
另请参阅:转换为 ARC 发布说明
记住:非主线程不得发送 UIKit
对象的方法!
由于UIKit
对象仅能在主线程上执行方法,因此可能会出现一个微妙的错误。
如果块捕获了一个UIKit
对象并异步分派它,在非主线程上执行,则可能发生这种情况。然后,块将保持对该UIKit
对象的最后一个强引用。当块最终被执行时,块将被销毁,UIKit
对象将被释放。由于这是对UIKit
对象的最后一个强引用,因此它将被释放。但是,这是在执行块的线程上发生的-而这不是主线程!现在,不好的事情可能(并且通常)会发生,因为dealloc
方法仍然是发送给UIKit
对象的方法。
您可以通过调度捕获指向该 UIKit 对象的强指针的块,并向其发送虚拟方法来避免此错误:
UIViewController* strongUIKitPointer = ...
dispatch_async(non_main_queue, ^{
...
dispatch(dispatch_get_main_queue(), ^{
[strongUIKitPointer self];
});
});
在您的场景中,最后一个强引用可能仅存在于在主线程上执行的块中。因此,您可以避免这种微妙的错误。 ;)
编辑:
在您的设置中,您永远不会出现保留循环。 如果可保留对象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
这里,我们有一个名为completion的属性,其值类型为一个Block。也就是说,我们会得到一个名为_completion
且类型为Block的ivar。
客户端可以设置完成处理程序,当某个操作完成时应该调用该处理程序。假设,该操作从远程服务器获取用户列表。计划是在操作完成后设置users属性:
粗心大意的方法会意外地引入一个循环引用:
在“UsersViewController.m”中的某个地方
self.completion = ^(NSArray* users){
self.users = users;
}
[self fetchUsers];
在这里,self 持有一个指向 _completion
的强引用,这是一个块对象。而且该块对象捕获 self,当块对象被复制并分发时,就会导致 self 被保留。这是一个典型的引用循环。
为了避免这种循环引用,我们有几个选择:
使用 __weak
限定符修饰的指针来引用 self
UsersViewController* __weak weakSelf = self;
self.completion = ^(NSArray* users) {
UsersViewController* strongSelf = weakSelf;
if (strongSelf) {
strongSelf.users = users;
}
else {
}
}
[usersViewController fetchUsers];
使用带有
__block
修饰符的
self指针,并在块完成时将其设置为
nil
:
UsersViewController* __block blockSelf = self;
self.completion = ^(NSArray* users) {
blockSelf.users = users;
blockSelf = nil;
}
[usersViewController fetchUsers];
相关链接: 转换到ARC发布说明
dispatch_async
之后,即使是一个虚拟函数在主线程上执行,它也可以防止出现保留循环,并且我不需要使用__weak
引用来避免? - Marko Zadravec