iOS攔截系統(tǒng)KVO監(jiān)聽,防止多次刪除和添加【it is not registered as an observer.】

淺談
  • 最近項目中處理kvo 的時候渐逃,遇到一個問題:當我操作的時候,會發(fā)現kvo 釋放的時候民褂,會崩潰茄菊, 崩潰日志如下

Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observerfor the key path "kvoState" frombecause it is not registered as an observer.'

經過反復研究,發(fā)現了錯誤的原因赊堪,并且找到解決錯誤的辦法下面我將介紹一下我的思路:(慢慢來 跟著我的思路走)

  • 1面殖、我在AppDelegate里面添加一個屬性
//測試kvo設置的一個字段 
@property(nonatomic,copy)NSString *kvoState;
  • 2、我在我創(chuàng)建的一個ViewController(SecondViewController)里面去監(jiān)聽這個屬性哭廉,但是沒有 調用monitorNet 方法
- (void)monitorNet {
    
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication  sharedApplication].delegate;    // kvo監(jiān)聽屬性值的改變
    [appDelegate addObserver:self forKeyPath:@"kvoState" options:NSKeyValueObservingOptionNew context:nil];
}

/**
 *  KVO 監(jiān)聽方法
 *
 *  @param keyPath 監(jiān)聽的屬性名稱
 *  @param object 被監(jiān)聽的對象
 *  @param change 屬性的值
 *  @param context 添加監(jiān)聽時傳來的值
 */
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context  {
    
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    
    if ([keyPath isEqualToString:@"kvoState"]) {
        
        NSNumber *number = [change objectForKey:@"new"];
        
        NSInteger item = [number integerValue];
        
        NSLog(@"%@====",appDelegate.kvoState);
        
        NSLog(@"%@----",number);
        
        if ([object isKindOfClass:[AppDelegate class]] ) {
            
        }
        
    }
    
}

  • 3脊僚、然后我再去釋放 復寫系統(tǒng) dealloc 這個方法
-(void)dealloc {
    NSLog(@"銷毀了");
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    
    [appDelegate removeObserver:self forKeyPath:@"kvoState"];
    
    //或者多次調用
    [appDelegate removeObserver:self forKeyPath:@"kvoState"];

}
  • 4、在第二步之后遵绰,我點擊一個button 辽幌,push 到 另外一個ViewController(TestViewController)里面,然后在TestViewController里面椿访,點擊button 乌企,在這個button 的點擊事件里面去執(zhí)行下面的代碼:(特地演示錯誤)
- (IBAction)btnAction:(id)sender {
    
    SecondViewController *vc = [[SecondViewController alloc] init];
    [self.navigationController pushViewController:vc  animated:YES];
}

當這個方法執(zhí)行完之后,就會出現前面所展示的錯誤

*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <SecondViewController 0x7f8b7ef0bad0> for the key path "kvoState" from <AppDelegate 0x600002c8b020> because it is not registered as an observer.'
*** First throw call stack:

為什么會出現這種錯誤呢赎离?逛犹??梁剔?其實出現這種錯誤也很簡單的:
首先在buttonAction 這個方法內虽画,secondVC 他是一個局部變量,現在是ARC 管理荣病,當這個方法執(zhí)行完成以后码撰,會銷毀 secondVC 這個對象,那么个盆,很自然的就會調用 SecondViewController 里面的 dealloc 這個方法【也就是第三步的方法脖岛,請看第三步】

解釋:

根據錯誤提示,appDelegate 的屬性kvoState 會被remove颊亮,但是的這個時候柴梆,it is not registered as an observer,所以终惑,就會出現上述的崩潰現象說了這么多绍在,大家能理解這個崩潰的原因了嗎?(PS:不懂的話也請繼續(xù)了解下面的內容)

總之就是:有時候我們會忘記添加多次KVO監(jiān)聽或者,不小心刪除如果KVO監(jiān)聽偿渡,如果添加多次KVO監(jiān)聽這個時候我們就會接受到多次監(jiān)聽臼寄。如果刪除多次kvo程序就會造成catch既然問題的出現,那么溜宽,肯定會伴隨著事務的解決

下面我講給大家講解幾個解決的方法(百度查資料的吉拳,親自驗證,安全可靠)适揉,

方案有三種:

那么iOS開發(fā)-黑科技防止多次添加刪除KVO出現的問題

  • 方案一 :利用 @try @catch

  • 利用 @try @catch(只能針對刪除多次KVO的情況下)

    利用 @try @catc 不得不說這種方法真是很Low留攒,不過很簡單就可以實現。(對于初學者來說涡扼,如果不怕麻煩稼跳,確實可以使用這種方法)
    這種方法只能針對多次刪除KVO的處理盟庞,原理就是try catch可以捕獲異常吃沪,不讓程序 catch。這樣就實現了防止多次刪除KVO什猖。

    在dealloc方法里面執(zhí)行下面代碼(我只是舉個例子票彪,監(jiān)聽的對象不一樣,具體代碼也不一樣)

