Mam kontroler widoku, który pobiera zasób w kolejce GCD tła. Przekażę mojej funkcji pobierania blok zwrotny do wykonania po zakończeniu pobierania i zawsze wykonuje ten blok w głównym wątku.dealloc wywołana na tle aplikacji powodującej awarię kolejki GCD zbudowanej za pomocą ARC
Problem występuje, gdy mój kontroler widoku zostanie odrzucony przez użytkownika przed zakończeniem pobierania. Podejrzewam, że to się dzieje, gdy mój kontroler widoku zostanie zwolniony, blok wywołania zwrotnego jest jedyną rzeczą, która ma silne odniesienie do kontrolera. Blok wywołania zwrotnego jest zachowywany tylko w wątku tła, więc po jego zwolnieniu wszystkie obiekty przechwycone w ramach bloku wywołania zwrotnego są również wydawane, ale w kolejce w tle.
To jest problem: zwolnienie w kolejce w tle powoduje, że dealloc działa w tej samej kolejce, a nie w głównej kolejce. To z kolei wywołuje dealloc
w tle i awarie aplikacji:
2012-01-19 12:47:36.349 500px iOS[4892:12107] bool _WebTryThreadLock(bool), 0x306c10: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
[Switching to process 16643 thread 0x4103]
[Switching to process 16643 thread 0x4103]
(gdb) where
#0 0x307fd3c8 in _WebTryThreadLock()
#1 0x307ff1b0 in WebThreadLock()
#2 0x33f7865e in -[UITextView dealloc]()
#3 0x0005d2ce in -[GCPlaceholderTextView dealloc] (self=0x309010, _cmd=0x340ce6b8) at /Users/ash/Dropbox/500px/500px-ios/500px iOS/500px iOS/GCPlaceholderTextView.m:113
#4 0x337cac42 in -[NSObject(NSObject) release]()
#5 0x33dee5f4 in -[UIView(Hierarchy) removeFromSuperview]()
#6 0x33e836cc in -[UIScrollView removeFromSuperview]()
#7 0x33f762f0 in -[UITextView removeFromSuperview]()
#8 0x33e01de2 in -[UIView dealloc]()
#9 0x337cac42 in -[NSObject(NSObject) release]()
#10 0x33dee5f4 in -[UIView(Hierarchy) removeFromSuperview]()
#11 0x33e01de2 in -[UIView dealloc]()
#12 0x33f437e4 in -[UIScrollView dealloc]()
#13 0x337cac42 in -[NSObject(NSObject) release]()
#14 0x33dee5f4 in -[UIView(Hierarchy) removeFromSuperview]()
#15 0x33e836cc in -[UIScrollView removeFromSuperview]()
#16 0x33e01de2 in -[UIView dealloc]()
#17 0x337cac42 in -[NSObject(NSObject) release]()
#18 0x33dee5f4 in -[UIView(Hierarchy) removeFromSuperview]()
#19 0x33e01de2 in -[UIView dealloc]()
#20 0x337cac42 in -[NSObject(NSObject) release]()
#21 0x33e5a00e in -[UIViewController dealloc]()
#22 0x00035f16 in -[PXPhotoViewController dealloc] (self=0x5158d0, _cmd=0x340ce6b8) at /Users/ash/Dropbox/500px/500px-ios/500px iOS/500px iOS/PXPhotoViewController.m:118
#23 0x337cac42 in -[NSObject(NSObject) release]()
#24 0x337e5046 in sendRelease()
#25 0x331fc92e in _Block_object_dispose()
#26 0x0003c33a in __destroy_helper_block_() at /Users/ash/Dropbox/500px/500px-ios/500px iOS/500px iOS/PXPhotoViewController.m:878
#27 0x331fc88e in _Block_release()
#28 0x331fc91c in _Block_object_dispose()
#29 0x000c8d32 in __destroy_helper_block_() at /Users/ash/Dropbox/500px/500px-ios/500px iOS/500px iOS/PXPhotoFetcher.m:557
#30 0x331fc88e in _Block_release()
#31 0x35eec8ec in _dispatch_call_block_and_release()
#32 0x35ee2de2 in _dispatch_queue_drain()
#33 0x35ee2f32 in _dispatch_queue_invoke()
#34 0x35ee24f2 in _dispatch_worker_thread2()
#35 0x34ecb590 in _pthread_wqthread()
#36 0x34ecbbc4 in start_wqthread()
Kod mogę wykonać w głównym wątku wygląda następująco:
[[PXPhotoFetcher sharedPXPhotoFetcher] fetchPhotoDetailsWithPriority:PhotoRequestLowPriority
withCallback:^(PXPhotoModel *thePhotoModel) {
// a callback function which captures self in its scope
} forModel:model];
buduję na 4.3, więc jeśli Używam __unsafe_unretained odwołanie do siebie w bloku oddzwaniania, to naprawi mój bieżący problem, ale wprowadzi nowy problem posiadania zwisającego wskaźnika.
Co jest zalecane do zaprojektowania rozwiązania tego problemu? Inne obejścia obejmowały overriding release
, więc zawsze był wywoływany w głównym wątku, ale to oczywiście nie zadziała w środowisku ARC.
Wydaje się, że powinien to być znacznie częstszy problem z ARC i GCD, ale nie mogę znaleźć niczego w Internecie. Czy to dlatego, że jestem kierowany na < iOS 5 i nie mogę używać słabych referencji?
Jeśli wolisz słabe odniesienie, możesz sprawdzić http://mikeash.com/pyblog/introducing-mazeroingweakref.html – tapi
To trochę brzydkie, ale to sprytna sztuczka. Chciałbym mieć lepszą odpowiedź. –
Tak, popatrzyłem na rozwiązanie Mike'a Asha, ale ponieważ w krótkim okresie będę wspierać system operacyjny iOS 4.3, pozostanę z tym hackem przez jakiś czas. –