如果使用KVO特定obj,如何观察属性变化?

时间:2013-04-18 13:16:52

标签: ios objective-c key-value-observing

我有两个控制器,即aViewControllerbViewController。我在bViewController中有一个textField(名为txt1)。我已声明如下:

在bViewController.m

- (void)viewDidLoad
{
    [self addObserver:self forKeyPath:@"txt1.text" options:NSKeyValueObservingOptionNew context:nil];
}

在aViewController.m

- (void)viewDidLoad
{      
    bViewController *obj = [[bViewController alloc] init];
    [obj addObserver:self forKeyPath:@"txt1.text" options:NSKeyValueObservingOptionNew context:NULL];

    [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    /*
        NSLog(@"%@",keyPath);
        NSLog(@"%@",object);
        NSLog(@"%@",change);
      */
        NSLog(@"%s",__PRETTY_FUNCTION__);
        if ([keyPath isEqualToString:@"txt1.text"]) {
            NSLog(@"text1 content changed");
        }
    }

当我在txt1中添加一些文本时(点击返回键后),我收到如下错误:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<bViewController: 0x9134560>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: txt1.text
Observed object: <bViewController: 0x9134560>
Change: {
    kind = 1;
    new = werwetw;
}
Context: 0x0'
*** First throw call stack:
(0x1c92012 0x10cfe7e 0x1c91deb 0xb85406 0xb2267d 0xb2233c 0xb09417 0xb22b24 0xad7d60 0xb21eb5 0xdd707 0xe4b02 0xedda1 0xdc645 0x121fb5 0x1220e1 0xdc4e6 0x2a0a 0xe4d2b 0xed9f8 0x1923cf 0x198f7f 0x198a8c 0x1979fe 0x1a1c72 0x24ddb 0x1227f5 0x1227f5 0x1227f5 0x1227f5 0x1227f5 0x1227f5 0x1227f5 0x1227f5 0x24e35 0x24806 0x24beb 0x16698 0x1beddf9 0x1bedad0 0x1c07bf5 0x1c07962 0x1c38bb6 0x1c37f44 0x1c37e1b 0x1bec7e3 0x1bec668 0x13ffc 0x1fa2 0x1ed5)
libc++abi.dylib: terminate called throwing an exception

谁能告诉我哪里出错了。

2 个答案:

答案 0 :(得分:1)

有几点意见:

  1. B viewDidLoad正在为自己的属性添加一个观察者。那是不必要的。

  2. 我也想知道B是否实施了observeValueForKeyPath。由于我之前的观点,这没有实际意义,但如果添加一个观察者,则必须实现observeValueForKeyPath。这让我觉得可能是您的例外情况的来源。

  3. A正在创建B的本地实例,添加一个观察者,如果你正在使用ARC,那么你就让B超出范围,被释放,因此你有一个对象的观察者不再存在。或者,如果你没有使用ARC,你就不会遇到这个问题,但是你会泄露你的B副本。

    你说你要到B并更改文本,所以我不得不假设为了简化代码示例,你省略了可以防止这个问题出现的代码(例如推送/的代码)提出B),所以也许这不是一个问题。但关键的一点是,在释放观察者的项目之前,确保删除了观察者。

  4. 与您的问题无关,但B viewDidLoad未致电[super viewDidLoad]

  5. 你向我们展示了观察者的创造,但没有在适当的时候移除观察者。

  6. 作为总法律顾问,在像iOS这样的MVC环境中,不会建议将观察者添加到另一个视图(即UITextField)。我有B根据视图中的更改更新其模型属性,并将A作为该模型属性的观察者,而不是B视图层次结构中的任何内容。这在6.0之前的iOS版本中尤其重要,因为如果B随后调用了第三个控制器,C和低内存条件,则可以释放B的视图(并且您不希望观察者对可以解除分配的项目)。

  7. 令我感到震惊的是,在我们进一步诊断您的KVO之前,您应该演示A如何调用B.

    • A是一个控制器吗?在这种情况下,您不应该添加观察者,直到您这样做。

    • B是子控制器并且是自定义容器吗?在这种情况下,您必须添加自定义容器调用(例如addChildController等)。

    如果您能清楚地表达A和B之间的逻辑流程,我们可以更好地帮助您。或许更完整的代码片段会有所帮助。也许还能说明你想要解决的业务问题。

    说完所有这些,如果你试图在视图控制器之间传递数据,那么有更有效的方法(例如委托协议)。如果你试图将视图控制器中的数据传回给提供它的控制器,KVO可能不是正确的技术。

答案 1 :(得分:1)

我真的没有得到你的要求,但在查看你的代码之后,似乎你想要观察用户是否更改了UITextField中的文本..

在ViewController.h中添加方法

(IBAction)textFieldChanged:(id)sender;

在您的故事板或XIB中,您可以将此方法与您的UITextField相关联,因为已发送事件选择值已更改编辑已更改(tbh我不知道atm)。

在你的ViewController.m中实现所述方法

(IBAction)textFieldChanged:(id)sender {
    if (![sender isKindOfClass:[UITextField class]]) {
        return;
    }

    UITextField *field = sender;
    NSString *text = field.text;
    // Do something with the text or the field
}