-(void)dealloc {
    
    NSLog(@"銷毀了");
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

    @try {
        [appDelegate removeObserver:self forKeyPath:@"kvoState"];

        //或者多次調用
        [appDelegate removeObserver:self forKeyPath:@"kvoState"];

    } @catch (NSException *exception) {
        NSLog(@"捕獲異常了");

    } @finally {
        NSLog(@"finally");
    }
}

上述方法基本可以解決這個崩潰的問題不狮,那么有沒有更好的方法解決同類的問題呢降铸?

  • 利用Run time
    給NSObject 增加一個分類,然后利用Run time 交換系統(tǒng)的 removeObserver方法摇零,在里面添加 @try @catch推掸。

  • 步驟
    創(chuàng)建一個類目category
    在銷毀KVO監(jiān)聽對象的文件里面導入頭文件 #import "NSObject+MKVO.h"


#import "NSObject+MKVO.h"
#import <objc/runtime.h>

@implementation NSObject (MKVO)

+ (void)load{
    
    [self switchMethod];
    
}
+ (void)switchMethod{
    
    //移除kvo的方法
    SEL removeSel = @selector(removeObserver:forKeyPath:);
    SEL myRemoveSel = @selector(removeDasen:forKeyPath:);
    
    //監(jiān)聽的方法
    SEL addSel = @selector(addObserver:forKeyPath:options:context:);
    SEL myaddSel = @selector(addDasen:forKeyPath:options:context:);
    
    
    Method systemRemoveMethod = class_getClassMethod([self class],removeSel);
    Method DasenRemoveMethod = class_getClassMethod([self class], myRemoveSel);
    
    Method systemAddMethod = class_getClassMethod([self class],addSel);
    Method DasenAddMethod = class_getClassMethod([self class], myaddSel);
    
    //交換方法的實現
    method_exchangeImplementations(systemRemoveMethod, DasenRemoveMethod);
    method_exchangeImplementations(systemAddMethod, DasenAddMethod);
}

//利用@try @catch
// 交換后的方法
- (void)removeDasen:(NSObject *)observer forKeyPath:(NSString *)keyPath{
    
    @try {//相對應解決方法1而已,只是把@try @catch 寫在這里而已
    
        [self removeDasen:observer forKeyPath:keyPath];
    
    } @catch (NSException *exception) {
    
    }
}

// 交換后的方法
- (void)addDasen:(NSObject *)observer forKeyPath:(NSString *)keyPath options:
(NSKeyValueObservingOptions)options context:(void *)context{
    
    [self addDasen:observer forKeyPath:keyPath options:options context:context];
    
}

總結: 在 dealloc 方法里面驻仅,調用removeObserver:forKeyPath: 方法谅畅,其實就是調用 分類category 里面的removeDasen: forKeyPath:方法了,因為利益runtime噪服,交換了這兩個方法的實現

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末毡泻,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子粘优,更是在濱河造成了極大的恐慌仇味,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雹顺,死亡現場離奇詭異丹墨,居然都是意外死亡,警方通過查閱死者的電腦和手機嬉愧,發(fā)現死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門贩挣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事揽惹”欢觯” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵搪搏,是天一觀的道長狭握。 經常有香客問我,道長疯溺,這世上最難降的妖魔是什么论颅? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮囱嫩,結果婚禮上恃疯,老公的妹妹穿的比我還像新娘。我一直安慰自己墨闲,他們只是感情好今妄,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸳碧,像睡著了一般盾鳞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞻离,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天腾仅,我揣著相機與錄音,去河邊找鬼套利。 笑死推励,一個胖子當著我的面吹牛,可吹牛的內容都是我干的肉迫。 我是一名探鬼主播验辞,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼昂拂!你這毒婦竟也來了受神?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤格侯,失蹤者是張志新(化名)和其女友劉穎鼻听,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體联四,經...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡撑碴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了朝墩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片醉拓。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出亿卤,到底是詐尸還是另有隱情愤兵,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布排吴,位于F島的核電站秆乳,受9級特大地震影響,放射性物質發(fā)生泄漏钻哩。R本人自食惡果不足惜屹堰,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望街氢。 院中可真熱鬧扯键,春花似錦、人聲如沸珊肃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽近范。三九已至嘶摊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間评矩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工阱飘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留斥杜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓沥匈,卻偏偏與公主長得像蔗喂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子高帖,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348