iOS:如何知道某个媒体资源是否符合KVO标准?

在“ 键值观察编程指南”中 , 键值观察注册部分表示:“如果按照文档logging,则Apple提供的框架中的典型属性只有KVO兼容”。 但是,我没有发现文档中logging为符合KVO的任何属性。 你能指点我一些吗?

具体来说,我想知道UIWindow @property(nonatomic,retain) UIViewController *rootViewController是否符合KVO。 原因是我将rootViewController属性添加到UIWindow for iOS <4,并想知道是否应该使其符合KVO。

 @interface UIWindow (Additions) #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0 @property (nonatomic, retain) UIViewController *rootViewController; #endif; @end @implementation UIWindow (Additions) #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0 @dynamic rootViewController; - (void)setRootViewController:(UIViewController *)newRootViewController { if (newRootViewController != _rootViewController) { // Remove old views before adding the new one. for (UIView *subview in [self subviews]) { [subview removeFromSuperview]; } [_rootViewController release]; _rootViewController = newRootViewController; [_rootViewController retain]; [self addSubview:_rootViewController.view]; } } #endif @end 

简短的回答:不。

长时间回答:UIKit中没有任何内容保证符合KVO标准。 如果你碰巧发现KVO-一个财产的作品,感激,这是无意的。 另外:要小心。 未来它可能会很好地打破。

如果您发现这是您需要的东西,请提交增强请求 。


关于你的实际代码,这本质上是有缺陷的。 不要试图以这种方式向UIWindow添加“rootViewController”设置器。 当你在iOS 4上编译你的代码,但有人在iOS 5设备上运行它时,它中断。 因为您使用4.x SDK进行编译,所以#if语句将评估为true,这意味着您的类别方法smasher将包含在二进制文件中。 但是,当您在iOS 5设备上运行它时,您现在将会遇到方法冲突,因为UIWindow上的两个方法将具有相同的方法签名,并且不能保证将使用哪一个方法

不要像这样使用框架。 如果你必须有这个,使用一个子类。 这是为什么分类存在。


你的子类看起来像这样:

 @interface CustomWindow : UIWindow @property (nonatomic, retain) UIViewController *rootViewController; @end @implementation CustomWindow : UIWindow static BOOL UIWindowHasRootViewController = NO; @dynamic rootViewController; - (void)_findRootViewControllerMethod { static dispatch_once_t predicate; dispatch_once(&predicate, ^{ IMP uiwindowMethod = [UIWindow instanceMethodForSelector:@selector(setRootViewController:)]; IMP customWindowMethod = [CustomWindow instanceMethodForSelector:@selector(setRootViewController:)]; UIWindowHasRootViewController = (uiwindowMethod != NULL && uiwindowMethod != customWindowMethod); }); } - (UIViewController *)rootViewController { [self _findRootViewControllerMethod]; if (UIWindowHasRootViewController) { // this will be a compile error unless you forward declare the property // i'll leave as an exercise to the reader ;) return [super rootViewController]; } // return the one here on your subclass } - (void)setRootViewController:(UIViewController *)rootViewController { [self _findRootViewControllerMethod]; if (UIWindowHasRootViewController) { // this will be a compile error unless you forward declare the property // i'll leave as an exercise to the reader ;) [super setRootViewController:rootViewController]; } else { // set the one here on your subclass } } 

警告实施者 :我在浏览器窗口中input了这个信息

基于@David DeLong的解决scheme ,这就是我想出来的,而且它运行的非常漂亮。

基本上,我在UIWindow上做了一个类。 并在+load ,我(运行时)检查是否[UIWindow instancesRespondToSelector:@selector(rootViewController)] 。 如果没有,我使用class_addMethod()class_addMethod()dynamic添加getter&setter方法。 另外,我使用objc_getAssociatedObjectobjc_setAssociatedObject来获取&设置rootViewController作为UIWindow一个实例variables。

 // UIWindow+Additions.h @interface UIWindow (Additions) @end // UIWindow+Additions.m #import "UIWindow+Additions.h" #include <objc/runtime.h> @implementation UIWindow (Additions) #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0 // Add rootViewController getter & setter. static UIViewController *rootViewControllerKey; UIViewController *rootViewController3(id self, SEL _cmd); void setRootViewController3(id self, SEL _cmd, UIViewController *newRootViewController); UIViewController *rootViewController3(id self, SEL _cmd) { return (UIViewController *)objc_getAssociatedObject(self, &rootViewControllerKey); } void setRootViewController3(id self, SEL _cmd, UIViewController *newRootViewController) { UIViewController *rootViewController = [self performSelector:@selector(rootViewController)]; if (newRootViewController != rootViewController) { // Remove old views before adding the new one. for (UIView *subview in [self subviews]) { [subview removeFromSuperview]; } objc_setAssociatedObject(self, &rootViewControllerKey, newRootViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self addSubview:newRootViewController.view]; } } + (void)load { if (![UIWindow instancesRespondToSelector:@selector(rootViewController)]) { class_addMethod([self class], @selector(rootViewController), (IMP)rootViewController3, "@@:"); class_addMethod([self class], @selector(setRootViewController:), (IMP)setRootViewController3, "v@:@"); } } #endif @end 

这是一个使用关联引用的解决scheme来定义一个类别的实例variables 。 但是,根据@Dave DeLong的说法,这不起作用,我必须使用运行时(而不是编译时)检查 。

 // UIWindow+Additions.h @interface UIWindow (Addtions) #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0 @property (retain, nonatomic) UIViewController *rootViewController; #endif @end // UIWindow+Additions.m #import "UIWindow+Additions.h" #include <objc/runtime.h> @implementation UIWindow (Additions) #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0 @dynamic rootViewController; static UIViewController *rootViewControllerKey; - (UIViewController *)rootViewController { return (UIViewController *)objc_getAssociatedObject(self, &rootViewControllerKey); } - (void)setRootViewController:(UIViewController *)newRootViewController { UIViewController *rootViewController = self.rootViewController; if (newRootViewController != rootViewController) { // Remove old views before adding the new one. for (UIView *subview in [self subviews]) { [subview removeFromSuperview]; } [rootViewController release]; objc_setAssociatedObject(self, &rootViewControllerKey, newRootViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [rootViewController retain]; [self addSubview:rootViewController.view]; } } #endif @end 

基于@David DeLong的反馈,我使用了一个简单的子类:

 // UIWindow3.h #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0 @interface UIWindow3 : UIWindow { } @property (nonatomic, retain) UIViewController *rootViewController; @end #endif // UIWindow3.m #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_4_0 #import "UIWindow3.h" @implementation UIWindow3 @synthesize rootViewController; - (void)setRootViewController:(UIViewController *)newRootViewController { if (newRootViewController != rootViewController) { // Remove old views before adding the new one. for (UIView *subview in [self subviews]) { [subview removeFromSuperview]; } [rootViewController release]; rootViewController = newRootViewController; [rootViewController retain]; [self addSubview:rootViewController.view]; } } @end #endif 

但是,这也需要通过现有的代码,并使用条件编译将UIWindowUIWindow3 ,从而访问rootViewController 。 (注:我认为@David DeLong的解决scheme可能不需要进行这些额外的更改,而只是始终使用CustomWindow而不是UIWindow 。)因此,这比我可以(仅适用于iOS <4)更加恼人,只需将rootViewController添加到UIWindow通过一个类别。 我可能会考虑使用关联引用 (仅适用于iOS <4)的类别来进行此操作,因为我认为这看起来是最有说服力的解决scheme,并且可能是在工具箱中学习和使用的好技术。