Objective c 观察上下文的键值无效

Objective c 观察上下文的键值无效,objective-c,key-value-observing,Objective C,Key Value Observing,在Objective-C中,当使用键值观察时,我有一个带有AccountHome属性和person属性的银行类。增加该人员以观察国内财产账户。我在Bank类中有一个静态void*bankContext=&bankContext作为其上下文。但是,在我更改AccountHomeland属性后,由于对象的-(void)observeValueForKeyPath:(NSString*)键路径:(id)对象更改:(NSDictionary*)更改上下文:(void*)上下文方法中的上下文和bankCo

在Objective-C中,当使用键值观察时,我有一个带有AccountHome属性和person属性的银行类。增加该人员以观察国内财产账户。我在Bank类中有一个
静态void*bankContext=&bankContext
作为其上下文。但是,在我更改AccountHomeland属性后,由于对象的
-(void)observeValueForKeyPath:(NSString*)键路径:(id)对象更改:(NSDictionary*)更改上下文:(void*)上下文
方法中的上下文和bankContext不匹配,因此无法正确显示新旧值

代码如下,首先是银行类:

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
Bank.h
#进口
#输入“Person.h”
静态void*const bankContext=&bankContext;
@班主任;
@接口库:NSObject
@属性(非原子,强)NSNumber*AccountNational;
@财产(非原子、强)人*人;
-(instancetype)initWithPerson:(Person*)Person;
@结束
m银行
@实施
-(instancetype)initWithPerson:(Person*)Person{
if(self=[super init]){
_人=人;
[自我添加观察者:\u个人
forKeyPath:NSStringFromSelector(@selector(AccountHome))
选项:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
上下文:bankContext];
}
回归自我;
}
-(无效)解除锁定{
[self-removeObserver:_PersonForkeyPath:NSStringFromSelector(@selector(AccountHome))];
}
@结束
然后是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
Person.h
#进口
#输入“Bank.h”
@接口人:NSObject
@结束
人
#输入“Person.h”
@执行人
-(void)observeValueForKeyPath:(NSString*)对象的键路径:(id)对象更改:(NSDictionary*)更改上下文:(void*)上下文{
NSLog(@“上下文:%p”,上下文);
NSLog(@“bankContext:%p”,bankContext);
if(context==bankContext){
if([keyPath IsequalString:NSStringFromSelector(@selector(AccountHome))])){
NSString*oldValue=change[NSKeyValueChangeOldKey];
NSString*newValue=change[NSKeyValueChangeNewKey];
NSLog(@“-----------------------------------”);
NSLog(@“会计国内旧值:%@”,旧值);
NSLog(@“Account国内新值:%@”,新值);
}
}
}
@结束
最后是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
ViewController.h
#进口
@界面ViewController:UIViewController
@结束
ViewController.m
#导入“ViewController.h”
#输入“Bank.h”
#输入“Person.h”
@界面视图控制器()
@不动产(非原子、强)银行*银行;
@财产(非原子、强)人*人;
@属性(非原子,强)NSNumber*delta;
@结束
@实现视图控制器
-(无效)viewDidLoad{
[超级视图下载];
self.person=[[person alloc]init];
self.delta=@10;
self.bank=[[bank alloc]initWithPerson:self.person];
}
-(iAction)AccountDomesticEncreateButtondTouch:(id)发件人{
self.bank.account国内=self.delta;
int temp=[self.delta intValue];
温度+=10;
self.delta=[NSNumber numberwhithint:temp];
}
@结束
单击按钮后,AccountHomeal的新值和旧值均未显示。您可以看到context和bankContext值不相等,如下图所示:


有人知道吗?

原因是有两个
bankContext
s。在
Bank.h
中,您有

static void*const bankContext=&bankContext;

然后,该文件同时包含在
Bank.m
Person.m
中,因此这两个文件(编译单元)都定义了一个指针
bankContext
,该指针标记为
static
,因此不会生成外部链接(因此可以有两个同名指针)

最直接的解决方案是确保只有一个
bankContext
。在
Bank.h
中:

extern void*const bankContext;

而在银行m中:

void*const bankContext=&bankContext;


也就是说,我认为最好重新构造代码,这样就没有必要了。你的职责设置让我很警惕(一个对象告诉另一个对象成为观察者),我很惊讶它编译正确,因为似乎存在循环导入(
Bank.h
Person.h
相互导入)。

如果你使用静态对象,那么这个类如果有效地简化为单例。为什么要这样限制类?本文建议这样做。作为象征。如果我不关心同一个类的不同实例,我认为这很好。关键是为什么contex和bankContext不相等。啊,好吧,我错了。我收回:)你说的是对的!使用bankContext=&bankContext的目的是作为特定类()的标记。为此,我认为可以使用
if(objectiskindofclass:[Bank class]])
if([context==bankContext)更好
无需额外的bankContext变量声明。我建议您继续使用NSHipster文章中建议的上下文指针。您需要学习更多关于C中变量存储的知识,以便将其正确应用于您的情况。感谢您的建议,我已经看到了静态和外部变量之间的区别。难道不需要声明吗上下文指针只是用来标识情况的标记?据我所知,我看不到其他优势。它不是严格必需的,但会更加健壮,因为如果只检查对象和键路径,超类或子类(甚至可能是其他对象)也可能设置可能会干扰的观察。