Skip to content

Instantly share code, notes, and snippets.

@ourui
Created May 26, 2016 01:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ourui/e28f5e4ad385a849fcef588be6c788e1 to your computer and use it in GitHub Desktop.
Save ourui/e28f5e4ad385a849fcef588be6c788e1 to your computer and use it in GitHub Desktop.
//
// NSObject+DLIntrospection.h
// DLIntrospection
//
// Created by Denis Lebedev on 12/27/12.
// Copyright (c) 2012 Denis Lebedev. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (DLIntrospection)
+ (NSArray *)classes;
+ (NSArray *)properties;
+ (NSArray *)instanceVariables;
+ (NSArray *)classMethods;
+ (NSArray *)instanceMethods;
+ (NSArray *)protocols;
+ (NSDictionary *)descriptionForProtocol:(Protocol *)proto;
+ (NSString *)parentClassHierarchy;
@end
//
// NSObject+DLIntrospection.m
// DLIntrospection
//
// Created by Denis Lebedev on 12/27/12.
// Copyright (c) 2012 Denis Lebedev. All rights reserved.
//
#import "NSObject+DLIntrospection.h"
#import <objc/runtime.h>
@interface NSString (DLIntrospection)
+ (NSString *)decodeType:(const char *)cString;
@end
@implementation NSString (DLIntrospection)
//https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
+ (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";
//@TODO: do handle bitmasks
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
static void getSuper(Class class, NSMutableString *result) {
[result appendFormat:@" -> %@", NSStringFromClass(class)];
if ([class superclass]) { getSuper([class superclass], result); }
}
@implementation NSObject (DLIntrospection)
+ (NSArray *)classes {
unsigned int classesCount;
Class *classes = objc_copyClassList(&classesCount);
NSMutableArray *result = [NSMutableArray array];
for (int i = 0 ; i < classesCount; i++) {
[result addObject:NSStringFromClass(classes[i])];
}
return [result sortedArrayUsingSelector:@selector(compare:)];
}
+ (NSArray *)classMethods {
return [self methodsForClass:object_getClass([self class]) typeFormat:@"+"];
}
+ (NSArray *)instanceMethods {
return [self methodsForClass:[self class] typeFormat:@"-"];
}
+ (NSArray *)properties {
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;
}
+ (NSArray *)instanceVariables {
unsigned int outCount;
Ivar *ivars = class_copyIvarList([self class], &outCount);
NSMutableArray *result = [NSMutableArray array];
for (int i = 0; i < outCount; i++) {
NSString *type = [NSString decodeType:ivar_getTypeEncoding(ivars[i])];
NSString *name = [NSString stringWithCString:ivar_getName(ivars[i]) encoding:NSUTF8StringEncoding];
NSString *ivarDescription = [NSString stringWithFormat:@"%@ %@", type, name];
[result addObject:ivarDescription];
}
free(ivars);
return result.count ? [result copy] : nil;
}
+ (NSArray *)protocols {
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;
}
+ (NSDictionary *)descriptionForProtocol:(Protocol *)proto {
NSMutableDictionary *methodsAndProperties = [NSMutableDictionary dictionary];
NSArray *requiredMethods = [[[self class] formattedMethodsForProtocol:proto required:YES instance:NO] arrayByAddingObjectsFromArray:[[self class]formattedMethodsForProtocol:proto required:YES instance:YES]];
NSArray *optionalMethods = [[[self class] formattedMethodsForProtocol:proto required:NO instance:NO] arrayByAddingObjectsFromArray:[[self class]formattedMethodsForProtocol:proto required:NO instance:YES]];
unsigned int propertiesCount;
NSMutableArray *propertyDescriptions = [NSMutableArray array];
objc_property_t *properties = protocol_copyPropertyList(proto, &propertiesCount);
for (int i = 0; i < propertiesCount; i++) {
[propertyDescriptions addObject:[self formattedPropery:properties[i]]];
}
if (requiredMethods.count) {
[methodsAndProperties setObject:requiredMethods forKey:@"@required"];
}
if (optionalMethods.count) {
[methodsAndProperties setObject:optionalMethods forKey:@"@optional"];
} if (propertyDescriptions.count) {
[methodsAndProperties setObject:[propertyDescriptions copy] forKey:@"@properties"];
}
free(properties);
return methodsAndProperties.count ? [methodsAndProperties copy ] : nil;
}
+ (NSString *)parentClassHierarchy {
NSMutableString *result = [NSMutableString string];
getSuper([self class], result);
return result;
}
#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 ? @"-" : @"+",
#warning return correct type
@"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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment