2016-03-17 2 views
1

В Objective-C, при использовании Key-Value Observing, у меня есть класс Bank with accountDomestic property и свойство person. Лицо добавляется для наблюдения за счетом. У меня есть static void *bankContext = & bankContext в классе Bank в качестве его контекста. Однако после того, как я изменил свойство accountDomestic, старое и новое значение отображаются неправильно из-за несоответствия контекста и bankContext в методе -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context в Person.Ключевое значение Контекст наблюдения не работает

код выглядит следующим образом, первым является Банк Класс:

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.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 Класс

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

Ah OK - моя ошибка. Я забираю его обратно :) – Droppy

ответ

2

Причина в том, что есть два bankContext s. В Bank.h, у вас есть

static void * const bankContext = &bankContext;

Затем этот файл включается как Bank.m и Person.m, поэтому оба файла (единицы компиляции) определяют указатель bankContext, который помечен как static так не создает внешних связей (так что вы может иметь два одинаковых имени).

Наиболее прямым решением является обеспечение того, что существует только один bankContext. В Bank.h:

extern void * const bankContext;

и Bank.m:

void * const bankContext = &bankContext;

More about static and extern.

То есть, я думаю, было бы лучше перестроить код, так что это не нужно. Ваша установка обязанностей заставляет меня настороженно (один объект говорит другому, чтобы стать наблюдателем), и я удивлен, что он правильно компилируется, так как существует циклический импорт (Bank.h и Person.h импортировать друг друга).

+0

Что вы сказали правильно! Цель использования bankContext = & bankContext - это токен для определенного класса (http://nshipster.com/key-value-observing/). Для этой цели, я думаю, можно использовать 'if (object isKindOfClass: [Bank class]])' лучше, чем 'if ([context == bankContext)' без дополнительного объявления переменной bankContext. – Johnson

+0

Я рекомендую вам использовать указатель контекста, как это предлагается в статье NSHipster. Вам нужно больше узнать о хранилище переменных в C, чтобы правильно применить его к вашей ситуации. –

+0

Спасибо за ваше предложение, я видел разницу между static и extern. Разве не нужно объявлять указатель контекста, как токен, чтобы идентифицировать ситуацию? Из-за моих знаний я не вижу другого преимущества. – Johnson

Смежные вопросы