addObserver(KVO)中上下文参数的最佳做法 [英] Best practices for context parameter in addObserver (KVO)

查看:196
本文介绍了addObserver(KVO)中上下文参数的最佳做法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道在观察属性时应在KVO中设置上下文指针.我刚刚开始使用KVO,并且我从文档中没有得到太多信息.我在此页面上看到: http://www.jakeri.net/2009/12/custom-callout-bubble-in-mkmapview-final-solution/作者是这样做的:

I was wondering what you should set the Context pointer in KVO when you are observing a property. I'm just starting to use KVO and I haven't gleaned too much from the documentation. I see on this page: http://www.jakeri.net/2009/12/custom-callout-bubble-in-mkmapview-final-solution/ the author does this:

[annView addObserver:self
forKeyPath:@"selected"
options:NSKeyValueObservingOptionNew
context:GMAP_ANNOTATION_SELECTED];

然后在回调中执行此操作:

And then in the callback, does this:

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

NSString *action = (NSString*)context;


if([action isEqualToString:GMAP_ANNOTATION_SELECTED]){

我假设在这种情况下,作者只是创建了一个字符串,供以后在回调中识别.

I'm assuming in this scenario, the author just creates a string to be identified later in the callback.

然后在iOS 5 Pushing the Limits中,我看到他这样做:

Then in iOS 5 Pushing the Limits book, I see he does this:

[self.target addObserf:self forKeyPath:self.property options:0 context:(__bridge void *)self];

回调:

if ((__bridge id)context == self) {
}
else {
   [super observeValueForKeyPath .......];
}

我想知道是否有标准或最佳做法可以传递给上下文指针?

I was wondering if there is a standard or best practices to pass into the context pointer?

推荐答案

重要的是(通常来说)您使用某物(而不是不使用任何东西),并且您使用的任何东西都要独特私人使用.

The important thing is (generally speaking) that you use something (as opposed to nothing) and that whatever you use be unique and private to your use of it.

这里的主要陷阱发生在您对一个类进行观察时,然后有人子类化了您的类,并且他们添加了对相同观察对象和相同keyPath的另一个观察.如果您原始的observeValueForKeyPath:...实现仅检查了keyPath或观察到的object或什至两者,则可能不足以知道它是您的 回调.使用值为您唯一且私有的context可以使您更加确定对observeValueForKeyPath:...的给定调用就是您期望的调用.

The primary pitfall here happens when you have an observation in one of your classes, and then someone subclasses your class, and they add another observation of the same observed object and the same keyPath. If your original observeValueForKeyPath:... implementation only checked keyPath, or the observed object, or even both, that might not be sufficient to know that it's your observation being called back. Using a context whose value is unique and private to you allows you to be much more certain that a given call to observeValueForKeyPath:... is the call you're expecting it to be.

这很重要,例如,如果您仅注册了didChange通知,但是子类使用NSKeyValueObservingOptionPrior选项注册了相同的对象和keyPath.如果您没有使用context过滤对observeValueForKeyPath:...的调用(或检查更改字典),则处理程序将执行多次,而您只希望它执行一次.不难想象这可能会引起什么问题.

This would matter if, for instance, you registered only for didChange notifications, but a subclass registers for the same object and keyPath with the NSKeyValueObservingOptionPrior option. If you weren't filtering calls to observeValueForKeyPath:... using a context (or checking the change dictionary), your handler would execute multiple times, when you only expected it to execute once. It's not hard to imagine how this might cause problems.

我使用的模式是:

static void * const MyClassKVOContext = (void*)&MyClassKVOContext;

该指针将指向其自己的位置,并且该位置是唯一的(其他任何静态或全局变量都不能具有该地址,任何堆或堆栈分配的对象也都不能具有该地址-尽管公认地,它非常强大)而不是绝对,保证),这要归功于链接器. const使得它,如果我们尝试编写会更改指针值的代码,则编译器将向我们发出警告,最后,static使其成为该文件的私有文件,因此该文件之外的任何人都无法获得该文件.对其的引用(再次使它更有可能避免碰撞).

This pointer will point to its own location, and that location is unique (no other static or global variable can have this address, nor can any heap or stack allocated object ever have this address -- it's a pretty strong, although admittedly not absolute, guarantee), thanks to the linker. The const makes it so that the compiler will warn us if we ever try to write code that would change the value of the pointer, and lastly, static makes it private to this file, so no one outside this file can obtain a reference to it (again, making it more likely to avoid collisions).

我特别提醒反对使用的一种模式是出现在问题中的一种模式:

One pattern I would specifically caution against using is one that appeared in the question:

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

    NSString *action = (NSString*)context;
    if([action isEqualToString:GMAP_ANNOTATION_SELECTED]) {

context被声明为void*,这意味着可以保证它是什么.通过将其强制转换为NSString*,您可以打开一大盒潜在的缺陷.如果其他人碰巧注册了NSString*用作context参数,则当您将非对象值传递给isEqualToString:时,此方法将崩溃.指针相等(或intptr_tuintptr_t相等)是可以与context值一起使用的唯一安全检查.

context is declared to be a void*, meaning that that's all the guarantee that can be made about what it is. By casting it to an NSString* you're opening a big box of potential badness. If someone else happens to have a registration that doesn't use an NSString* for the context parameter, this approach will crash when you pass the non-object value to isEqualToString:. Pointer equality (or alternatively intptr_t or uintptr_t equality) are the only safe checks that can be used with a context value.

self用作context是常见的方法.它总比没有好,但是唯一性和保密性却弱得多,因为其他对象(更不用说子类)可以访问self的值,并且可能将其用作context(引起歧义),这与我的方法不同.以上建议.

Using self as a context is a common approach. It's better than nothing, but has much weaker uniquing and privacy, since other objects (not to mention subclasses) have access to the value of self and might use it as a context (causing ambiguity), unlike with the approach I suggested above.

还请记住,不是 just 子类可能会引起陷阱.尽管这可以说是一种罕见的模式,但是没有什么可以阻止另一个对象为新的KVO观测而注册您的对象.

Also remember, it's not just subclasses that might cause pitfalls here; Although it's arguably a rare pattern, there's nothing that preventing another object from registering your object for new KVO observations.

为了提高可读性,您还可以将其包装在预处理器宏中,例如:

For improved readability, you could also wrap this up in a preprocessor macro like:

#define MyKVOContext(A) static void * const A = (void*)&A;

这篇关于addObserver(KVO)中上下文参数的最佳做法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