4

我使用 XIB 创建一个 CustomView:UIView,为 NSInteger 属性加载和 addObserver,如下所示:

//自定义视图.h

@interface CustomView : UIView
    @property (nonatomic) NSInteger inputStateControl;
@end

//自定义视图.m

static void *kInputStateControlObservingContext = &kInputStateControlObservingContext;
@implementation CustomView
- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomView" owner:self options:nil];
        self = [nib objectAtIndex:0];
        //
        [self commonInit];
    }
    return self;
}

-(void)commonInit{
[self addObserver:self forKeyPath:@"inputStateControl" options:NSKeyValueObservingOptionOld context:kInputStateControlObservingContext];
}

#pragma mark Observer
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    if ( context == kInputStateControlObservingContext ) {
        NSInteger oldState = [[change objectForKey:NSKeyValueChangeOldKey] integerValue];
        if ( oldState != self.inputStateControl ) {
            NSLog(@"CONTEXT change %i to %i",oldState,self.inputStateControl);
        }
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

-(void)dealloc{
    [self removeObserver:self forKeyPath:@"inputStateControl"];
//    [self removeObserver:self forKeyPath:@"inputStateControl" context:kInputStateControlObservingContext];
}

@end

如果我在 dealloc 中注释掉 removeObserver,一切正常,这是日志:

CONTEXT change 0 to 2

但是当removeObserver,App崩溃:

*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <Keyboard 0x6a8bcc0> for the key path "inputStateControl" from <Keyboard 0x6a8bcc0> because it is not registered as an observer.'

评论加载 CustomView.xib 时不会崩溃,但与 XIB 无关。我的代码有什么问题?

如何使用自定义 Xib 在 CustomView 中为 NSInteger 属性添加和删除Observer?

提前致谢!

*编辑:我添加我的代码以明确我的问题。请帮忙!

https://github.com/lequysang/github_zip/blob/master/CustomViewKVO.zip

4

4 回答 4

3

这就是正在发生的事情 - 在您的 viewDidLoad 方法中,您调用[[CustomView alloc] init]. 这将创建一个新的 CustomView 实例,并调用init它。但是,在 中init,您从 nib 加载一个新实例并用 nibself中的那个替换。这会导致您创建alloc并设置的实例self = [super init];被释放,因为不再有对它的强引用。由于此实例在调用之前被释放commonInit,因此它从不观察自己的属性,因此将自己作为观察者移除会导致异常。

解决此问题的一种方法是直接从视图控制器中的 nib 加载视图,或在 CustomView 上创建一个类方法

NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"CustomView" owner:nil options:nil];
CustomView *customView = topLevelObjects[0];

如果您确实采用了这种方法,请丢弃您init的实现并将其替换为执行以下操作的initWithCoder:

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        _inputStateControl = 0;
        [self commonInit];
    }

    return self;
}

实现的原因initWithCoder:是当你从 nib 加载视图时会自动调用它。您只需要实现它,您就可以进行已经在init. 还要确保你的 dealloc 是这样实现的:

-(void)dealloc{
    [self removeObserver:self forKeyPath:@"inputStateControl" context:kInputStateControlObservingContext];
}
于 2013-03-29T02:25:19.123 回答
1

我不知道为什么你正在做的事情不起作用,但是让一个物体像这样观察自己是一个坏主意。您应该只显式地实现您的 inputStateControl 的 setter ( setInputStateControl) 并在该 setter 方法中执行您的日志记录以及您想要的任何其他副作用。

于 2013-03-29T00:08:11.070 回答
0

虽然无论如何都不是一个理想的解决方案,但使用 try-catch 块围绕删除将解决您的问题。如果实际上观察者没有注册,那么在移除它的过程中忽略异常是安全的。主要风险是您的应用程序依赖于它已注册的任何假设。

于 2013-03-29T00:43:29.510 回答
0

不知道你为什么会遇到这个问题。我做的最快捷的方法是在 dealloc 中将自定义观察者设置为“nil”;),但在你的情况下,使用这种快捷方式还需要添加观察者其他方式。

好吧,至少我可以告诉你的是,在你的交易中,

-(void)dealloc{
    [[NSNotificationCenter default center] removeObserver: self];
    [self removeObserver:self forKeyPath:@"inputStateControl"];
    //OR
    //If you had create a observer called "temp",then easy way to remove the observer is temp=nil;

}

如果您仍然坚持,请将@try @catch 块用于临时解决方案,请注意它不会删除观察者;)...

不是您正在寻找的完美答案,但它正是我认为的方式......快乐编码:-)

于 2013-03-29T02:14:38.667 回答