NSNotificationCenter addObserverForName

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)查魏蔗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市痹筛,隨后出現(xiàn)的幾起案子莺治,更是在濱河造成了極大的恐慌,老刑警劉巖帚稠,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谣旁,死亡現(xiàn)場離奇詭異,居然都是意外死亡滋早,警方通過查閱死者的電腦和手機(jī)榄审,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杆麸,“玉大人搁进,你說我怎么就攤上這事∥敉罚” “怎么了饼问?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長揭斧。 經(jīng)常有香客問我莱革,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任盅视,我火速辦了婚禮捐名,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘闹击。我一直安慰自己镶蹋,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布拇砰。 她就那樣靜靜地躺著梅忌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪除破。 梳的紋絲不亂的頭發(fā)上牧氮,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機(jī)與錄音瑰枫,去河邊找鬼踱葛。 笑死,一個胖子當(dāng)著我的面吹牛光坝,可吹牛的內(nèi)容都是我干的尸诽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼盯另,長吁一口氣:“原來是場噩夢啊……” “哼性含!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸳惯,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤商蕴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后芝发,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绪商,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年辅鲸,在試婚紗的時候發(fā)現(xiàn)自己被綠了格郁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡独悴,死狀恐怖例书,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绵患,我是刑警寧澤雾叭,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站落蝙,受9級特大地震影響织狐,放射性物質(zhì)發(fā)生泄漏暂幼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一移迫、第九天 我趴在偏房一處隱蔽的房頂上張望旺嬉。 院中可真熱鬧,春花似錦厨埋、人聲如沸邪媳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雨效。三九已至,卻和暖如春废赞,著一層夾襖步出監(jiān)牢的瞬間徽龟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工唉地, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留据悔,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓耘沼,卻偏偏與公主長得像极颓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子群嗤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容