通过runtime获取对象相关信息
通过runtime获取对象相关信息
在这里,本人给大家提供一个runtime关于NSObject的扩展,用来显示各种NSObject中的信息,这有助于你来分析类的组成:)
先准备以下类供测试:
Model.h 与 Model.m
// // Model.h // Runtime // // Copyright (c) 2014年 Y.X. All rights reserved. // #import <Foundation/Foundation.h> typedef enum : NSUInteger { male, female, } ModelSex; @interface Model : NSObject @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSNumber *age; @property (nonatomic, assign) ModelSex sex; - (void)info; + (void)className; @end
// // Model.m // Runtime // // Copyright (c) 2014年 Y.X. All rights reserved. // #import "Model.h" @implementation Model - (void)info { NSLog(@"info"); } + (void)className { NSLog(@"Model"); } @end
以下是NSObject关于runtime的扩展category
NSObject+Runtime.h 与 NSObject+Runtime.m
// // NSObject+Runtime.h // Runtime // // Copyright (c) 2014年 Y.X. All rights reserved. // #import <Foundation/Foundation.h> @interface NSObject (Runtime) /* ------------------------------------ */ // 获取当前类所有的子类 + (NSArray *)runtimeSubClasses; - (NSArray *)runtimeSubClasses; // 获取当前类所有的父类继承关系 + (NSString *)runtimeParentClassHierarchy; - (NSString *)runtimeParentClassHierarchy; /* ------------------------------------ */ /* ------------------------------------ */ // 获取当前类类方法 + (NSArray *)runtimeClassMethods; - (NSArray *)runtimeClassMethods; // 获取当前类实例方法 + (NSArray *)runtimeInstanceMethods; - (NSArray *)runtimeInstanceMethods; /* ------------------------------------ */ /* ------------------------------------ */ // 获取当前类实例变量大小 + (size_t)runtimeInstanceSize; - (size_t)runtimeInstanceSize; // 获取当前类的所有属性 + (NSArray *)runtimeProperties; - (NSArray *)runtimeProperties; /* ------------------------------------ */ // 获取当前类继承的所有协议 + (NSArray *)runtimeProtocols; - (NSArray *)runtimeProtocols; @end
// // NSObject+Runtime.m // Runtime // // Copyright (c) 2014年 Y.X. All rights reserved. // #import "NSObject+Runtime.h" #import <objc/runtime.h> static void getSuper(Class class, NSMutableString *result) { [result appendFormat:@" -> %@", NSStringFromClass(class)]; if ([class superclass]) { getSuper([class superclass], result); } } @interface NSString (Runtime) + (NSString *)decodeType:(const char *)cString; @end @implementation NSString (Runtime) + (NSString *)decodeType:(const char *)cString { if (!strcmp(cString, @encode(id))) return @"id"; if (!strcmp(cString, @encode(void))) return @"void"; if (!strcmp(cString, @encode(float))) return @"float"; if (!strcmp(cString, @encode(int))) return @"int"; if (!strcmp(cString, @encode(BOOL))) return @"BOOL"; if (!strcmp(cString, @encode(char *))) return @"char *"; if (!strcmp(cString, @encode(double))) return @"double"; if (!strcmp(cString, @encode(Class))) return @"class"; if (!strcmp(cString, @encode(SEL))) return @"SEL"; if (!strcmp(cString, @encode(unsigned int))) return @"unsigned int"; NSString *result = [NSString stringWithCString:cString encoding:NSUTF8StringEncoding]; if ([[result substringToIndex:1] isEqualToString:@"@"] && [result rangeOfString:@"?"].location == NSNotFound) { result = [[result substringWithRange:NSMakeRange(2, result.length - 3)] stringByAppendingString:@"*"]; } else if ([[result substringToIndex:1] isEqualToString:@"^"]) { result = [NSString stringWithFormat:@"%@ *", [NSString decodeType:[[result substringFromIndex:1] cStringUsingEncoding:NSUTF8StringEncoding]]]; } return result; } @end @implementation NSObject (Runtime) - (NSArray *)runtimeProperties { return [[self class] runtimeProperties]; } + (NSArray *)runtimeProperties { unsigned int outCount; objc_property_t *properties = class_copyPropertyList([self class], &outCount); NSMutableArray *result = [NSMutableArray array]; for (int i = 0; i < outCount; i++) { [result addObject:[self formattedPropery:properties[i]]]; } free(properties); return result.count ? [result copy] : nil; } - (NSString *)runtimeParentClassHierarchy { return [[self class] runtimeParentClassHierarchy]; } + (NSString *)runtimeParentClassHierarchy { NSMutableString *result = [NSMutableString string]; getSuper([self class], result); return result; } - (NSArray *)runtimeSubClasses { return [[self class] runtimeSubClasses]; } + (NSArray *)runtimeSubClasses { Class *buffer = NULL; int count, size; do { count = objc_getClassList(NULL, 0); buffer = (Class *)realloc(buffer, count * sizeof(*buffer)); size = objc_getClassList(buffer, count); } while(size != count); NSMutableArray *array = [NSMutableArray array]; for(int i = 0; i < count; i++) { Class candidate = buffer[i]; Class superclass = candidate; while(superclass) { if(superclass == self) { [array addObject: candidate]; break; } superclass = class_getSuperclass(superclass); } } free(buffer); return array; } - (size_t)runtimeInstanceSize { return [[self class] runtimeInstanceSize]; } + (size_t)runtimeInstanceSize { return class_getInstanceSize(self); } - (NSArray *)runtimeClassMethods { return [[self class] runtimeClassMethods]; } + (NSArray *)runtimeClassMethods { return [self methodsForClass:object_getClass([self class]) typeFormat:@"+"]; } - (NSArray *)runtimeInstanceMethods { return [[self class] runtimeInstanceMethods]; } + (NSArray *)runtimeInstanceMethods { return [self methodsForClass:[self class] typeFormat:@"-"]; } - (NSArray *)runtimeProtocols { return [[self class] runtimeProtocols]; } + (NSArray *)runtimeProtocols { unsigned int outCount; Protocol * const *protocols = class_copyProtocolList([self class], &outCount); NSMutableArray *result = [NSMutableArray array]; for (int i = 0; i < outCount; i++) { unsigned int adoptedCount; Protocol * const *adotedProtocols = protocol_copyProtocolList(protocols[i], &adoptedCount); NSString *protocolName = [NSString stringWithCString:protocol_getName(protocols[i]) encoding:NSUTF8StringEncoding]; NSMutableArray *adoptedProtocolNames = [NSMutableArray array]; for (int idx = 0; idx < adoptedCount; idx++) { [adoptedProtocolNames addObject:[NSString stringWithCString:protocol_getName(adotedProtocols[idx]) encoding:NSUTF8StringEncoding]]; } NSString *protocolDescription = protocolName; if (adoptedProtocolNames.count) { protocolDescription = [NSString stringWithFormat:@"%@ <%@>", protocolName, [adoptedProtocolNames componentsJoinedByString:@", "]]; } [result addObject:protocolDescription]; //free(adotedProtocols); } //free((__bridge void *)(*protocols)); return result.count ? [result copy] : nil; } #pragma mark - Private + (NSArray *)methodsForClass:(Class)class typeFormat:(NSString *)type { unsigned int outCount; Method *methods = class_copyMethodList(class, &outCount); NSMutableArray *result = [NSMutableArray array]; for (int i = 0; i < outCount; i++) { NSString *methodDescription = [NSString stringWithFormat:@"%@ (%@)%@", type, [NSString decodeType:method_copyReturnType(methods[i])], NSStringFromSelector(method_getName(methods[i]))]; NSInteger args = method_getNumberOfArguments(methods[i]); NSMutableArray *selParts = [[methodDescription componentsSeparatedByString:@":"] mutableCopy]; NSInteger offset = 2; //1-st arg is object (@), 2-nd is SEL (:) for (int idx = offset; idx < args; idx++) { NSString *returnType = [NSString decodeType:method_copyArgumentType(methods[i], idx)]; selParts[idx - offset] = [NSString stringWithFormat:@"%@:(%@)arg%d", selParts[idx - offset], returnType, idx - 2]; } [result addObject:[selParts componentsJoinedByString:@" "]]; } free(methods); return result.count ? [result copy] : nil; } + (NSArray *)formattedMethodsForProtocol:(Protocol *)proto required:(BOOL)required instance:(BOOL)instance { unsigned int methodCount; struct objc_method_description *methods = protocol_copyMethodDescriptionList(proto, required, instance, &methodCount); NSMutableArray *methodsDescription = [NSMutableArray array]; for (int i = 0; i < methodCount; i++) { [methodsDescription addObject: [NSString stringWithFormat:@"%@ (%@)%@", instance ? @"-" : @"+", @"void", NSStringFromSelector(methods[i].name)]]; } free(methods); return [methodsDescription copy]; } + (NSString *)formattedPropery:(objc_property_t)prop { unsigned int attrCount; objc_property_attribute_t *attrs = property_copyAttributeList(prop, &attrCount); NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; for (int idx = 0; idx < attrCount; idx++) { NSString *name = [NSString stringWithCString:attrs[idx].name encoding:NSUTF8StringEncoding]; NSString *value = [NSString stringWithCString:attrs[idx].value encoding:NSUTF8StringEncoding]; [attributes setObject:value forKey:name]; } free(attrs); NSMutableString *property = [NSMutableString stringWithFormat:@"@property "]; NSMutableArray *attrsArray = [NSMutableArray array]; //https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5 [attrsArray addObject:[attributes objectForKey:@"N"] ? @"nonatomic" : @"atomic"]; if ([attributes objectForKey:@"&"]) { [attrsArray addObject:@"strong"]; } else if ([attributes objectForKey:@"C"]) { [attrsArray addObject:@"copy"]; } else if ([attributes objectForKey:@"W"]) { [attrsArray addObject:@"weak"]; } else { [attrsArray addObject:@"assign"]; } if ([attributes objectForKey:@"R"]) {[attrsArray addObject:@"readonly"];} if ([attributes objectForKey:@"G"]) { [attrsArray addObject:[NSString stringWithFormat:@"getter=%@", [attributes objectForKey:@"G"]]]; } if ([attributes objectForKey:@"S"]) { [attrsArray addObject:[NSString stringWithFormat:@"setter=%@", [attributes objectForKey:@"G"]]]; } [property appendFormat:@"(%@) %@ %@", [attrsArray componentsJoinedByString:@", "], [NSString decodeType:[[attributes objectForKey:@"T"] cStringUsingEncoding:NSUTF8StringEncoding]], [NSString stringWithCString:property_getName(prop) encoding:NSUTF8StringEncoding]]; return [property copy]; } @end
以下是使用情形:
// // AppDelegate.m // Runtime // // Copyright (c) 2014年 Y.X. All rights reserved. // #import "AppDelegate.h" #import "NSObject+Runtime.h" #import "Model.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSLog(@"%@", [Model runtimeClassMethods]); NSLog(@"%@", [Model runtimeInstanceMethods]); NSLog(@"%@", [Model runtimeProperties]); NSLog(@"%@", [Model runtimeParentClassHierarchy]); NSLog(@"%@", [Model runtimeSubClasses]); return YES; } @end
打印信息如下:
Runtime[43597:60b] (
"+ (void)className"
)
Runtime[43597:60b] (
"- (id)age",
"- (void)setAge:(id)arg0 ",
"- (unsigned int)sex",
"- (void)setSex:(unsigned int)arg0 ",
"- (void)setName:(id)arg0 ",
"- (void).cxx_destruct",
"- (id)name",
"- (void)info"
)
Runtime[43597:60b] (
"@property (nonatomic, strong) NSString* name",
"@property (nonatomic, strong) NSNumber* age",
"@property (nonatomic, assign) unsigned int sex"
)
Runtime[43597:60b] -> Model -> NSObject
Runtime[43597:60b] (
Model
)
以下是一些注意一点的地方:
每一个类均有一个类方法和实例变量方法相互对应