2016-03-17 62 views
1

在Objective-C中,使用鍵值觀察時,我有一個包含accountDomestic屬性和person屬性的Bank類。該人被添加到觀察賬戶國內財產。我在Bank類中有一個static void *bankContext = & bankContext作爲上下文。但是,在更改accountDomestic屬性後,由於上下文和bankContext在Person中的-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context方法不匹配,未能正確顯示舊值和新值。關鍵值觀察上下文不起作用

代碼如下,首先是銀行類:

Bank.h 

#import <Foundation/Foundation.h> 
#import "Person.h" 

static void * const bankContext = &bankContext; 
@class Person; 

@interface Bank : NSObject 
@property (nonatomic, strong) NSNumber* accountDomestic; 
@property (nonatomic, strong) Person* person; 
-(instancetype)initWithPerson:(Person *)person; 
@end 



Bank.m 

@implementation 
-(instancetype)initWithPerson:(Person *)person{ 
    if(self = [super init]){ 
     _person = person; 
     [self addObserver:_person 
       forKeyPath:NSStringFromSelector(@selector(accountDomestic)) 
        options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 
        context:bankContext]; 
} 
    return self; 
} 

-(void)dealloc{ 
    [self removeObserver:_person forKeyPath:NSStringFromSelector(@selector(accountDomestic))]; 
} 

@end 

則是Person類:

Person.h 

#import <Foundation/Foundation.h> 
#import "Bank.h" 
@interface Person : NSObject 
@end 


Person.m 

#import "Person.h" 
@implementation Person 

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{ 
    NSLog(@"context: %p",context); 
    NSLog(@"bankContext: %p",bankContext); 
    if(context == bankContext){ 
     if([keyPath isEqualToString:NSStringFromSelector(@selector(accountDomestic))]){ 
      NSString *oldValue = change[NSKeyValueChangeOldKey]; 
      NSString *newValue = change[NSKeyValueChangeNewKey]; 
      NSLog(@"--------------------------"); 
      NSLog(@"accountDomestic old value: %@", oldValue); 
      NSLog(@"accountDomestic new value: %@", newValue); 
     } 
    } 
} 

@end 

最後是視圖控制器類

ViewController.h 

#import <UIKit/UIKit.h> 
@interface ViewController : UIViewController 
@end 


ViewController.m 

#import "ViewController.h" 
#import "Bank.h" 
#import "Person.h" 
@interface ViewController() 
@property (nonatomic, strong) Bank *bank; 
@property (nonatomic, strong) Person *person; 
@property (nonatomic, strong) NSNumber *delta; 
@end 

@implementation ViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.person = [[Person alloc] init]; 
    self.delta = @10; 
    self.bank = [[Bank alloc] initWithPerson:self.person]; 
} 

- (IBAction)accountDomesticIncreaseButtonDidTouch:(id)sender { 
    self.bank.accountDomestic = self.delta; 
    int temp = [self.delta intValue]; 
    temp += 10; 
    self.delta = [NSNumber numberWithInt:temp]; 
} 
@end 

後我點擊按鈕,accountDomestic的新舊值不顯示。你可以看到背景和bankContext值不相等,如下圖所示:

enter image description here

有沒有人有任何想法?

+0

如果你使用靜態,那麼這個類如果有效地簡化爲單例。你爲什麼要這樣限制班級? – Droppy

+0

這是建議在這篇文章http://nshipster.com/key-value-observing/。作爲一個令牌。如果我不關心不同的事例,只要是同一個班級,我認爲這很好。問題的關鍵在於爲什麼這個概念和bankContext不相等。 – Johnson

+0

好的 - 我的錯。我把它拿回來:) – Droppy

回答

2

原因是有兩個bankContext s。在Bank.h,你必須然後

static void * const bankContext = &bankContext;

此文件是包含在兩個Bank.mPerson.m,所以這兩個文件(編譯單元)定義指針bankContext,其被標記爲static所以不產生外部鏈接(這樣就可以有兩個同名)。

最直接的解決方案是確保只有一個bankContext。在Bank.h

extern void * const bankContext;

Bank.m

void * const bankContext = &bankContext;

More about static and extern.

這麼說,我認爲這將是更好的重組代碼,所以這是沒有必要的。你的職責設置讓我保持警惕(一個對象告訴另一個成爲觀察者),我感到驚訝,它編譯正確,因爲似乎有循環進口(Bank.hPerson.h導入對方)。

+0

你說的是對的!使用bankContext =&bankContext的目的是作爲某個類的標記(http://nshipster.com/key-value-observing/)。爲此,我認爲如果沒有額外的bankContext變量聲明,if(object isKindOfClass:[Bank class]])比if([context == bankContext)'更好。 – Johnson

+0

我建議您繼續使用NSHipster文章中建議的上下文指針。您需要了解更多關於C中的變量存儲的信息,才能將其適用於您的情況。 –

+0

感謝您的建議,我已經看到了靜態和外部的區別。沒有必要將上下文指針作爲標識來標識情況嗎?由於我的知識,我沒有看到其他優勢。 – Johnson