成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

iOS自動(dòng)移除KVO觀察者的實(shí)現(xiàn)方法

問題

成都創(chuàng)新互聯(lián)公司是一家專注于成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站與策劃設(shè)計(jì),即墨網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設(shè)10多年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:即墨等地區(qū)。即墨做網(wǎng)站價(jià)格咨詢:18982081108

KVO即:Key-Value Observing, 直譯為:基于鍵值的觀察者。 它提供一種機(jī)制,當(dāng)指定的對(duì)象的屬性被修改后,則對(duì)象就會(huì)接受到通知。 簡(jiǎn)單的說就是每次指定的被觀察的對(duì)象的屬性被修改后,KVO就會(huì)自動(dòng)通知相應(yīng)的觀察者了。

KVO的優(yōu)點(diǎn):當(dāng)有屬性改變,KVO會(huì)提供自動(dòng)的消息通知。 這樣開發(fā)人員不需要自己去實(shí)現(xiàn)這樣的方案:每次屬性改變了就發(fā)送消息通知。 這是KVO機(jī)制提供的最大的優(yōu)點(diǎn)。 因?yàn)檫@個(gè)方案已經(jīng)被明確定義,獲得框架級(jí)支持,可以方便地采用。 開發(fā)人員不需要添加任何代碼,不需要設(shè)計(jì)自己的觀察者模型,直接可以在工程里使用。 其次,KVO的架構(gòu)非常的強(qiáng)大,可以很容易的支持多個(gè)觀察者觀察同一個(gè)屬性,以及相關(guān)的值。

但我們都知道, 使用KVO模式, 對(duì)某個(gè)屬性進(jìn)行監(jiān)聽時(shí), Observer 需要在必要的時(shí)刻進(jìn)行移除, 否則 App 必然會(huì) Crash. 這個(gè)問題有點(diǎn)煩人, 因?yàn)榕紶枙?huì)忘記寫移除 Observer 的代碼...

我一直想要這樣一個(gè)效果:

只管監(jiān)聽, 并處理監(jiān)聽方法. 不去分心, 管何時(shí)移除 Observer , 讓其能夠適時(shí)自動(dòng)處理.

所幸, 它能夠?qū)崿F(xiàn), 先預(yù)覽一下:

@interface NSObject (SJObserverHelper)

- (void)sj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

@end

@interface SJObserverHelper : NSObject
@property (nonatomic, unsafe_unretained) id target;
@property (nonatomic, unsafe_unretained) id observer;
@property (nonatomic, strong) NSString *keyPath;
@property (nonatomic, weak) SJObserverHelper *factor;
@end

@implementation SJObserverHelper
- (void)dealloc {
 if ( _factor ) {
 [_target removeObserver:_observer forKeyPath:_keyPath];
 }
}
@end

@implementation NSObject (ObserverHelper)

- (void)sj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
 
 [self addObserver:observer forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:nil];
 
 SJObserverHelper *helper = [SJObserverHelper new];
 SJObserverHelper *sub = [SJObserverHelper new];
 
 sub.target = helper.target = self;
 sub.observer = helper.observer = observer;
 sub.keyPath = helper.keyPath = keyPath;
 helper.factor = sub;
 sub.factor = helper;
 
 const char *helpeKey = [NSString stringWithFormat:@"%zd", [observer hash]].UTF8String;
 objc_setAssociatedObject(self, helpeKey, helper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
 objc_setAssociatedObject(observer, helpeKey, sub, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

項(xiàng)目源碼

下面來說說一步一步的實(shí)現(xiàn)吧:

初步思路實(shí)現(xiàn):

我們都知道, 對(duì)象被釋放之前, 會(huì)調(diào)用dealloc方法, 其持有的實(shí)例變量也會(huì)被釋放.

我就這樣想, 在監(jiān)聽注冊(cè)時(shí), 為self和Observer關(guān)聯(lián)個(gè)臨時(shí)對(duì)象, 當(dāng)兩者在釋放實(shí)例變量時(shí), 我借助這個(gè)時(shí)機(jī), 在臨時(shí)對(duì)象的dealloc方法中, 移除Observer就行了.

想法很好, 可總不能每個(gè)類里都加一個(gè)臨時(shí)對(duì)象的屬性吧. 那如何在不改變?cè)蓄惖那闆r下, 為其關(guān)聯(lián)一個(gè)臨時(shí)對(duì)象呢?

關(guān)聯(lián)屬性

不改變?cè)蓄? 這時(shí)候肯定是要用Category了, 系統(tǒng)框架里面有很多的分類, 并且有很多的關(guān)聯(lián)屬性, 如下圖 UIView 頭文件第180行:

iOS自動(dòng)移除KVO觀察者的實(shí)現(xiàn)方法

依照上圖, 我們先看一個(gè)示例, 為NSObject的添加一個(gè)Category, 并添加了一個(gè)property, 在.m中實(shí)現(xiàn)了它的setter和getter方法.

#import <objc/message.h>
@interface NSObject (Associate)
@property (nonatomic, strong) id tmpObj;
@end
@implementation NSObject (Associate)
static const char *testKey = "TestKey";
- (void)setTmpObj:(id)tmpObj {
 // objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
 objc_setAssociatedObject(self, testKey, tmpObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)tmpObj {
 // objc_getAssociatedObject(id object, const void *key)
 return objc_getAssociatedObject(self, testKey);
}
@end

很明確, objc_setAssociatedObject 便是關(guān)聯(lián)屬性的setter方法, 而objc_getAssociatedObject便是關(guān)聯(lián)屬性的getter方法. 最需要關(guān)注的就是setter方法, 因?yàn)槲覀円脕硖砑雨P(guān)聯(lián)屬性對(duì)象.

初步思路探索

初步嘗試:

既然屬性可以隨時(shí)使用objc_setAssociatedObject關(guān)聯(lián)了, 那我就嘗試先為self關(guān)聯(lián)一個(gè)臨時(shí)對(duì)象, 在其dealloc中, 將Observer移除.

