Skip to content

Instantly share code, notes, and snippets.

@rsms
Created February 26, 2015 00:34
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 rsms/eccb008ddad25284f9c7 to your computer and use it in GitHub Desktop.
Save rsms/eccb008ddad25284f9c7 to your computer and use it in GitHub Desktop.
class-dump is awesome, but sometimes it crashes (i.e. when trying to dump WebKit.dylib) and sometimes you need some programmatic action. Really hacky and incomplete "class dump"
#import <objc/runtime.h>
static NSString* ObjcTypeToCode(const char* t) {
switch (*t) {
case 'c': return @"BOOL"; // A char
case 'i': return @"int"; // An int
case 's': return @"short"; // A short
case 'l': return @"int32_t"; // A long (l is treated as a 32-bit quantity on 64-bit programs)
case 'q': return @"int64_t"; // A long long
case 'C': return @"unsigned char"; // An unsigned char
case 'I': return @"unsigned int"; // An unsigned int
case 'S': return @"unsigned short"; // An unsigned short
case 'L': return @"uint32_t"; // An unsigned long
case 'Q': return @"uint64_t"; // An unsigned long long
case 'f': return @"float"; // A float
case 'd': return @"double"; // A double
case 'B': return @"bool"; // A C++ bool or a C99 _Bool
case 'v': return @"void"; // A void
case '*': return @"char*"; // A character string (char *)
case '@': return @"id"; // An object (whether statically typed or typed id)
case '#': return @"Class"; // A class object (Class)
case ':': return @"SEL"; // A method selector (SEL)
//case '[': //return @"[array type]"; // An array
//case '{': //return @"{name=type...}"; // A structure
//case '(': //return @"(name=type...)"; // A union
//case 'b': //return @"bnum" // A bit field of num bits
//case '^': return @"^type"; // A pointer to type
case '?': return @"?"; // An unknown type (among other things, this code is used for function pointers)
default: return [NSString stringWithUTF8String:t];
}
}
static NSString* ObjcAttrToCode(const char* t, BOOL isPropertyDelc) {
switch (*t) {
case 'R': return @"readonly"; // The property is read-only (readonly).
case 'C': return @"copy"; // The property is a copy of the value last assigned (copy).
case '&': return @"retain"; // The property is a reference to the value last assigned (retain).
case 'N': return @"nonatomic"; // The property is non-atomic (nonatomic).
case 'G': // The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,).
return [NSString stringWithFormat:@"getter=%s", &t[1]];
case 'S': // The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,).
return [NSString stringWithFormat:@"setter=%s", &t[1]];
case 'D': return isPropertyDelc ? @"dynamic" : @"@dynamic"; // The property is dynamic (@dynamic).
case 'W': return isPropertyDelc ? @"weak" : @"__weak"; // The property is a weak reference (__weak).
case 'P': return @"<meta:gc-enabled>"; // The property is eligible for garbage collection.
case 't': // Specifies the type using old-style encoding.
return [NSString stringWithFormat:@"<old-style encoding=%s>", &t[1]];
default:
return [NSString stringWithUTF8String:t];
}
}
static void ghetto_class_dump(Class cls) {
@autoreleasepool {
const char* clsName = class_getName(cls);
Class superCls = class_getSuperclass(cls);
if (superCls == NULL) {
printf("@interface %s", clsName);
} else {
printf("@interface %s : %s", clsName, class_getName(superCls));
}
// Instance variables
unsigned int ic = 0;
Ivar* iv = class_copyIvarList(cls, &ic);
if (ic == 0) {
printf("\n");
} else {
printf(" {\n");
for (unsigned int i = 0; i != ic; i++) {
auto name = ivar_getName(iv[i]);
auto offs = ivar_getOffset(iv[i]);
auto t = ivar_getTypeEncoding(iv[i]);
printf(" %s %s; // offset=%lu\n", ObjcTypeToCode(t).UTF8String, name, offs);
}
printf("}\n");
}
free(iv);
// Properties
unsigned int pc = 0;
objc_property_t* pv = class_copyPropertyList(cls, &pc);
for (unsigned int i = 0; i != pc; i++) {
auto p = pv[i];
auto name = property_getName(p);
unsigned int attrc = 0;
objc_property_attribute_t* attrv = property_copyAttributeList(p, &attrc);
NSString* Type = @"?";
auto Attrs = [NSMutableArray new];
if (attrc != 0) {
Type = ObjcTypeToCode(attrv[0].value);
for (unsigned int ai = 1; ai < attrc; ai++) {
[Attrs addObject:[NSString stringWithFormat:@"%@%s", ObjcAttrToCode(attrv[ai].name, YES), attrv[ai].value]];
}
}
printf("@property(%s) %s %s;\n",
[Attrs componentsJoinedByString:@", "].UTF8String, Type.UTF8String, name);
}
free(pv);
// Methods
unsigned int mc = 0;
Method* mv = class_copyMethodList(cls, &mc);
for (unsigned int i = 0; i != mc; i++) {
struct objc_method_description* descr = method_getDescription(mv[i]);
if (descr == NULL) { continue; }
const char* name = sel_getName(descr->name);
unsigned int argc = method_getNumberOfArguments(mv[i]); // minimum 2 (cls, self)
char* rtype = method_copyReturnType(mv[i]);
auto* Namev = [[NSString stringWithUTF8String:name] componentsSeparatedByString:@":"];
auto* Codev = [NSMutableArray new];
if (argc <= 2) {
[Codev addObject:Namev[0]];
} else {
for (unsigned int argi = 0; argi < argc-2; argi++) {
char argt[10];
method_getArgumentType(mv[i], argi+2, argt, sizeof(argt));
NSString* Param = Namev[argi];
NSString* Arg = @"_";
if (argi != 0) {
Arg = Param;
}
[Codev addObject:[NSString stringWithFormat:@"%@:(%@)%@", Param, ObjcTypeToCode(argt), Arg]];
}
}
printf("%s\n", [NSString stringWithFormat:@"-(%@)%@;", ObjcTypeToCode(rtype), [Codev componentsJoinedByString:@" "]].UTF8String);
free(rtype);
}
free(mv);
printf("@end // %s\n", clsName);
}
}
static void __attribute__((constructor)) __init() {
ghetto_class_dump([WebView class]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment