从单元格到托管对象

时间:2016-01-22 15:48:49

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

我正在查看表格视图单元格,我找到了这段代码:

- (void)awakeFromNib {
    [super awakeFromNib];
    [self addObserver:self forKeyPath:@"model.isDownloading" options:NSKeyValueObservingOptionNew context:NULL];
    [self addObserver:self forKeyPath:@"model.isCached" options:NSKeyValueObservingOptionNew context:NULL];
    [self addObserver:self forKeyPath:@"model.isOutDated" options:NSKeyValueObservingOptionNew context:NULL];
    [self addObserver:self forKeyPath:@"model.cacheUpdateDate" options:NSKeyValueObservingOptionNew context:NULL];
    [self addObserver:self forKeyPath:@"model" options:NSKeyValueObservingOptionNew context:NULL];
}

dealloc方法中删除了观察者。 modelweak属性,接收托管对象(核心数据)。

我收到虚假的崩溃,告诉我,托管对象被删除了,但仍然注册了观察者。

为什么错误发生对我来说非常清楚:对象在后台的某处被删除,但仍然链接到tableview的单元格中。由于单元格上的dealloc基本上从未在应用程序的生命周期内被调用,因此观察者永远不会被删除。由于对核心数据对象的引用是weak,它将在后台静默释放 - 至少尝试一下。这失败了,因为仍然可以观察到模型。

我有一些问题:

  • 如果观察到类似“model.isDownloading”的路径,那么观察者在model对象中注册,而不是self中的设置者,是正确的吗?
  • 如果重新分配model self.model = newThingremoveObserver需要,model在分配newThing之前调用newThing,objC是否足够聪明以处理观察者更改,并且观察员需要在model之后注册。
  • 由于崩溃发生在托管对象的dealloc上,我认为一个简单的解决方案是使weak强而不是nil,当然要确保它已正确设置为prepareForReuse:中的class xxx was deallocated while key value observers were still registered with it。这有副作用吗,我还没有意识到?

错误消息是:

/// <summary> /// Computes the PBKDF2-SHA1 hash of a password. /// </summary> /// <param name="password">The password to hash.</param> /// <param name="salt">The salt.</param> /// <param name="iterations">The PBKDF2 iteration count.</param> /// <param name="outputBytes">The length of the hash to generate, in bytes.</param> /// <returns>A hash of the password.</returns> private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes) { var pdb = new Pkcs5S2ParametersGenerator(); pdb.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()), salt, iterations); var key = (KeyParameter)pdb.GenerateDerivedMacParameters(outputBytes * 8); return key.GetKey(); }

1 个答案:

答案 0 :(得分:1)

如果观察到像“model.isDownloading”这样的路径,那么观察者在model对象中注册,而不是self中的setter,这是正确的吗?

这是一个非常好的问题。据我所知,当一个对象用KVO注册时,运行时至少会记录两件事情 1) 观察的对象和
2)它正在观察的属性的关键路径。

我们知道运行时会覆盖属性的setter,以通知观察到更​​改的对象。

但显然运行时还必须跟踪被>观察到的对象,否则如果仍有观察者注册的话,它是否会知道它是否被释放?

运行时似乎解析了keyPath的点,并跟随接收器的参考链(在您的示例中为self),以跟踪观察到的对象(self.model

如果model需要重新分配self.model = newThing,那么objC足够聪明,可以处理观察员更改removeObservermodel上调用newThing,然后再分配newThing然后需要在self.model之后注册观察者。

不,它不够聪明。例如,运行时将Model对象(我们说它的类型为isDownloading)子类化为覆盖self.model的setter。现在,您的NSKVONotifying_Model对象的类型为self.model。如果要将Model指针交换为指向model类型的新对象,则它将与运行时创建的KVO类不同。因此,属性的setter不会添加指令以通知观察者。 所以是的,您必须删除第一个对象上的观察者并将其添加到第二个对象,即使您使用相同的指针变量。

由于崩溃发生在托管对象的dealloc上,我认为一个简单的解决方案是使weak强而不是nil,当然要确保它是在prepareForReuse:中正确设置为model。这是否有副作用,我还没有意识到?

这是正确的,但正如您所知,如果您的引用的Model对象被换出,您将不得不重新添加观察者。

另一种替代方法(如果您可以更改Model类)是在此上下文中将self类中的引用添加回init。然后,在dealloc课程的Model / addObserver中,您可以将自己添加为新参考的观察者。

最后要注意的是,如果您发现-setModel消息,其中接收者的观察者对象是相同的 - 您也可以自己覆盖设置者。< / p>

在您的示例中,您可以覆盖-observeValueForKey:::来执行您在观察通知处理程序中执行的任何操作(class Foo: pass foo = Foo() setattr(foo, 'bar', 42) print(foo.bar) # 42