Key-Value Observing Programming Guide

官方文檔點這里:
Key-Value Observing Programming GuideNSKey?Value?Observing
Key-Value Coding Programming Guide迄埃、NSKey?Value?Coding


Introduction


Introduction to KVO Programming Guide


Key-value observing 是一個機制尿招,允許在別的對象屬性發(fā)生變化時,收到通知伏穆。

為了理解 kvo,要先了解 kvc纷纫。


At a Glance

kvo 對 Model 層和 controller 層之間的通信有重要作用枕扫。控制器通過 kvo 觀察模型對象辱魁,視圖層也通過控制器來觀察模型對象烟瞧。另外,模型對象也可能觀察模型對象染簇。

kvo 可以觀察任意的對象屬性参滴,包括 simple attributes, to-one relationships, 和 to-many relationships。attributes 可以理解為標量锻弓,比如 int砾赔,或者 NSNumber 對象。to-one relationships 可以理解為一個對象青灼,比如一個學生類對象暴心。to-many relationships 可以理解為集合對象,比如 NSArray 對象杂拨。觀察 to-many relationships 的觀察者可以被通知變化的類型和發(fā)生變化的對象专普。

要使用 kvo,首先要確認被觀察的對象實現(xiàn)了 kvo扳躬。NSObject 和子類實現(xiàn)了 kvo脆诉。也可以手動實現(xiàn) kvo,具體看 KVO Compliance 部分贷币。

其次,被觀察者調(diào)用 addObserver:forKeyPath:options:context: 方法注冊和添加觀察者亏狰。當觀察者收到通知的時候役纹,會調(diào)用 observeValueForKeyPath:ofObject:change:context: 函數(shù)進行處理。

最后暇唾,觀察者不想接收通知了促脉,在被釋放內(nèi)存之前,要調(diào)用 removeObserver:forKeyPath: 方法策州,移除觀察者瘸味。


Registering for Key-Value Observing



使用 kvo 的3個步驟:

  • 被觀察者調(diào)用 addObserver:forKeyPath:options:context: 方法注冊和添加觀察者。
  • 觀察者實現(xiàn) observeValueForKeyPath:ofObject:change:context: 方法够挂,收到通知的時候會調(diào)用旁仿。
  • 不再接收通知,要在釋放內(nèi)存之前孽糖,調(diào)用 removeObserver:forKeyPath: 方法移除觀察者枯冈。


Registering as an Observer

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


// demo 
// 假設B有個數(shù)組屬性
@property (nonatomic, strong) NSMutableArray *mArray;
// 注冊觀察者A毅贮。注意,如果 keyPath 是".mArray"會拋出字符串 NSRangeException 異常尘奏。
[B addObserver:A forKeyPath:@"mArray" options: NSKeyValueObservingOptionNew context: NULL];
  • 注冊觀察者滩褥,接收 kvo 通知。
  • observer炫加,觀察者瑰煎,必須實現(xiàn) *observeValueForKeyPath: ofObject: change: context: *方法,收到通知時會調(diào)用俗孝。
  • key?Path酒甸,發(fā)生變化的屬性。不能為 nil驹针。
  • options烘挫,定義 kvo 通知要傳遞的內(nèi)容,有 NSKeyValueObservingOptionNew柬甥、Old饮六、Initial、Prior苛蒲。比如 New 意味著通知的 change 字典包含變化之后的值卤橄,具體看 NSKey?Value?Observing?Options 部分。
  • context臂外,隨意的數(shù)據(jù)窟扑,傳給觀察者的 observeValueForKeyPath: ofObject: change: context: 方法。
  • 不會 retain 觀察者和被觀察者(Neither the object receiving this message, nor observer, are retained.)漏健。記得調(diào)用 remove Observer: for Key Path: 或 remove Observer: for Key Path: context: 移除觀察者嚎货。


Options

影響通知提供給觀察者的 change dictionary 中的內(nèi)容,以及通知的方式蔫浆。

使用 NSKeyValueObservingOptionOld 可以獲得舊值殖属,NSKeyValueObservingOptionNew 可以獲得新值⊥呤ⅲ可以兩個同時使用洗显。

使用 NSKeyValueObservingOptionInitial 可以獲得初始值,被觀察者會立即發(fā)送一個通知(在 addObserver:forKeyPath:options:context: 返回之前)原环。NSKeyValueObservingOptionInitial 不單獨使用挠唆,也不和 OptionOld 一起使用,而是和 OptionNew 一起使用嘱吗,因為對于觀察者來說玄组,第一次通知時,屬性的值就是新的。

使用 NSKeyValueObservingOptionInitial 代碼如下所示:

// 注冊觀察者時巧勤,創(chuàng)建 option
NSKeyValueObservingOptions option = NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew;

// 觀察者處理通知
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    // 輸出屬性初始值
    NSLog(@"new = %@", change[NSKeyValueChangeNewKey]);
    // 輸出 null
    NSLog(@"old = %@", change[NSKeyValueChangeOldKey]);
}

使用 NSKeyValueObservingOptionPrior 可以在值改變之前和改變之后收到通知嵌灰。官方文檔:“You instruct the observed object to send a notification just prior to a property change (除此之外,正常的通知在值改變之后發(fā)送颅悉,in addition to the usual notification just after the change) by including the option NSKeyValueObservingOptionPrior”沽瞭。值改變之前的通知的 change 字典里面有一個 NSKeyValueChangeNotificationIsPriorKey,對應的值是用 @(YES) 裝箱的 NSNumber 對象剩瓶。利用提前發(fā)送的通知驹溃,可以在手動實現(xiàn) kvo 的時候,有機會調(diào)用 - willChangeValueForKey: 方法延曙。

使用 NSKeyValueObservingOptionPrior 代碼的斷點調(diào)試:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    // 此處有斷點豌鹤。會執(zhí)行兩次。
    // NSLog(@"%@", change[NSKeyValueChangeNotificationIsPriorKey]); // 輸出 1

}

值改變之前的輸出結果:
(lldb) po change
{
    kind = 1;
    notificationIsPrior = 1;
}

值改變之后的輸出結果:
(lldb) po change
{
    kind = 1;
}


Context

用于觀察者判斷通知來源枝缔〔几恚可以傳 NULL,只根據(jù) key path 來判斷愿卸。但是這樣在某些情況會引起問題灵临,比如對同一個 key path 注冊多次觀察者,或者父類也對這個 key path 注冊了觀察者趴荸∪甯龋可以根據(jù) context 來判斷是否要調(diào)用 super 的方法來處理通知。子類在移除觀察者的時候也可以根據(jù) context 來選定要移除的是自己而不是父類发钝。

獨一無二的靜態(tài)變量的地址可以作為不錯的 context顿涣。可以為一個類定義一個 context酝豪,也可以為每個屬性都定義一個 context涛碑,這樣可以免去對 key path 的比較判斷,提高效率孵淘。

創(chuàng)建 context 的例子:

Listing 1  Creating context pointers

static void *PersonAccountBalanceContext = &PersonAccountBalanceContext;
static void *PersonAccountInterestRateContext = &PersonAccountInterestRateContext;

Person锌唾、Account 是類名,balance夺英、interestRate 是屬性。void *是一個指針滋捶,變量創(chuàng)建的時候就會分配一塊內(nèi)存痛悯,用&取地址后賦值給指針變量就行,不用管內(nèi)存里保存的值是多少重窟。

Listing 2 顯示使用 context 注冊觀察者的例子:

Listing 2  Registering the inspector as an observer of the balance and interestRate properties

- (void)registerAsObserverForAccount:(Account*)account {
    [account addObserver:self
              forKeyPath:@"balance"
                 options:(NSKeyValueObservingOptionNew |
                          NSKeyValueObservingOptionOld)
                 context:PersonAccountBalanceContext];
 
    [account addObserver:self
              forKeyPath:@"interestRate"
                 options:(NSKeyValueObservingOptionNew |
                          NSKeyValueObservingOptionOld)
                  context:PersonAccountInterestRateContext];
}

使用 self 作為 context 可以嗎载萌?試了兩種情況,第一種失敗了:

void *context = &self; // 編譯器會警告
if (context == &self) {
  // 不會進來,因為比較結果是 nil扭仁。
}

第二種是成功的垮衷,使用橋接:

// 定義 context
void *context = (__bridge void *)(self);

// 處理通知
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (context == (__bridge void *)(self)) {
        NSLog(@"use __bridge void *self");
    }
}

注意
addObserver:forKeyPath:options:context: 方法不會 maintain 觀察者、被觀察者和 context乖坠。


Receiving Notification of a Change

發(fā)生變化時搀突,觀察者會收到 observeValueForKeyPath:ofObject:change:context: 消息,所有的觀察者都要實現(xiàn)這個函數(shù)熊泵。

change 字典包含了變化的信息仰迁,context 是注冊觀察者的時候提供的。

change 字典可能包含的 key 有:NSKeyValueChangeKindKey顽分、NSKeyValueChangeNewKey徐许、NSKeyValueChangeOldKey、NSKeyValueChangeIndexesKey卒蘸、NSKey?Value?Change?Notification?Is?Prior?Key雌隅,分別對應變化類型、變化后的值缸沃、變化前的值恰起、發(fā)生變化的元素下標、是否變化前后都發(fā)送通知和泌。

NSKeyValueChangeKindKey 肯定是有的村缸,表示變化類型,對應的值可以是 NSKeyValueChangeSetting武氓、NSKeyValueChangeInsertion梯皿、NSKeyValueChangeRemoval、NSKeyValueChangeReplacement县恕。

NSKeyValueChangeNewKey东羹、NSKeyValueChangeOldKey、NSKey?Value?Change?Notification?Is?Prior?Key忠烛,由注冊觀察者時使用的 NSKeyValueObservingOptions 決定属提。NSKeyValueObservingOptions 的值可以是 NSKeyValueObservingOptionOld、NSKeyValueObservingOptionNew美尸、NSKey?Value?Observing?Option?Initial冤议、NSKey?Value?Observing?Option?Prior。注冊觀察者時师坎,NSKeyValueObservingOptionOld 對應 change 字典的 NSKeyValueChangeOldKey恕酸,NSKeyValueObservingOptionNew 對應 NSKeyValueChangeNewKey,NSKey?Value?Observing?Option?Prior 對應 NSKey?Value?Change?Notification?Is?Prior?Key胯陋。

NSKeyValueChangeIndexesKey 在被觀察的是集合對象(比如數(shù)組)時蕊温,字典會包含有袱箱,表示發(fā)生變化的元素下標。注意是數(shù)組的元素發(fā)生變化义矛,而不是數(shù)組的元素的屬性發(fā)生變化发笔。比如數(shù)組里放的是 UIView 對象 view,是 view 被刪除或者替換掉凉翻,而不是 view 的背景色改變顏色了讨。

使用 NSKeyValueChangeIndexesKey 的示例代碼:

// 初始化數(shù)組
self.mArray = [NSMutableArray new];
[self.mArray addObject:[UIView new]];

// 注冊觀察者
[self addObserver:self forKeyPath:@"mArray" options: option context:contex];

// 修改數(shù)組。這里會引發(fā)通知噪矛,變化類型是 NSKeyValueChangeReplacement量蕊,
// change 字典里面有 NSKeyValueChangeIndexesKey 及其對應的 NSIndexSet 對象。
NSMutableArray * mArray = [self mutableArrayValueForKeyPath:@"mArray"];
mArray[0] = @"99"; 

// 在處理通知的函數(shù)里面斷點調(diào)試艇挨,獲取 NSIndexSet 對象残炮,用 po 輸出 change 字典
NSIndexSet *indexSet = change[NSKeyValueChangeIndexesKey];
(lldb) po change
{
    indexes = "<_NSCachedIndexSet: 0x60800003b360>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
    kind = 4;
    new =     (
        99
    );
}

// 注意,如果是這樣修改數(shù)組缩滨,不會引發(fā)通知势就。
self.mArray[0] = @"99";

// 如果是這樣修改,會引發(fā)通知脉漏,類型是 NSKeyValueChangeSetting
self.mArray = nil;
// 斷點輸出
(lldb) po change
{
    kind = 1;
    new = "<null>";
}

change 字典中的 NSKeyValueChangeKindKey 提供了變化類型苞冯。如果是屬性的值發(fā)生變化,NSKeyValueChangeKindKey 對應的值就是 NSKeyValueChangeSetting侧巨,那么字典中可能會有 NSKeyValueChangeOldKey舅锄、NSKeyValueChangeNewKey,兩個key 分別對應屬性變化前和變化后的值司忱。字典是否包含這兩個 key皇忿,由注冊觀察者時的 NSKeyValueObservingOptions 決定,比如 NSKeyValueObservingOptionOld坦仍、NSKeyValueObservingOptionNew鳍烁。

如果觀察的屬性是一個 to-many relationship(比如數(shù)組),當它的元素發(fā)生變化時繁扎,NSKeyValueChangeKindKey 對應的值可能是 NSKeyValueChangeInsertion幔荒、NSKeyValueChangeRemoval 或者 NSKeyValueChangeReplacement,分別是插入梳玫、刪除爹梁、替換的意思。

字典的 NSKeyValueChangeIndexesKey 是一個 NSIndexSet 對象提澎,表示發(fā)生變化的元素的位置卫键。如果注冊觀察者的時候,使用了 NSKeyValueObservingOptionNew 或者 NSKeyValueObservingOptionOld虱朵,那么字典的 NSKeyValueChangeOldKey 或者 NSKeyValueChangeNewKey 對應一個數(shù)組莉炉,包含發(fā)生變化前或者變化后的相關元素。

Listing 3 顯示了處理通知的例子碴犬,一個 Person 類對象觀察 Account 類對象的 balance絮宁、interestRate 屬性:

Listing 3  Implementation of observeValueForKeyPath:ofObject:change:context:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
 
    if (context == PersonAccountBalanceContext) {
        // Do something with the balance…
 
    } else if (context == PersonAccountInterestRateContext) {
        // Do something with the interest rate…
 
    } else {
        // Any unrecognized context must belong to super
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                               context:context];
    }
}

上面的例子可以看到 context 的用處,不用比較字符串服协,而且可以區(qū)分子類和父類绍昂。

注意
如果通知傳遞到了類層的頂端,NSObject 會拋出一個 NSInternalInconsistencyException 異常偿荷,因為子類注冊觀察者收到的通知窘游,沒有進行處理。因為不是父類觀察的跳纳,所以肯定也不會處理忍饰。


Removing an Object as an Observer

Listing 4 演示移除觀察者:

Listing 4  Removing the inspector as an observer of balance and interestRate

- (void)unregisterAsObserverForAccount:(Account*)account {
    [account removeObserver:self
                 forKeyPath:@"balance"
                    context:PersonAccountBalanceContext];
 
    [account removeObserver:self
                 forKeyPath:@"interestRate"
                    context:PersonAccountInterestRateContext];
}

移除觀察者的注意事項:

  • 移除非觀察者會引發(fā)異常。如果不確定移除次數(shù)寺庄,可以放在 try/catch 塊處理艾蓝。
  • 觀察者在內(nèi)存中釋放時,不會自動移除斗塘,被觀察者會繼續(xù)發(fā)送通知赢织。和其他消息一樣,發(fā)送給釋放掉的對象馍盟,會引發(fā)內(nèi)存訪問異常于置。
  • 一般是在 init 或者 viewDidLoad 注冊觀察者,在 dealloc 移除贞岭。


KVO Compliance


kvo 有兩種方式發(fā)送通知:自動和手動八毯。子類通過重寫 automaticallyNotifiesObserversForKey: 方法,控制自動發(fā)送通知曹步。


Automatic Change Notification

Listing 1 顯示自動通知:

Listing 1  Examples of method calls that cause KVO change notifications to be emitted

// Call the accessor method.
[account setName:@"Savings"];
 
// Use setValue:forKey:.
[account setValue:@"Savings" forKey:@"name"];
 
// Use a key path, where 'account' is a kvc-compliant property of 'document'.
[document setValue:@"Savings" forKeyPath:@"account.name"];
 
// Use mutableArrayValueForKey: to retrieve a relationship proxy object.
Transaction *newTransaction = <#Create a new transaction for the account#>; // 偽代碼
NSMutableArray *transactions = [account mutableArrayValueForKey:@"transactions"];
[transactions addObject:newTransaction];


Manual Change Notification

有些情況需要手動通知宪彩,比如盡量減少不必要的通知數(shù)量,或者把一些類型的變化合并到一個通知來發(fā)送讲婚。

手動和自動通知不是互斥的尿孔。如果想關閉自動通知,可以重寫 automaticallyNotifiesObserversForKey: 方法筹麸,對某些 key 返回 NO活合。對于子類不處理的 key,要調(diào)用 super 來處理物赶。

Listing 2 顯示重寫的例子:

Listing 2  Example implementation of automaticallyNotifiesObserversForKey:

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
 
    BOOL automatic = NO;
    if ([theKey isEqualToString:@"balance"]) {
        automatic = NO;
    }
    else {
        automatic = [super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}

要實現(xiàn)手動通知白指,在變化發(fā)生前,調(diào)用 willChangeValueForKey:酵紫,變化發(fā)生后調(diào)用 didChangeValueForKey:告嘲。

Listing 3 實現(xiàn)手動通知:

Listing 3  Example accessor method implementing manual notification

- (void)setBalance:(double)theBalance {
    [self willChangeValueForKey:@"balance"];
    _balance = theBalance;
    [self didChangeValueForKey:@"balance"];
}

可以通過檢查值是否變化來減少通知的發(fā)送错维,如 Listing 4:

Listing 4  Testing the value for change before providing notification

- (void)setBalance:(double)theBalance {
    if (theBalance != _balance) {
        [self willChangeValueForKey:@"balance"];
        _balance = theBalance;
        [self didChangeValueForKey:@"balance"];
    }
}

多個值變化還可以嵌套發(fā)送通知,如 Listing 5:

Listing 5  Nesting change notifications for multiple keys

- (void)setBalance:(double)theBalance {
    [self willChangeValueForKey:@"balance"];
    [self willChangeValueForKey:@"itemChanged"];
    _balance = theBalance;
    _itemChanged = _itemChanged+1;
    [self didChangeValueForKey:@"itemChanged"];
    [self didChangeValueForKey:@"balance"];
}

對于 ordered to-many relationship(比如數(shù)組)橄唬,不僅要指定 key赋焕,還要指定變化的類型,和發(fā)生變化的元素下標仰楚。變化類型是 NSKeyValueChange 定義的 NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, 或者 NSKeyValueChangeReplacement隆判。下標是 NSIndexSet 對象。如 Listing 6 所示:

Listing 6  Implementation of manual observer notification in a to-many relationship

- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeRemoval
        valuesAtIndexes:indexes forKey:@"transactions"];
 
    // Remove the transaction objects at the specified indexes.
 
    [self didChange:NSKeyValueChangeRemoval
        valuesAtIndexes:indexes forKey:@"transactions"];
}


Registering Dependent Keys


有些屬性是相互依賴的僧界,一個變化會引起其他屬性跟著變化侨嘀。通過重寫 keyPathsForValuesAffectingValueForKey: 方法定義依賴屬性。


To-One Relationships

對于 to-one relationship捂襟,兩種方法實現(xiàn)屬性依賴咬腕,一種是重寫 keyPathsForValuesAffectingValueForKey: 方法,另一種是實現(xiàn)類似 keyPathsForValuesAffecting<Key> 的方法(比如 keyPathsForValuesAffectingAge)笆豁。

比如 full name 由 first 和 last name 決定:

- (NSString *)fullName {
    return [NSString stringWithFormat:@"%@ %@",firstName, lastName];
}

fullName 的觀察者應該在 firstName 或者 lastName 發(fā)生變化時收到通知郎汪。

Listing 1 顯示重寫 keyPathsForValuesAffectingValueForKey: 方法的例子:

Listing 1  Example implementation of keyPathsForValuesAffectingValueForKey:

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
 
    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
 
    if ([key isEqualToString:@"fullName"]) {
        NSArray *affectingKeys = @[@"lastName", @"firstName"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}

重寫要先調(diào)用父類的方法,然后把依賴的屬性名放入一個數(shù)組闯狱,再添加到父類方法返回的 set 對象煞赢。

另一個方法是實現(xiàn) keyPathsForValuesAffecting<Key> 方法,如 Listing 2 所示:

Listing 2  Example implementation of the keyPathsForValuesAffecting<Key> naming convention

+ (NSSet *)keyPathsForValuesAffectingFullName {
    return [NSSet setWithObjects:@"lastName", @"firstName", nil];
}

不能在 category 中重寫 keyPathsForValuesAffectingValueForKey: 方法哄孤,因為會覆蓋系統(tǒng)方法照筑。這種情況可以在 category 添加 keyPathsForValuesAffecting<Key> 方法實現(xiàn)屬性依賴。


To-Many Relationships

對于 to-many relationships瘦陈, 不能通過重寫 keyPathsForValuesAffectingValueForKey: 方法實現(xiàn)屬性依賴凝危。

比如 Department 對象有個 to-many relationship (employees),存放 Employee 對象晨逝。Department 有 totalSalary 屬性蛾默,依賴于 Employee 的 salary 屬性。不能重寫 keyPathsForValuesAffectingValueForKey: 方法返回 employees.salary 作為依賴屬性的 keyPath捉貌。

兩種方法支鸡,一種是使用 kvo 注冊 parent (in this example, Department) 成為所有 children (Employees in this example) 的相關屬性(這里指 salary)的觀察者。在添加和刪除 child 的時候趁窃, parent 要注冊或者注銷觀察者牧挣。然后某個 child 的 salary 屬性發(fā)生變化的時候,parent 在 observeValueForKeyPath:ofObject:change:context: 方法中更新自己的 totalSalary 屬性并通知自己的觀察者醒陆。如 Listing 3 所示:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
 
    if (context == totalSalaryContext) {
        [self updateTotalSalary];
    }
    else
    // deal with other observations and/or invoke super...
}
 
