__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
は必要ありません。
selfがUIViewController
へのオブジェクトポインターであると仮定します。
考慮事項:
UIViewController
は「UIKit」オブジェクトです。 UIKitオブジェクトは、非メインスレッドのメソッドに送信されません。つまり、これらのメソッドはメインスレッドでのみ実行する必要があります。
キューに入れられたブロック-これが同期的であるか非同期的であるかにかかわらず、最終的には実行済み-何であれ!まあ、これが起こる前にプログラムが終了しない限り。
キャプチャーされた保持可能strongポインターはretainedブロックがコピーされるとき(たとえば、非同期にディスパッチされるとき)、再びreleasedブロックがされるとき(終了後)破棄されます。
キャプチャーされた保持可能weakポインターは保持されず、解放されません。
メインキューでディスパッチされるブロックでselfをキャプチャするシナリオでは、悪いことが起こることを心配する必要はありません。
selfはcapturedディスパッチされるブロックでasynchronouslyになるので、selfは暗黙的にretained、およびreleasedブロックが終了したら再び。
つまり、selfの有効期間は、ブロックが終了するまでextendedになります。 secondブロックがメインスレッドでディスパッチされ、そのブロックが実行されたときにselfがまだ生きていることが保証されていることに注意してください。
上記の「寿命の延長」は、プログラムの望ましい機能である可能性があります。
explicitlyUIViewController
オブジェクトの寿命を延長したくなく、代わりにブロックが必要な場合-最終的に実行されるとき-checkかどうかこのUIViewController
オブジェクトはまだ存在しているので、selfの__weakポインターを使用できます。 UIViewController
がまだ生きているか、その間に割り当て解除されたかに関係なく、ブロックは最終的に実行されることに注意してください。
UIViewController
の割り当てが解除された場合、ブロックは「何もしない」ことを望むかもしれませんbeforeブロックが実行されます:
MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
MyController* strongSelf = weakSelf;
if (strongSelf) {
...
}
else {
// self has been deallocated in the meantime.
}
});
参照: ARCリリースノートへの移行
UIKit
オブジェクトは、非メインスレッドのメソッドに送信されません。UIKit
オブジェクトがメインスレッドでのみメソッドを実行するという事実により、もう1つの微妙なエラーが発生する場合があります。
ブロックが非同期にディスパッチされるUIKit
オブジェクトをキャプチャし、non-mainスレッドで実行される場合、これは違反される可能性があります。その後、ブロックがそのUIKit
オブジェクトへのlast強い参照を保持することが起こります。これで、最終的にブロックが実行されると、ブロックが破棄され、UIKit
オブジェクトが解放されます。これが最後のstrongUIKit
オブジェクトへの参照であるため、割り当てが解除されます。ただし、これはブロックが実行されたスレッドで発生します。これはメインスレッドではありません。 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
という名前のivarを取得します。
クライアントは、特定の操作が終了したときに呼び出される完了ハンドラーを設定できます。操作がリモートサーバーからユーザーのリストを取得するとします。操作が終了したら、プロパティsersを設定する計画です。
不注意なアプローチでは、誤って循環参照が導入されます。
「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];
参照: 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
はこのアプローチを使用しています。
[weak self]およびguard letstrongSelf =を使用してオブジェクトの寿命を延長self else {return}イディオム。
詳細については Swift-style-guide をご覧ください