2011-07-07 49 views
15

Key-Value Observing Programming Guide中,Registering for Key-Value Observing部分指出:「如果Apple等提供的框架中的屬性記錄爲如此,那麼它們通常只具有KVO兼容性。」但是,我沒有在文檔中找到任何記錄爲KVO兼容的屬性。你能指點我一些嗎?iOS:如何知道某個媒體資源是否符合KVO標準?

具體而言,我想知道UIWindow@property(nonatomic,retain) UIViewController *rootViewController是否符合KVO。原因是我將rootViewController屬性添加到UIWindow對於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 

回答

16

簡短的回答:第

龍答:UIKit中沒有什麼是保證KVO兼容。如果您碰巧發現KVO-property屬性有效,請注意,這是無意的。另外:要小心。它可能會在未來破裂。

如果您發現這是您需要的東西,請file an enhancement request


關於您的實際代碼,它本質上存在缺陷。做不是試圖通過這種方式添加一個「rootViewController」setter到UIWindow。當你在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 
    } 
} 

買者實施者:我在瀏覽器窗口

+0

很酷,謝謝!那麼,作爲後續問題,代碼看起來是否正確?這是我第一次通過類別添加伊娃。 – ma11hew28

+0

@MattDiPasquale編輯答案:) –

+0

嗯......是的,我無法編譯它的iPhone 4.3.1設備。我得到了'爲架構的ARMv6未定義的符號: 「_OBJC_IVAR _ $ _ UIWindow._rootViewController」,從引用: - [一個UIWindow(添加)setRootViewController:在一個UIWindow + Additions.o LD:符號(S)沒有發現架構的ARMv6 collect2:ld返回1退出狀態。所以,我想爲iOS 3.2創建子類。多一點代碼,但是應該這樣做!謝謝! – ma11hew28

-2

基於@大衛德隆的反饋輸入這個,我去一個簡單的小類如下:

// 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 

但是,這也需要通過現有代碼並使用條件編譯將UIWindow轉換爲UIWindow3,其中有rootViewController正在被訪問。 (注意:我認爲@David DeLong的解決方案可能不需要進行這些額外的更改,而只是始終使用CustomWindow而不是UIWindow。)因此,這比我可以更加煩人(僅適用於iOS < 4)只需將rootViewController添加到UIWindow通過一個類別。我可能會考慮使用category using Associative References(僅適用於iOS < 4),因爲我認爲這看起來會是最有說服力的解決方案,並且可能是在工具箱中學習和使用的好技術。

+1

你還有我前面提到的iOS 5問題。編譯時檢查不能解決這個問題;它必須是一個運行時檢查。 –

-1

下面是使用Associative References to define an instance variable with a category的解決方案。但是,根據@Dave DeLong的說法,這不起作用,因此我必須使用run-time (not compile-time) check

// 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 
0

根據@David DeLong's solution,這就是我想到的,它的工作原理非常漂亮。

基本上,我在UIWindow上做了一個類別。並在+load,我(運行時)檢查是否爲[UIWindow instancesRespondToSelector:@selector(rootViewController)]。如果不是,我使用class_addMethod()動態添加rootViewController的getter & setter方法。此外,我使用objc_getAssociatedObjectobjc_setAssociatedObject以獲得&將rootViewController設置爲UIWindow的實例變量。

// 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, "[email protected]:@"); 
    } 
} 
#endif 

@end 
+0

這仍然是一個壞主意。 Apple Framework對象上的類別應始終在其方法名稱的前面,以避免與將來或私有方法發生衝突。 – uchuugaka

相關問題