- (void)updateTotalSalary {
    // 這里是使用 kvc 的操作符瀑构,對數(shù)組 employees 的元素求和
    [self setTotalSalary:[self valueForKeyPath:@"employees.@sum.salary"]];
}
 
- (void)setTotalSalary:(NSNumber *)newTotalSalary {
 
    if (totalSalary != newTotalSalary) {
        [self willChangeValueForKey:@"totalSalary"];
        _totalSalary = newTotalSalary;
        [self didChangeValueForKey:@"totalSalary"];
    }
}
 
- (NSNumber *)totalSalary {
    return _totalSalary;
}

另一種方法和 Core Data 相關,If you're using Core Data, you can register the parent with the application's notification center as an observer of its managed object context. The parent should respond to relevant change notifications posted by the children in a manner similar to that for key-value observing刨摩。


Key-Value Observing Implementation Details


Automatic key-value observing is implemented using a technique called isa-swizzling.

自動 kvo 是通過 isa-swizzling 技術實現(xiàn)的寺晌。

The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.

isa 指針指向對象的 class世吨,class 維護一張分發(fā)表,分發(fā)表包含了 class 實現(xiàn)的方法的方法指針和其他數(shù)據(jù)折剃。

When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.

注冊觀察者的時候另假,被觀察者的 isa 指針被修改了,指向一個中間類怕犁,結果就是 isa 指針不一定能反映對象真實的 class。

You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.

不要用 isa 指針來判斷對象所屬的 class己莺,應該使用 class 方法 來判斷奏甫。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市凌受,隨后出現(xiàn)的幾起案子阵子,更是在濱河造成了極大的恐慌,老刑警劉巖胜蛉,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挠进,死亡現(xiàn)場離奇詭異,居然都是意外死亡誊册,警方通過查閱死者的電腦和手機领突,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來案怯,“玉大人君旦,你說我怎么就攤上這事〕凹睿” “怎么了金砍?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長麦锯。 經(jīng)常有香客問我恕稠,道長,這世上最難降的妖魔是什么扶欣? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任鹅巍,我火速辦了婚禮,結果婚禮上宵蛀,老公的妹妹穿的比我還像新娘昆著。我一直安慰自己,他們只是感情好术陶,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布凑懂。 她就那樣靜靜地躺著,像睡著了一般梧宫。 火紅的嫁衣襯著肌膚如雪接谨。 梳的紋絲不亂的頭發(fā)上摆碉,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音脓豪,去河邊找鬼巷帝。 笑死,一個胖子當著我的面吹牛扫夜,可吹牛的內(nèi)容都是我干的楞泼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼笤闯,長吁一口氣:“原來是場噩夢啊……” “哼堕阔!你這毒婦竟也來了?” 一聲冷哼從身側響起颗味,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤超陆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后浦马,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體时呀,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年晶默,在試婚紗的時候發(fā)現(xiàn)自己被綠了谨娜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡荤胁,死狀恐怖瞧预,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仅政,我是刑警寧澤垢油,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站圆丹,受9級特大地震影響滩愁,放射性物質發(fā)生泄漏。R本人自食惡果不足惜辫封,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一硝枉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧倦微,春花似錦妻味、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春雏逾,著一層夾襖步出監(jiān)牢的瞬間嘉裤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工栖博, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留屑宠,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓仇让,卻偏偏與公主長得像典奉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丧叽,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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