KVO

KVO提供了一種允許一個(gè)對(duì)象觀察另一個(gè)對(duì)象的屬性變化的機(jī)制,常用于model層和controller層之間的通信。
觀察者類型:
1.controller觀察model中的屬性
2.model觀察另一個(gè)model中的屬性
3.model觀察自身的屬性

可觀察的屬性類型:
1.簡(jiǎn)單的屬性
2.to-one relationships
3.to-many relationships

KVO實(shí)現(xiàn)原理

KVO通過(guò)isa-swizzling技術(shù)實(shí)現(xiàn)

當(dāng)某個(gè)類的對(duì)象第一次被觀察時(shí),系統(tǒng)會(huì)在運(yùn)行期動(dòng)態(tài)地創(chuàng)建該類的一個(gè)派生類,并將被觀察對(duì)象的isa指向這個(gè)派生類。
如果觀察者注冊(cè)了當(dāng)前對(duì)象的某個(gè)屬性的觀察,派生類會(huì)重寫該屬性的setter方法。
派生類在重寫的setter方法中實(shí)現(xiàn)真正的通知機(jī)制(我們也可以實(shí)現(xiàn)手動(dòng)通知)。
如果不使用setter方法改變屬性值,而是直接修改屬性對(duì)應(yīng)的成員變量,則不會(huì)觸發(fā)通知。

KVC(Key-Value Coding)

KVC定義了一種按名稱訪問(wèn)對(duì)象屬性的機(jī)制,支持這種訪問(wèn)的主要方法有

 1. - (id)valueForKey:(NSString *)key;  
 2. - (void)setValue:(id)value forKey:(NSString *)key;  
 3. - (id)valueForKeyPath:(NSString *)keyPath;  
 4. - (void)setValue:(id)value forKeyPath:(NSString *)keyPath; 

key對(duì)應(yīng)屬性名稱字符串,keyPath是一個(gè)被點(diǎn)操作符隔開(kāi)的字符串,用于訪問(wèn)對(duì)象屬性的屬性,如address.street將會(huì)訪問(wèn)消息接收對(duì)象的address屬性所包含的street屬性。(address和street不一定是property,但必須具有訪問(wèn)器方法)

KVC使用的一個(gè)舉例

@interface DetailViewController ()

@property (weak, nonatomic) IBOutlet UITextField *nameField;
@property (weak, nonatomic) IBOutlet UITextField *nicknameField;
@property (weak, nonatomic) IBOutlet UITextField *emailField;
@property (weak, nonatomic) IBOutlet UITextField *cityField;

@end

@implementation DetailViewController
- (NSArray *)contactStringKeys;
{
    return @[@"name", @"nickname", @"email", @"city"];
}

- (UITextField *)textFieldForModelKey:(NSString *)key;
{
    return [self valueForKey:[key stringByAppendingString:@"Field"]];
}
@end

textFieldForModelKey方法根據(jù)key值返回DetailViewController的UITextField屬性。

Key-Value Coding Accessor Method

通過(guò)Accessor來(lái)實(shí)現(xiàn)KVC
常用的訪問(wèn)器方法

-<key> //用于返回一個(gè)object/變量/struct
-is<Key>  //用于返回布爾值
-set<key>

KVC按順序使用如下技術(shù):
1.檢查是否存在-<key>/-is<Key>/-get<key>的訪問(wèn)器方法,如果有則用這些方法返回值;檢查是否存在
-set<key>:,如果有則使用它設(shè)置值;(-is<Key>/-get<key>/-set<key>方法將大些字符串的第一個(gè)字母)
2.如果上述方法不可用,則檢查名為-_<key>、-_is<key>(只針對(duì)布爾值有效)、-_get<key>和-set<key>:方法
3.如果沒(méi)有找到訪問(wèn)器方法,可以嘗試直接訪問(wèn)實(shí)例變量。實(shí)例變量可以是名為:<key>或
<key>;
4.如果仍未找到,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法。這些方法的默認(rèn)實(shí)現(xiàn)都是拋出異常,我們可以根據(jù)需要重寫它們。

to-Many屬性的集合訪問(wèn)器

使用集合訪問(wèn)器有以下好處:
1.處理可變集合(NSMutableArray/NSMutableSet/NSMutableOrderedSet)時(shí)可以得到性能的提升
2.實(shí)現(xiàn)一些適當(dāng)?shù)姆椒ǎ梢灾С炙袑?duì)集合對(duì)象的調(diào)用,但是不需要真的實(shí)現(xiàn)一個(gè)集合對(duì)象
3.可以使用集合訪問(wèn)器來(lái)改變集合,并發(fā)送KVO通知

