2015-11-08 71 views
0

我想提供我自己實施的NSArray類的description方法,這樣我可以簡單地使用這樣的:如何自定義NSArray的描述?

NSLog(@"%@", @[]); 

我的想法是,爲NSArray提供一個類別,只需覆蓋description方法那裏。但它不起作用,因爲NSArray是一個類集羣,其實際類爲__NSArrayI,所以我的類別實現從不會被調用。很遺憾,我無法提供__NSArrayI的類別,因爲此類不可用。

當然,我可以繼承NSArray,在我的子類實現此方法,但同樣,由於NSArray是一類簇我必須提供一堆不同的方法實現,比如objectAtIndex:我不想這樣做是因爲這對於簡單地改變數組打印到控制檯的方式來說太過繁重。

你有什麼想法嗎?謝謝

+0

你幾乎已經經歷了選擇和缺點。我不認爲有任何其他選擇。我想你可以改變方式,但除非你能詳盡地涵蓋所有班級成員,否則你可能不得不面對不完整的報道。並記住NSMutableArray也是一個類集羣:) – Avi

+0

如果你想在特定的設計中爲特定的數組做這個,那麼寫一個方法/函數來生成數組的所有者中的描述可能是最簡單的,也就是說, (NSString *)describeMyArray :(NSArray *)array {...} – Jef

+0

爲什麼你想提供一個自定義的' description'?目標是什麼?解決這個問題可能比解決基本集合類的功能更好。 –

回答

2

你有什麼想法嗎?謝謝

我們得到的想法。解決方案...你必須決定。

documentation about format specifiers,你不能只擔心description。具體地,從該文件...

Objective-C的對象,打印爲通過 descriptionWithLocale返回的字符串:如果有的話,或以其它方式描述。另外 與CFTypeRef對象一起使用,返回 CFCopyDescription函數的結果。

除非我們想鏈接器和/或動態加載程序破解我們不能做太多關於CFTypeRef對象。

然而,我們可以做一些descriptiondescriptionWithLocale:,雖然這東西是有點粗糙。

你也可以考慮debugDescription以及。

這是一種方法來實現你的目標,雖然我認爲它是「教育」,你應該使用你最好的判斷,你是否要走這條路線。

首先,您需要確定您的替換description實現的樣子。我們將像這樣宣佈替換爲description的簡單替換(忽略原始實現)。

static NSString * swizzledDescription(id self, SEL _cmd) 
{ 
    NSUInteger count = [self count]; 
    NSMutableString *result = [NSMutableString stringWithFormat:@"Array instance (%p) of type %@ with %lu elements", (void*)self, NSStringFromClass([self class]), (unsigned long)count]; 
    int fmtLen = snprintf(0,0,"%lu",count); 
    for (NSUInteger i = 0; i < count; ++i) { 
     [result appendFormat:@"\n%p: %*lu: %@", (void*)self, fmtLen, i, self[i]]; 
    } 
    return result; 
} 

和更簡單的實現descriptionWithLocale:完全忽略了語言環境。

static NSString * swizzledDescriptionWithLocale(id self, SEL _cmd, id locale) { 
    return swizzledDescription(self, _cmd); 
} 

現在,我們該如何使NSArray實現使用這段代碼?一種方法是找到NSArray的所有子類並替換它們的方法...

static void swizzleMethod(Class class, SEL selector, IMP newImp) { 
    Method method = class_getInstanceMethod(class, selector); 
    if (method) { 
     IMP origImp = method_getImplementation(method); 
     if (origImp != newImp) { 
      method_setImplementation(method, newImp); 
     } 
    } 
} 

static void swizzleArrayDescriptions() { 
    int numClasses = objc_getClassList(NULL, 0); 
    if (numClasses <= 0) return; 
    Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses); 
    numClasses = objc_getClassList(classes, numClasses); 

    Class target = [NSArray class]; 
    for (int i = 0; i < numClasses; i++) { 
     for (Class c = classes[i]; c; c = class_getSuperclass(c)) { 
      if (c == target) { 
       c = classes[i]; 
       swizzleMethod(c, @selector(description), (IMP)swizzledDescription); 
       swizzleMethod(c, @selector(descriptionWithLocale:), (IMP)swizzledDescriptionWithLocale); 
       break; 
      } 
     } 
    } 
    free(classes); 
} 

合理的地方打電話swizzleArrayDescriptions是在應用程序委託的+initialize方法。

@implementation AppDelegate 

+ (void)initialize { 
    if (self == [AppDelegate class]) { 
     swizzleArrayDescriptions(); 
    } 
} 

現在,你應該可以玩它,看看你是如何相處的。

作爲一個非常簡單的測試......

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    // Insert code here to initialize your application 
    NSArray *array = @[@"One", @"Two", @3, @"4", @"FIVE", @(6.0), @".7.", @8, @9, @10, @"Eleven" ]; 
    NSLog(@"%@", array); 
    NSLog(@"%@", [array mutableCopy]); 
} 

產生這種輸出...

2015-11-08 14:30:45.501 TestApp[72183:25861219] Array instance (0x6000000c5780) of type __NSArrayI with 11 elements 
0x6000000c5780: 0: One 
0x6000000c5780: 1: Two 
0x6000000c5780: 2: 3 
0x6000000c5780: 3: 4 
0x6000000c5780: 4: FIVE 
0x6000000c5780: 5: 6 
0x6000000c5780: 6: .7. 
0x6000000c5780: 7: 8 
0x6000000c5780: 8: 9 
0x6000000c5780: 9: 10 
0x6000000c5780: 10: Eleven 
2015-11-08 14:30:45.501 TestApp[72183:25861219] Array instance (0x600000045580) of type __NSArrayM with 11 elements 
0x600000045580: 0: One 
0x600000045580: 1: Two 
0x600000045580: 2: 3 
0x600000045580: 3: 4 
0x600000045580: 4: FIVE 
0x600000045580: 5: 6 
0x600000045580: 6: .7. 
0x600000045580: 7: 8 
0x600000045580: 8: 9 
0x600000045580: 9: 10 
0x600000045580: 10: Eleven 

當然,你應該做的比我更多的測試,因爲我所做的就是黑客這在教堂之後,因爲它似乎有點有趣(下雨,所以野餐被取消)。

如果您需要調用原始實現,您將需要創建原始實現的緩存,並按類別鍵入,並相應地調用它們。然而,對於這樣的情況,如果你想修改返回的字符串,你可能不需要這樣做,並且它在任何情況下都應該是直截了當的。

此外,請注意關於混合遊戲的正常注意事項,並且在玩類集羣時它們會更高一些。

注意,你也可以做這樣的事情來在運行時創建自定義的子類。你甚至可以用正常的方式將你的子類定義爲NSArray的直接子類,然後調整它們的類型而不影響任何不屬於你的類......或者一堆不同的東西......記住ObjectiveC運行時是你的朋友。