Cry*_*tal 40 objective-c key-value-observing
我想知道在观察属性时应该在KVO中设置Context指针.我刚开始使用KVO而且我没有从文档中收集到太多东西.我在这个页面上看到:http://www.jakeri.net/2009/12/custom-callout-bubble-in-mkmapview-final-solution/作者这样做:
[annView addObserver:self
forKeyPath:@"selected"
options:NSKeyValueObservingOptionNew
context:GMAP_ANNOTATION_SELECTED];
Run Code Online (Sandbox Code Playgroud)
然后在回调中,这样做:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context{
NSString *action = (NSString*)context;
if([action isEqualToString:GMAP_ANNOTATION_SELECTED]){
Run Code Online (Sandbox Code Playgroud)
我假设在这种情况下,作者只是创建一个字符串,以便稍后在回调中识别.
然后在iOS 5 Pushing the Limits一书中,我看到他这样做:
[self.target addObserf:self forKeyPath:self.property options:0 context:(__bridge void *)self];
Run Code Online (Sandbox Code Playgroud)
打回来:
if ((__bridge id)context == self) {
}
else {
[super observeValueForKeyPath .......];
}
Run Code Online (Sandbox Code Playgroud)
我想知道是否有传递到上下文指针的标准或最佳实践?
ipm*_*mcc 96
最重要的是(一般来说)您使用的东西(而不是没有),而且无论你使用是独特的和私人在您使用它.
这里的主要缺陷发生在你的某个类中有一个观察结果,然后有人对你的类进行子类化时,他们会添加对同一个观察对象和同一个keyPath的另一个观察结果.如果您的原始observeValueForKeyPath:...
实现仅检查keyPath
,或观察到object
,或甚至两者,可能不足以知道您的观察被回叫.使用context
其值是唯一且私有的,可以让您更加确定给定的调用observeValueForKeyPath:...
是您期望的调用.
例如,如果您仅注册didChange
通知,但子类注册了同一对象,则使用该NSKeyValueObservingOptionPrior
选项注册了keyPath,这就很重要.如果您没有过滤observeValueForKeyPath:...
使用a context
(或检查更改字典)的调用,那么当您只希望执行一次时,您的处理程序将执行多次.不难想象这会如何导致问题.
我使用的模式是:
static void * const MyClassKVOContext = (void*)&MyClassKVOContext;
Run Code Online (Sandbox Code Playgroud)
该指针将指向它自己的位置,该位置是唯一的(没有其他静态或全局变量可以有这个地址,也不能有任何堆或堆栈分配的对象曾经有这个地址-这是一个非常强大的,但无可否认不是绝对的,保证),感谢链接器.将const
使得这样编译器会警告我们,如果我们曾经尝试编写的代码,会改变指针的值,最后,static
使得它私有的这个文件,所以这个文件之外没有人能够获得对它的引用(再次,使其更有可能避免碰撞).
我特别注意一个模式对使用是一个出现了问题:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSString *action = (NSString*)context;
if([action isEqualToString:GMAP_ANNOTATION_SELECTED]) {
Run Code Online (Sandbox Code Playgroud)
context
声明是a void*
,这意味着可以保证它是什么.把它NSString*
扔到你正在打开一大盒潜在的坏处.如果有人碰巧有一个注册不使用NSString*
的context
参数,当你通过非对象物价值这种方法将崩溃isEqualToString:
.指针相等(或替代intptr_t
或uintptr_t
相等)是唯一可与context
值一起使用的安全检查.
使用self
作为一种context
常见的方法.它总比没有好,但具有更弱的统一性和隐私性,因为其他对象(更不用说子类)可以访问它的价值self
并且可能将其用作context
(引起歧义),这与我上面提到的方法不同.
还要记住,这不仅仅是可能导致陷阱的子类; 虽然它可以说是一种罕见的模式,但没有任何东西阻止另一个对象注册你的对象以获得新的KVO观察结果.
为了提高可读性,您还可以将其包装在预处理器宏中,如:
#define MyKVOContext(A) static void * const A = (void*)&A;
Run Code Online (Sandbox Code Playgroud)
Nat*_*ler 18
KVO上下文应该是指向静态变量的指针,正如这个要点所示.通常,我发现自己在做以下事情:
在我的文件顶部附近,ClassName.m
我将有线
static char ClassNameKVOContext = 0;
Run Code Online (Sandbox Code Playgroud)
当我开始观察我所aspect
拥有的targetObject
(一个实例TargetClass
)上的属性时
[targetObject addObserver:self
forKeyPath:PFXKeyTargetClassAspect
options://...
context:&ClassNameKVOContext];
Run Code Online (Sandbox Code Playgroud)
其中PFXKeyTargetClassAspect的NSString *
定义TargetClass.m
是等于@"aspect"
和声明extern
的TargetClass.h
.(当然PFX只是你在项目中使用的前缀的占位符.)这给了我自动完成的优势并保护我免受错别字的影响.
当我完成观察时aspect
,targetObject
我会有
[targetObject removeObserver:self
forKeyPath:PFXKeyTargetClassAspect
context:&ClassNameKVOContext];
Run Code Online (Sandbox Code Playgroud)
为了避免在我的实现中有太多的缩进-observeValueForKeyPath:ofObject:change:context:
,我喜欢写
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context != &ClassNameKVOContext) {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
if ([object isEqual:targetObject]) {
if ([keyPath isEqualToString:PFXKeyTargetClassAspect]) {
//targetObject has changed the value for the key @"aspect".
//do something about it
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
15074 次 |
最近记录: |