[英]Using weak self in dispatch_async function
我讀了很多關於在dispatch_async
使用__weak self
的帖子,現在我有點困惑。
如果我有 :
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對象不應該在非主線程上發送方法,即 - 那些方法只能在主線程上執行!
已排隊的塊 - 無論是同步還是異步 - 最終將被執行 - 無論如何! 好吧,除非程序在此之前終止。
當復制塊時(例如,異步調度時)將保留捕獲的可保留 強指針,並在塊被銷毀時(在完成后)再次釋放 。
捕獲的可保留弱指針不會被保留而不會被釋放。
在您的方案中,您在主隊列中調度的塊中捕獲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.
}
});
另請參閱: 轉換到ARC發行說明
UIKit
對象不應該在非主線程上發送方法! 由於UIKit
對象僅在主線程上執行方法,因此可能會發生另一個細微錯誤。
如果一個塊捕獲異步調度的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,則發生保留周期。請注意,“塊”也是可保留對象。
一個帶有循環引用的人為例子:
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
的ivar,其類型為Block。
客戶端可以設置完成處理程序,當某個操作完成時應該調用該處理程序。 假設,該操作從遠程服務器獲取用戶列表。 計划是在操作完成后設置屬性用戶 :
不小心的方法會不小心引入循環引用:
在“UsersViewController.m”中的某處
self.completion = ^(NSArray* users){
self.users = users;
}
[self fetchUsers]; // start asynchronous task
在這里, self強烈提到了ivar _completion
,這是一個塊。 並且塊本身捕獲self ,這導致在塊被調度時復制塊時保留self 。 這是一個經典的參考周期。
為了避免這種循環引用,我們有幾個選擇:
使用__weak
限定的self指針
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
限定的self指針,並在完成時最終在塊中將其設置為nil
:
UsersViewController* __block blockSelf = self; self.completion = ^(NSArray* users) { blockSelf.users = users; blockSelf = nil; } [usersViewController fetchUsers];
另請參閱: 轉換到ARC發行說明
在swift中這種所謂的強弱舞蹈的一個例子:
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 =自其他{}回報成語。
有關更多信息,請查看swift-style-guide
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.