addObserverForName這個方法并不常用, 但估計很多人和我一樣, 最開始的時候也不太了解這個方法, 再看了網(wǎng)上的一些技術(shù)貼, 更是對addObserverForName產(chǎn)生了誤解.
應(yīng)用場景
當(dāng)通知的發(fā)出不在主線程, 但是發(fā)出的通知需要更新UI, 就可能會用到這個方法.
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name
object:(nullable id)obj
queue:(nullable NSOperationQueue *)queue
usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
// The return value is retained by the system, and should be held onto by the caller in
// order to remove the observer with removeObserver: later, to stop observation.
這里我把蘋果爸爸的注釋也copy上了, 意思, 是使用者要持有addObserverForName的返回值, 也就是持有這個observer, 后面需要通過removeObserver來停止監(jiān)聽. 但實際使用中, 我們會發(fā)現(xiàn), addObserverForName并不需要caller去持有這個observer, 只需要在block中使用weak, 系統(tǒng)就會在dealloc中幫我們?nèi)emove這個observer.
沒想到幾年前的一篇學(xué)習(xí)心得會給大家造成這么大的困擾惩歉,在此表示表示歉意等脂,上面的這種說法是不嚴(yán)謹(jǐn)?shù)模皇且驗槲覀兪褂昧藈eak系統(tǒng)才幫我們?nèi)emove撑蚌,而且從iOS9開始上遥,當(dāng)對象dealloc的時候,系統(tǒng)會幫我執(zhí)行removeObserver
, 所以這里使用weak是為了不讓block對self產(chǎn)生強(qiáng)引用锨并,進(jìn)而產(chǎn)生循環(huán)引用露该,即系統(tǒng)(NSNotification
內(nèi)部持有block),block持有self第煮,self不釋放解幼,系統(tǒng)block也不會觸發(fā)釋放
由于有人質(zhì)疑此文是拿來主義,為此本人還親自重新做了實驗包警,與當(dāng)時的結(jié)果還是一樣的撵摆,這里把實驗的細(xì)節(jié)也放上來,也是當(dāng)時做實驗的一些心路歷程害晦。
實驗1:block中使用self, 在不適用后進(jìn)行移除
結(jié)論1:沒有用, self沒法釋放, dealloc不會執(zhí)行, 但觀察者可以成功移除特铝,beginLuckyDogs 只會執(zhí)行一次
- (void)dealloc
{
NSLog(@"begin dealloc");
[[NSNotificationCenter defaultCenter] removeObserver:self.observer name:@"luckyDogNotification" object:nil];
}
- (void)beginLuckyDogs {
NSLog(@"beginLuckyDogs=========");
}
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"luckyDogNotification" object:nil];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"luckyDogNotification");
[[NSNotificationCenter defaultCenter] removeObserver:self.observer name:@"luckyDogNotification" object:nil];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"luckyDogNotification" object:nil];
});
self.observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"luckyDogNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[self beginLuckyDogs];
}];
}
實驗2:同實驗1暑中,只是在移除觀察的時候用[[NSNotificationCenter defaultCenter] removeObserver:self];
結(jié)論2:沒有用,觀察無法移除成功鲫剿,self無法釋放
- (void)dealloc
{
NSLog(@"begin dealloc");
[[NSNotificationCenter defaultCenter] removeObserver:self.observer name:@"luckyDogNotification" object:nil];
}
- (void)beginLuckyDogs {
NSLog(@"beginLuckyDogs=========");
}
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"luckyDogNotification" object:nil];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"luckyDogNotification");
[[NSNotificationCenter defaultCenter] removeObserver:self];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(12 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"luckyDogNotification" object:nil];
});
[[NSNotificationCenter defaultCenter] addObserverForName:@"luckyDogNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[self beginLuckyDogs];
}];
實驗3:類似實驗2鳄逾,只是block中使用weak
結(jié)論3:self可以釋放,但觀察者[[NSNotificationCenter defaultCenter] removeObserver:self];不能正常移除成功灵莲,beginLuckyDogs會執(zhí)行多次
- (void)dealloc
{
NSLog(@"begin dealloc");
[[NSNotificationCenter defaultCenter] removeObserver:self.observer name:@"luckyDogNotification" object:nil];
}
- (void)beginLuckyDogs {
NSLog(@"beginLuckyDogs=========");
}
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"luckyDogNotification" object:nil];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"luckyDogNotification");
[[NSNotificationCenter defaultCenter] removeObserver:self];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(12 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"luckyDogNotification" object:nil];
});
__weak typeof(self) weak_self = self;
[[NSNotificationCenter defaultCenter] addObserverForName:@"luckyDogNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[weak_self beginLuckyDogs];
}];
}
實驗4:block中使用weak, 不顯式調(diào)用remove操作
結(jié)論4:self可以正常釋放雕凹,不會多次執(zhí)行beginLuckyDogs
- (void)dealloc
{
NSLog(@"begin dealloc");
}
- (void)beginLuckyDogs {
NSLog(@"beginLuckyDogs=========");
}
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"luckyDogNotification" object:nil];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"luckyDogNotification" object:nil];
});
__weak typeof(self) weak_self = self;
[[NSNotificationCenter defaultCenter] addObserverForName:@"luckyDogNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[weak_self beginLuckyDogs];
}];
}
由于之前的文章,少了一些中間的心路歷程政冻,造成了大家的誤解枚抵,評論區(qū)有說學(xué)藝不精
,誤人子弟
的都欣然接受明场。
有些同學(xué)可能并不知道系統(tǒng)會幫我們remove這個情況, 還是會在dealloc中去無腦調(diào)用一次[[NSNotificationCenter defaultCenter] removeObserver:self];
但是我們應(yīng)該知道[[NSNotificationCenter defaultCenter] removeObserver:self];
應(yīng)該是用于普通的addObserver
的移除的.
上面這段描述也不夠準(zhǔn)確汽摹,針對iOS9系統(tǒng)才會不移自除,對于非iOS9苦锨,還是要用
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;
進(jìn)行移除逼泣,[[NSNotificationCenter defaultCenter] removeObserver:self];
無法移除- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
方式添加的觀察者
從iOS9以后,確實在對下釋放的時候系統(tǒng)會幫我們進(jìn)行remove操作逆屡,本文的核心意思主要是針對
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name
object:(nullable id)obj
queue:(nullable NSOperationQueue *)queue
usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
// The return value is retained by the system, and should be held onto by the caller in
// order to remove the observer with removeObserver: later, to stop observation.
這段代碼中圾旨,蘋果的這個注釋展開,并結(jié)合使用中出現(xiàn)強(qiáng)引用問題造成對象無法釋放問題的調(diào)查魏蔗。