Reputation: 491
In Objective-C, when using Key-Value Observing, I have a Bank class with a accountDomestic property and a person property. The person is added to observe the accountDomestic property. I have a static void *bankContext = & bankContext
in Bank class as its context. However, after I change the accountDomestic property, the old and new value are not shown correctly due to mismatch of context and bankContext in the -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
method in Person.
the code is as following, first is the Bank Class:
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
then is the Person Class:
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
at last is the ViewController Class
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
after I click the button, the new and old value of accountDomestic not shown. You can see the context and bankContext value are not equal as the picture shown below:
Does anyone have any idea of that?
Upvotes: 1
Views: 112
Reputation: 1547
The reason is there are two bankContext
s. In Bank.h
, you have
static void * const bankContext = &bankContext;
This file is then included in both Bank.m
and Person.m
, so both files (compilation units) define a pointer bankContext
, which is marked as static
so generates no external linkage (so you can have two with same name).
The most direct solution is to ensure there is only one bankContext
. In Bank.h
:
extern void * const bankContext;
and in Bank.m
:
void * const bankContext = &bankContext;
That said, I think it would be better to restructure the code so this isn’t necessary. Your setup of responsibilities makes me wary (one object telling another to become an observer), and I’m surprised it compiles correctly since there appear to be cyclic imports (Bank.h
and Person.h
import each other).
Upvotes: 2