2010-08-24 62 views
16

假設(爲了參數)我有一個包含NSDictionary的視圖類。我想要一大堆屬性,所有這些屬性都可以訪問該字典的成員。在Cocoa中編寫我自己的@dynamic屬性

例如,我想要@property NSString* title@property NSString* author

對於這些屬性中的每一個,實現都是相同的:對於getter,請調用[dictionary objectForKey:propertyName];,setter對setObject:forKey:執行相同的操作。

這將花費大量的時間並使用大量的複製粘貼代碼來編寫所有這些方法。有沒有一種方法可以自動生成它們,就像Core Data用NSManagedObject子類的@dynamic屬性一樣?清楚的是,我只想用這種方式訪問​​我在頭文件中定義的屬性,而不是任何任意的鍵。

我遇到過valueForUndefinedKey:作爲鍵值編碼的一部分,它可以處理getter,但我不完全確定這是否是最好的方法。

我需要這些是顯式屬性,所以我可以在Interface Builder中將它們綁定到它們:我最終計劃爲此視圖編寫一個IB調色板。我寫了一個WebView的子類,屬性會引用HTML元素的ID,但這並不重要,因爲這對於使用NSDictionary來存儲這些元素來說是不重要的。我的問題的邏輯!)

回答

17

我在設法解決了這個問題後,傾注了Objective-c運行時文檔。

我實現此類方法:

+ (BOOL) resolveInstanceMethod:(SEL)aSEL 
{ 
    NSString *method = NSStringFromSelector(aSEL); 

    if ([method hasPrefix:@"set"]) 
    { 
     class_addMethod([self class], aSEL, (IMP) accessorSetter, "[email protected]:@"); 
     return YES; 
    } 
    else 
    { 
     class_addMethod([self class], aSEL, (IMP) accessorGetter, "@@:"); 
     return YES; 
    } 
    return [super resolveInstanceMethod:aSEL]; 
} 

隨之而來的是對C函數:

NSString* accessorGetter(id self, SEL _cmd) 
{ 
    NSString *method = NSStringFromSelector(_cmd); 
    // Return the value of whatever key based on the method name 
} 

void accessorSetter(id self, SEL _cmd, NSString* newValue) 
{ 
    NSString *method = NSStringFromSelector(_cmd); 

    // remove set 
    NSString *anID = [[[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""] lowercaseString] stringByReplacingOccurrencesOfString:@":" withString:@""]; 

    // Set value of the key anID to newValue 
} 

由於該代碼試圖執行該被調用的類,而不是已經實現的任何方法,如果有人試圖調用你期望的東西,它會引起問題。我計劃增加一些理智檢查,以確保名稱與我期望的匹配。

+2

我只會做小寫的第一個字母(並確保_cmd字符串至少有5個字符): NSString * property = NSStringFromSelector(_cmd); NSString * firstLetter = [[property substringWithRange:NSMakeRange(3,1)] lowercaseString]; property = [firstLetter stringByAppendingString:[property substringWithRange:NSMakeRange(4,[property length] - 5)]]; – charles 2012-12-04 09:57:38

+0

很好的例子。我得到的只是一種凍結的IMP(即對於'setName:'我的IMP是'setValueForName'),但是您使用'NSStringFromSelector(_cmd)'給了我準確的踢我正在尋找的精神褲子把它推廣到'setWhatever:'。感謝您撰寫它。 – matt 2012-12-11 16:34:51

0

聽起來像你應該應該使用類而不是字典。你正在接近手動實現語言試圖給你的東西。

+1

是的,字典的東西是一個人爲的例子。我實際上是使用它來爲可以連接到綁定的HTML文檔製作一個視圖。你是對的,如果這就是我所做的一切,那將是一個糟糕的計劃:) – 2010-08-24 20:31:17

2

您可以使用您的建議選項的組合:

  • 使用@dynamic關鍵字
  • 覆蓋valueForKey:和setValue方法:forKey:訪問字典
  • 使用Objective-C的反射API的方法class_getProperty並檢查它是否爲零。如果不是零,你的班上就有這樣一個屬性。它不會如果是。
  • 然後在沒有這種屬性的情況下調用super方法。

我希望這有助於。可能似乎有點hacky(使用反射),但實際上這是一個非常靈活,也是絕對「合法」的解決方案的問題...

PS:的coredata的方式是可能的,但將是在你的情況是總的矯枉過正。

+0

我想將class_getProperty位添加到我的解決方案中。謝謝。 – 2010-08-24 20:32:00

2

想要一個宏嗎?這可能不是100%正確的。

#define propertyForKey(key, type) \ 
    - (void) set##key: (type) key; \ 
    - (type) key; 

#define synthesizeForKey(key, type) \ 
    - (void) set##key: (type) key \ 
    { \ 
     [dictionary setObject];// or whatever \ 
    } \ 
    - (type) key { return [dictionary objectForKey: key]; } 
相關問題