2015-11-08 72 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运行时是你的朋友。