键值观察上下文不起作用
Key Value Observing context not work
在 Objective-C 中,当使用 Key-Value Observing 时,我有一家银行 class,账户为国内 属性 和一个人 属性。添加此人以观察 accountDomestic 属性。我在 Bank class 中有一个 static void *bankContext = & bankContext
作为它的上下文。但是,在我更改 accountDomestic 属性 后,由于 Person 中 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
方法中的上下文和 bankContext 不匹配,旧值和新值无法正确显示。
代码如下,首先是银行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
然后是人 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
最后是ViewControllerClass
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 的新旧值未显示。可以看到context和bankContext的值不相等,如下图:
有人知道吗?
原因是有两个bankContext
。在 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
相互导入)。
在 Objective-C 中,当使用 Key-Value Observing 时,我有一家银行 class,账户为国内 属性 和一个人 属性。添加此人以观察 accountDomestic 属性。我在 Bank class 中有一个 static void *bankContext = & bankContext
作为它的上下文。但是,在我更改 accountDomestic 属性 后,由于 Person 中 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
方法中的上下文和 bankContext 不匹配,旧值和新值无法正确显示。
代码如下,首先是银行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
然后是人 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
最后是ViewControllerClass
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 的新旧值未显示。可以看到context和bankContext的值不相等,如下图:
有人知道吗?
原因是有两个bankContext
。在 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
相互导入)。