你有什么想法吗?谢谢
我们得到的想法。解决方案...你必须决定。
从documentation about format specifiers,你不能只担心description
。具体地,从该文件...
Objective-C的对象,打印为通过 descriptionWithLocale返回的字符串:如果有的话,或以其它方式描述。另外 与CFTypeRef对象一起使用,返回 CFCopyDescription函数的结果。
除非我们想链接器和/或动态加载程序破解我们不能做太多关于CFTypeRef
对象。
然而,我们可以做一些约和description
descriptionWithLocale:
,虽然这东西是有点粗糙。
你也可以考虑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运行时是你的朋友。
你几乎已经经历了选择和缺点。我不认为有任何其他选择。我想你可以改变方式,但除非你能详尽地涵盖所有班级成员,否则你可能不得不面对不完整的报道。并记住NSMutableArray也是一个类集群:) – Avi
如果你想在特定的设计中为特定的数组做这个,那么写一个方法/函数来生成数组的所有者中的描述可能是最简单的,也就是说, (NSString *)describeMyArray :(NSArray *)array {...} – Jef
为什么你想提供一个自定义的' description'?目标是什么?解决这个问题可能比解决基本集合类的功能更好。 –