各位,你们有什么想法吗?谢谢
我们得到的想法。解决方案...您必须做出决定。
从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
当然,你应该比我做更多的测试,因为我所做的只是在教堂结束后修改它,因为它看起来有些有趣(正在下雨,所以野餐被取消了)。
如果您需要调用原始实现,则需要创建原始实现的缓存,以类为键,并相应地调用它们。但是,对于这样的情况,您想要修改返回的字符串,您可能不需要这样做,而且无论如何都应该是直截了当的。
另外,请注意关于 swizzling 的一般注意事项,在使用类集群时它们会更加突出。
注意,您也可以这样做以在运行时创建自定义子类。您甚至可以以正常方式将您的子类定义为 NSArray 的直接子类,然后在不影响任何不属于您的类的情况下调整它们的类型......或一堆不同的东西......记住 ObjectiveC 运行时是你的朋友。