@interface SJObserverHelper : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, weak) id observer;
@property (nonatomic, strong) NSString *keyPath;
@end
@implementation SJObserverHelper
- (void)dealloc {
 [_target removeObserver:_observer forKeyPath:_keyPath];
}
@end
- (void)addObserver {
 NSString *keyPath = @"name";
 [_xiaoM addObserver:_observer forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:nil]; 
 SJObserverHelper *helper_obj = [SJObserverHelper new];
 helper_obj.target = _xiaoM;
 helper_obj.observer = _observer;
 helper_obj.keyPath = keyPath;
 const char *helpeKey = [NSString stringWithFormat:@"%zd", [_observer hash]].UTF8String;
 // 關(guān)聯(lián)
 objc_setAssociatedObject(_xiaoM, helpeKey, helper_obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

于是, 美滋滋的運(yùn)行了一下程序, 當(dāng)將_xiaoM 置為 nil 時(shí), 砰 App Crash......

reason: 'An instance 0x12cd1c370 of class Person was deallocated while key value observers were still registered with it.

分析: 臨時(shí)對(duì)象的dealloc, 確確實(shí)實(shí)的跑了. 為什么會(huì)還有registered? 于是我嘗試在臨時(shí)對(duì)象的dealloc中, 打印實(shí)例變量target, 發(fā)現(xiàn)其為nil. 好吧, 這就是Crash問題原因!

嘗試 unsafe_unretained

通過上面操作, 我們知道self在被釋放之前, 會(huì)先釋放其持有的關(guān)聯(lián)屬性, self并未完全釋放, 可在臨時(shí)對(duì)象中target卻成了nil. 同時(shí)self還是有效的, 那如何保持不為nil呢?

我們看看OC中的兩個(gè)修飾符weak與unsafe_unretained:

  • weak: 持有者不會(huì)對(duì)目標(biāo)進(jìn)行retain, 當(dāng)目標(biāo)銷毀時(shí), 持有者的實(shí)例變量會(huì)被置空
  • unsafe_unretained: 持有者不會(huì)對(duì)目標(biāo)進(jìn)行retain, 當(dāng)目標(biāo)釋放后, 持有者的實(shí)例變量還會(huì)依然指向之前的內(nèi)存空間(野指針)

由上, unsafe_unretained很好的解決了我們的問題. 于是我做了如下修改:

@interface SJObserverHelper : NSObject
@property (nonatomic, unsafe_unretained) id target;
@property (nonatomic, unsafe_unretained) id observer;
@property (nonatomic, strong) NSString *keyPath;
@end

再次運(yùn)行程序, 還行, 觀察者移除了.

最終實(shí)現(xiàn)

還存在的問題

目前, 我們只是實(shí)現(xiàn)了, 如何在self釋放的時(shí)候, 移除自己身上的Observer.

但如果Observer提前釋放了呢?

而添加關(guān)聯(lián)屬性, 兩者還不能同時(shí)持有臨時(shí)對(duì)象, 否則臨時(shí)對(duì)象也不會(huì)及時(shí)的釋放.

好吧, 既然一個(gè)不行, 那就各自關(guān)聯(lián)一個(gè):

- (void)addObserver {
 .....  
 SJObserverHelper *helper_obj = [SJObserverHelper new];
 SJObserverHelper *sub_obj = [SJObserverHelper new];
 sub_obj.target = helper_obj.target = _xiaoM;
 sub_obj.observer = helper_obj.observer = _observer;
 sub_obj.keyPath = helper_obj.keyPath = keyPath;
 const char *helpeKey = [NSString stringWithFormat:@"%zd", [_observer hash]].UTF8String;
 // 關(guān)聯(lián)
 objc_setAssociatedObject(_xiaoM, helpeKey, helper_obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
 // 關(guān)聯(lián)
 objc_setAssociatedObject(_observer, helpeKey, sub_obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

如上, 仔細(xì)想想, 存在一個(gè)很明顯的問題, 兩個(gè)關(guān)聯(lián)屬性釋放的同時(shí), 進(jìn)行了兩次觀察移除的操作. 為避免這個(gè)問題, 我又做了如下修改:

@interface SJObserverHelper : NSObject
@property (nonatomic, unsafe_unretained) id target;
@property (nonatomic, unsafe_unretained) id observer;
@property (nonatomic, strong) NSString *keyPath;
@property (nonatomic, weak) SJObserverHelper *factor; // 1. 新增一個(gè) weak 變量
@end

@implementation SJObserverHelper
- (void)dealloc {
 if ( _factor ) {
  [_target removeObserver:_observer forKeyPath:_keyPath];
 }
}
@end

- (void)addObserver {
 ..... 
 SJObserverHelper *helper_obj = [SJObserverHelper new];
 SJObserverHelper *sub_obj = [SJObserverHelper new];
 sub_obj.target = helper_obj.target = _xiaoM;
 sub_obj.observer = helper_obj.observer = _observer;
 sub_obj.keyPath = helper_obj.keyPath = keyPath;
 // 2. 互相 weak 引用
 helper_obj.factor = sub_obj; 
 sub_obj.factor = helper_obj;
 const char *helpeKey = [NSString stringWithFormat:@"%zd", [_observer hash]].UTF8String;
 // 關(guān)聯(lián)
 objc_setAssociatedObject(_xiaoM, helpeKey, helper_obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
 // 關(guān)聯(lián)
 objc_setAssociatedObject(_observer, helpeKey, sub_obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

在之前的操作中, 我們知道, weak 修飾的變量, 在目標(biāo)釋放時(shí),持有者的實(shí)例變量都會(huì)自動(dòng)置為nil, 因此如上dealloc方法中, 我們只需要判斷weak引用的實(shí)例變量factor是否為空即可.

抽取

以上操作, 我們就可以解決偶爾忘記寫移除Observer的代碼了. 現(xiàn)在只需要把實(shí)現(xiàn)抽取出來, 做成一個(gè)通用的工具方法:

我新建了一個(gè)NSObject的Category, 并添加了一個(gè)方法, 如下:

iOS自動(dòng)移除KVO觀察者的實(shí)現(xiàn)方法

然后將上述的實(shí)現(xiàn)進(jìn)行了整合放到了.m中:

iOS自動(dòng)移除KVO觀察者的實(shí)現(xiàn)方法

到此, 以后只需要調(diào)用- (void)sj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;這個(gè)方法即可, 移除就交給臨時(shí)變量自己搞定.

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。

分享名稱:iOS自動(dòng)移除KVO觀察者的實(shí)現(xiàn)方法
網(wǎng)站路徑:http://jinyejixie.com/article12/gggogc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、網(wǎng)頁(yè)設(shè)計(jì)公司、網(wǎng)站策劃、搜索引擎優(yōu)化、商城網(wǎng)站、品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)
新郑市| 民丰县| 江达县| 铜山县| 田林县| 湄潭县| 西乌珠穆沁旗| 安陆市| 定结县| 张家口市| 北安市| 江川县| 郑州市| 巴楚县| 荥经县| 东丰县| 黔南| 旺苍县| 文昌市| 封丘县| 洪江市| 搜索| 高州市| 托克托县| 景宁| 漠河县| 娄底市| 双流县| 阿克| 健康| 丰宁| 阜宁县| 广南县| 马公市| 武威市| 昭苏县| 呼和浩特市| 镇康县| 南昌县| 通州市| 两当县|