Getter Indexed Accessors 注解
-countOf<Key> 必須,相當(dāng)于NSArray的Count屬性
-objectIn<Key>AtIndex: or -<key>AtIndexs 二者必實(shí)現(xiàn)一個(gè),相當(dāng)于objectAtIndex:/objectsAtIndex方法
-get<key>:range: 可選,可以增強(qiáng)性能,相當(dāng)于NSArray的getObjects:range:方法
Mutable Indexed Accessors 注解
-insertObject:in<Key>AtIndex: or -insert<Key>:atIndexes: 2選1,相當(dāng)于NSMutableArray的insertObject:atIndex: 和insertObjects:atIndexes:
-removeObjectFrom<Key>AtIndex: or -remove<Key>AtIndexes: 2選1,相當(dāng)于NSMutableArray的removeObjectAtIndex:和removeObjectsAtIndexes:
-replaceObjectIn<Key>AtIndex:withObject: or -replace<Key>AtIndexes:with<Key>: 可選,增強(qiáng)性能

鍵值驗(yàn)證 Key-Value Validation

KVC提供了驗(yàn)證Key對(duì)應(yīng)的Value是否可用的方法

- (BOOL)validateValue:(inout id *)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

該方法默認(rèn)的實(shí)現(xiàn)是調(diào)用一個(gè)如下格式的方法

- (BOOL)validate<Key>:error:

KVO鍵值觀察

某個(gè)對(duì)象anObserver通過(guò)以下方法注冊(cè)為觀察者:

- (void)addObserver:(NSObject *)anObserver
         forKeyPath:(NSString *)keyPath
            options:(NSKeyValueObservingOptions)options
            context:(void *)context

在keyPath的值改變時(shí),觀察者的以下方法會(huì)被觸發(fā):

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context

調(diào)用以下方法來(lái)移除觀察著:

- (void)removeObserver:(NSObject *)anObserver
            forKeyPath:(NSString *)keyPath

注冊(cè)依賴鍵

Foundation框架提供的表示屬性依賴的機(jī)制如下:

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

具體實(shí)現(xiàn)如下

+ (NSSet *)keyPathsForValuesAffecting<Key>

舉一個(gè)例子

+ (NSSet *)keyPathsForValuesAffectingRedComponent
{
    return [NSSet setWithObject:@"lComponent"];
}

+ (NSSet *)keyPathsForValuesAffectingGreenComponent
{
    return [NSSet setWithObjects:@"lComponent", @"aComponent", nil];
}

+ (NSSet *)keyPathsForValuesAffectingBlueComponent
{
    return [NSSet setWithObjects:@"lComponent", @"bComponent", nil];
}

+ (NSSet *)keyPathsForValuesAffectingColor
{
    return [NSSet setWithObjects:@"redComponent", @"greenComponent", @"blueComponent", nil];
}

上述代碼建立了color、{redComponent,greenComponent,blueComponent}和{lComponent、aComponent、bComponent}之間的依賴關(guān)系;如果某個(gè)對(duì)象注冊(cè)為color屬性的觀察者,那么lComponent、aComponent、bComponent任一屬性發(fā)生改變,都會(huì)觸發(fā)觀察者的observeValueForKeyPath方法。
屬性依賴的注冊(cè)方法在程序啟動(dòng)時(shí)即執(zhí)行,后續(xù)運(yùn)行中不再執(zhí)行以上方法;且l,a,b任一項(xiàng)發(fā)生改變,僅計(jì)算依賴它的屬性,不相關(guān)的屬性不會(huì)再次計(jì)算。

手動(dòng)通知

KVO實(shí)現(xiàn)原理為runtime動(dòng)態(tài)地重寫被觀察屬性的setter方法,我們也可以自己重寫setter方法來(lái)達(dá)到手動(dòng)通知的目的:

//關(guān)閉自動(dòng)調(diào)用
+ (BOOL)automaticallyNotifiesObserversForLComponent;
{
    return NO;
}

- (void)setLComponent:(double)lComponent;
{
    if (_lComponent == lComponent) {
        return;
    }
    [self willChangeValueForKey:@"lComponent"];
    _lComponent = lComponent;
    [self didChangeValueForKey:@"lComponent"];
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容