在Objective-C中,通过打印对象ID和实例变量名称/值的方法来覆盖-description是很常见的.我想制作一个通用的描述方法,通过内省来做,而不是为每个类手动这样做.我希望输出如下:
<ClassName: 0x??????,ivar1: value1,ivar2: value2,ivar3: value3,...>
对实例变量名进行排序也是很好的(所以它们总是按照相同的顺序排列).最后,如果这可以安全地覆盖NSObject功能,那将是完美的(但是我看到这并不简单,因为NSObject.m有一个关于在-description中使用 – [NSString stringWithFormat:]的警告).
解决方法
这是一个很好的问题!因为我没有找到这样的东西,我写了一个小功能,为你做这个.它使用方法swizzling替换(NSString *)NSObject的描述(是的,我是这个邪恶的MAHAHAHAHAHAHAHAHAHAHAHAHAHA),并按照它们也出现在类中的顺序打印出ivar(您可以轻松地编辑它们以字母顺序显示) .
别!忘了调用NSObjectSwizzleDescription()!
.h文件:
@interface NSObject (JSObjectAdditions) @end void NSObjectSwizzleDescription();
.m文件:
#import <objc/objc.h> #import "JSObject.h" @implementation NSObject (JSObjectAdditions) - (NSString *)verboseDescription { NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: %p>",NSStringFromClass([self class]),self]; uint32_t ivarCount; Ivar *ivars = class_copyIvarList([self class],&ivarCount); if(ivars) { [description appendString:@"\n{"]; for(uint32_t i=0; i<ivarCount; i++) { Ivar ivar = ivars[i]; const char *ivarType = ivar_getTypeEncoding(ivar); id ivarObject = object_getIvar(self,ivar); [description appendFormat:@"\n %s: ",ivar_getName(ivar)]; // Default signed data types if(strcmp(ivarType,"c") == 0) { char character = (char)ivarObject; [description appendFormat:@"'%c'",character]; } else if(strcmp(ivarType,"i") == 0 || strcmp(ivarType,"l") == 0) // l is also 32 bit in the 64 bit runtime environment { int integer = (int)ivarObject; [description appendFormat:@"%i",integer]; } else if(strcmp(ivarType,"s") == 0) { short shortVal = (short)ivarObject; [description appendFormat:@"%i",(int)shortVal]; } else if(strcmp(ivarType,"q") == 0) { long long longVal = (long long)ivarObject; [description appendFormat:@"%l",longVal]; } // Default unsigned data types else if(strcmp(ivarType,"C") == 0) { unsigned char chracter = (unsigned char)ivarObject; [description appendFormat:@"'%c'",chracter]; } else if(strcmp(ivarType,"I") == 0 || strcmp(ivarType,"L") == 0) { unsigned int integer = (unsigned int)ivarObject; [description appendFormat:@"%u","S") == 0) { unsigned short shortVal = (unsigned short)ivarObject; [description appendFormat:@"%i","Q") == 0) { unsigned long long longVal = (unsigned long long)ivarObject; [description appendFormat:@"%ll",longVal]; } // Floats'n'stuff else if(strcmp(ivarType,"f") == 0) { float floatVal; memcpy(&floatVal,&ivarObject,sizeof(float)); [description appendFormat:@"%f",floatVal]; } else if(strcmp(ivarType,"d") == 0) { double doubleVal; memcpy(&doubleVal,sizeof(double)); [description appendFormat:@"%f",doubleVal]; } // Boolean and pointer else if(strcmp(ivarType,"B") == 0) { BOOL booleanVal = (BOOL)ivarObject; [description appendFormat:@"%@",(booleanVal ? @"YES" : @"NO")]; } else if(strcmp(ivarType,"v") == 0) { void *pointer = (void *)ivarObject; [description appendFormat:@"%p",pointer]; } else if(strcmp(ivarType,"*") == 0 || strcmp(ivarType,":") == 0) // SEL is just a typecast for a cstring { char *cstring = (char *)ivarObject; [description appendFormat:@"\"%s\"",cstring]; } else if(strncmp(ivarType,"@",1) == 0) { [description appendFormat:@"%@",ivarObject]; } else if(strcmp(ivarType,"#") == 0) { Class objcClass = (Class)ivarObject; [description appendFormat:@"%s",class_getName(objcClass)]; } else [description appendString:@"???"]; } [description appendString:@"\n}"]; free(ivars); } return description; } @end void NSObjectSwizzleDescription() { Method origMethod = class_getInstanceMethod([NSObject class],@selector(description)); Method newMethod = class_getInstanceMethod([NSObject class],@selector(verboseDescription)); method_exchangeImplementations(origMethod,newMethod); }