Skip to content

Instantly share code, notes, and snippets.

@markd2
Created July 9, 2013 20:55
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save markd2/5961219 to your computer and use it in GitHub Desktop.
Save markd2/5961219 to your computer and use it in GitHub Desktop.
Objective-C runtime metadata dumper.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "typestring.h"
// clang -g -fobjc-arc -Wall -framework Foundation -o runtime typestring.m runtime.m
// Runtime reference, at least until Apple breaks the link
// http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html
static NSString *ReadableTypeString (NSString *chunk);
@protocol XXSnork <NSObject>
@required
- (void) somethingRequired: (NSString *) blorp;
+ (CGRect) classicallyRequired: (short) fuse;
@optional
+ (void) classicallyOptional: (NSAffineTransform *) robotsInTheSkies;
- (void) somethingOptional1;
- (void) somethingOptional2;
@end // XXSnork
@interface XXStuff : NSObject <XXSnork>
@property (assign, nonatomic) CGRect bounds;
@property (assign, nonatomic) CGRect bounds2;
@property (assign, nonatomic) CGRect *boundsPointer;
@property (weak, setter=getBlah:, getter=setBlah) NSString *blah;
@property (assign, nonatomic) unsigned long long shoeSize;
@property (weak, nonatomic) id <NSObject> delegate;
@property (strong) NSURL *mikeysPonyFarm;
@property (assign, nonatomic) CGFloat gilliganFactor;
@property (copy) void (^ook)(void);
@property (copy) void (^blockHead)(NSFileHandle *);
- (void) blahBlahBlah: (id<NSObject>) ook;
- (void) downloadFanFicWithCompletion: (BOOL (^) (NSURL *url, NSError *error)) completion;
- (NSString *) blahWithBitmapDataPlanes: (unsigned char **) planes
pixelsWide: (NSInteger) width
pixelsHigh: (NSInteger) height
bitsPerSample: (NSInteger) bps
samplesPerPixel: (NSInteger) spp
hasAlpha: (BOOL) alpha
isPlanar: (BOOL) isPlanar
colorSpaceName: (NSString *) colorSpaceName
bytesPerRow: (NSInteger) rBytes
bitsPerPixel: (NSInteger) pBits;
@end // XXStuff
@interface XXStuff (Category)
- (void) categoriesRUs;
@end // XXStuff
@implementation XXStuff
@dynamic bounds;
@synthesize blah = _scoobyDoobyDoo;
+ (void) aClassMethod { }
+ (CGRect) classicallyRequired: (short) fuse { return (CGRect){ {1, 2}, {3, 4} }; }
+ (void) classicallyOptional: (NSAffineTransform *) robotsInTheSkies { }
- (void) anInstanceMethod { }
- (void) greebleBork { }
- (void) somethingRequired: (NSString *) blorp { }
- (void) blahBlahBlah: (id<NSObject>) ook { }
- (void) downloadFanFicWithCompletion: (BOOL (^) (NSURL *url, NSError *error)) completion { }
- (NSString *) blahWithBitmapDataPlanes: (unsigned char **) planes
pixelsWide: (NSInteger) width
pixelsHigh: (NSInteger) height
bitsPerSample: (NSInteger) bps
samplesPerPixel: (NSInteger) spp
hasAlpha: (BOOL) alpha
isPlanar: (BOOL) isPlanar
colorSpaceName: (NSString *) colorSpaceName
bytesPerRow: (NSInteger) rBytes
bitsPerPixel: (NSInteger) pBits {
return @"this method name is too damn long!";
}
@end // XXStuff
static void ListClasses (void) {
#define OLD_SCHOOL 1
#if OLD_SCHOOL
unsigned int classCount = objc_getClassList (NULL, 0);
Class *classes = (__unsafe_unretained Class *) malloc (sizeof(Class) * classCount);
unsigned int newClassCount = objc_getClassList (classes, classCount);
assert (classCount == newClassCount);
#else
unsigned int classCount;
Class *classes = objc_copyClassList (&classCount);
#endif
// Sort by name
qsort_b (classes, classCount, sizeof(Class),
^(const void *thing1, const void *thing2) {
return strcmp(class_getName(*((Class *)thing1)),
class_getName(*((Class *)thing2)));
});
printf ("Got %d classes\n", classCount);
for (int i = 0; i < classCount; i++) {
const char *name = class_getName (classes[i]);
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList (classes[i], &propertyCount);
free (properties);
unsigned int ivarCount;
Ivar *ivars = class_copyIvarList (classes[i], &ivarCount);
free (ivars);
unsigned int protocolCount;
Protocol * __unsafe_unretained *protocols = class_copyProtocolList (classes[i], &protocolCount);
free (protocols);
unsigned int instanceMethodCount;
unsigned int classMethodCount;
Method *instanceMethods = class_copyMethodList (classes[i], &instanceMethodCount);
Method *classMethods = class_copyMethodList (object_getClass(classes[i]), &classMethodCount);
free (instanceMethods);
free (classMethods);
printf (" %d: %s, %d properties, %d ivars, %d protocols, "
"%d instance methods, %d class methods\n",
i, name, propertyCount, ivarCount, protocolCount,
instanceMethodCount, classMethodCount);
}
free (classes);
} // ListClasses
static void ListProtocols (void) {
unsigned int protocolCount;
Protocol *__unsafe_unretained *protocols = objc_copyProtocolList (&protocolCount);
printf ("Got %d protocols\n", protocolCount);
for (int i = 0; i < protocolCount; i++) {
const char *protocolName = protocol_getName (protocols[i]);
unsigned int adopteeCount;
Protocol * __unsafe_unretained *adoptees =
protocol_copyProtocolList (protocols[i], &adopteeCount);
free (adoptees);
struct objc_method_description *methods;
unsigned int count;
unsigned int requiredCount = 0;
unsigned int optionalCount = 0;
methods = protocol_copyMethodDescriptionList (protocols[i], YES, YES, &count);
requiredCount += count;
free (methods);
methods = protocol_copyMethodDescriptionList (protocols[i], YES, NO, &count);
requiredCount += count;
free (methods);
methods = protocol_copyMethodDescriptionList (protocols[i], NO, YES, &count);
optionalCount += count;
free (methods);
methods = protocol_copyMethodDescriptionList (protocols[i], NO, NO, &count);
optionalCount += count;
free (methods);
printf (" %d: %s, %d adoptees, %d required, %d optional\n",
i, protocolName, adopteeCount, requiredCount, optionalCount);
}
free (protocols);
} // ListProtocols
static NSString *ReadableTypeString (NSString *typestring) {
NSArray *chunks = ParseTypeString (typestring);
NSString *result = [chunks componentsJoinedByString: @", "];
return result;
} // ReadableTypeString
static const char *ReadablePropertyAttributes (const char *attributeCString) {
NSString *attributeString = @( attributeCString );
NSArray *chunks = [attributeString componentsSeparatedByString: @","];
NSMutableArray *translatedChunks = [NSMutableArray arrayWithCapacity: chunks.count];
NSString *string;
for (NSString *chunk in chunks) {
unichar first = [chunk characterAtIndex: 0];
switch (first) {
case 'T': // encode type. @ has class name after it
string = ReadableTypeString ([chunk substringFromIndex: 1]);
[translatedChunks addObject: string];
break;
case 'V': // backing ivar name
string = [NSString stringWithFormat: @"ivar: %@",
[chunk substringFromIndex: 1]];
[translatedChunks addObject: string];
break;
case 'R': // read-only
[translatedChunks addObject: @"readonly"];
break;
case 'C': // copy
[translatedChunks addObject: @"copy"];
break;
case '&': // retain
[translatedChunks addObject: @"retain/strong"];
break;
case 'N': // non-atomic
[translatedChunks addObject: @"non-atomic"];
break;
case 'G': // custom getter
string = [NSString stringWithFormat: @"getter: %@",
[chunk substringFromIndex: 1]];
[translatedChunks addObject: string];
break;
case 'S': // custom setter
string = [NSString stringWithFormat: @"setter: %@",
[chunk substringFromIndex: 1]];
[translatedChunks addObject: string];
break;
case 'D': // dynamic
[translatedChunks addObject: @"dynamic"];
break;
case 'W': // weak
[translatedChunks addObject: @"weak"];
break;
case 'P': // eligible for GC
[translatedChunks addObject: @"GC"];
break;
case 't': // old-style encoding
[translatedChunks addObject: chunk];
break;
default:
[translatedChunks addObject: chunk];
break;
}
}
NSString *result = [translatedChunks componentsJoinedByString: @", "];
return [result UTF8String];
} // ReadablePropertyAttributes
static void ClassInfo (const char *classname) {
Class classy = objc_getClass (classname);
if (classy == nil) {
printf ("could not find class named '%s'\n", classname);
exit (1);
}
printf ("Interesting stuff about %s\n", classname);
Class superclass = class_getSuperclass (classy);
if (superclass == nil) {
printf ("* %s has no superclass\n", classname);
} else {
printf ("* %s is a subclass of %s\n", classname, class_getName (superclass));
}
int version = class_getVersion (classy);
printf ("* is at version %d\n", version);
// Classes with non-zero versions:
// NSAffineTransform, NSCountedSet, NSDateFormatter,
// NSMutableString, NSNumberFormatter, NSString
size_t instanceSize = class_getInstanceSize (classy);
printf ("* is %zu bytes large\n", instanceSize);
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList (classy, &propertyCount);
printf ("* has %d properties\n", propertyCount);
// For fun, walk this pointer-style
objc_property_t *propertyScan = properties;
while (*propertyScan) {
const char *propertyName = property_getName (*propertyScan);
const char *propertyAttributes = property_getAttributes (*propertyScan);
printf (" - %s -> %s\n", propertyName,
ReadablePropertyAttributes(propertyAttributes));
propertyScan++;
}
free (properties);
unsigned int ivarCount;
Ivar *ivars = class_copyIvarList (classy, &ivarCount);
printf ("* has %d ivars\n", ivarCount);
for (int i = 0; i < ivarCount; i++) {
const char *ivarName = ivar_getName (ivars[i]);
ptrdiff_t ivarOffset = ivar_getOffset (ivars[i]);
const char *ivarEncoding = ivar_getTypeEncoding (ivars[i]);
printf (" - %s -> offset %d, %s\n", ivarName, (int)ivarOffset,
[ReadableTypeString(@(ivarEncoding)) UTF8String]);
}
free (ivars);
unsigned int protocolCount;
Protocol * __unsafe_unretained *protocols = class_copyProtocolList (classy, &protocolCount);
printf ("* has %d protocols\n", protocolCount);
for (int i = 0; i < protocolCount; i++) {
const char *protocolName = protocol_getName (protocols[i]);
unsigned int requiredMethodCount;
unsigned int optionalMethodCount;
struct objc_method_description *requiredMethods =
protocol_copyMethodDescriptionList (protocols[i], YES, YES, &requiredMethodCount);
free (requiredMethods);
struct objc_method_description *optionalMethods =
protocol_copyMethodDescriptionList (protocols[i], NO, YES, &optionalMethodCount);
free (optionalMethods);
printf (" - %s -> %d required, %d optional\n", protocolName,
requiredMethodCount, optionalMethodCount);
}
free (protocols);
} // ClassInfo
static void PrintMethods (Method methods[], BOOL isInstance) {
while (*methods) {
SEL selector = method_getName(*methods);
const char *name = sel_getName(selector);
// This returns another kind of encoding string : e.g. Q16@0:8 with sizes
// and offsets. Which according to email threads are wrong and should be
// ignored
// http://lists.apple.com/archives/objc-language/2009/Apr/msg00184.html
char buffer[100];
buffer[0] = '\0';
method_getReturnType (*methods, buffer, sizeof(buffer));
NSString *returnTypeString = ReadableTypeString (@(buffer));
unsigned int argumentCount = method_getNumberOfArguments (*methods);
NSMutableArray *arguments = [NSMutableArray arrayWithCapacity: argumentCount];
// Skip over self and selector.
for (int i = 2; i < argumentCount; i++) {
method_getArgumentType (*methods, i, buffer, sizeof(buffer));
[arguments addObject: ReadableTypeString (@(buffer))];
}
NSString *argumentString = @"nothing";
if (arguments.count > 0) {
argumentString = [arguments componentsJoinedByString: @", "];
}
printf (" %s %s : returns %s and takes %s\n", (isInstance) ? "-" : "+",
name, returnTypeString.UTF8String, argumentString.UTF8String);
methods++;
}
} // PrintMethods
static void MethodInfo (const char *classname) {
Class classy = objc_getClass (classname);
if (classy == nil) {
printf ("could not find class named '%s'\n", classname);
exit (1);
}
unsigned int instanceMethodCount;
unsigned int classMethodCount;
Method *instanceMethods = class_copyMethodList (classy, &instanceMethodCount);
Method *classMethods = class_copyMethodList (object_getClass(classy), &classMethodCount);
printf ("%d Instance Methods for %s\n", instanceMethodCount, classname);
PrintMethods (instanceMethods, YES);
printf ("%d Class Methods for %s\n", classMethodCount, classname);
PrintMethods (classMethods, NO);
free (instanceMethods);
free (classMethods);
} // MethodInfo
static void DumpMethodDescriptions (struct objc_method_description *methods,
unsigned int count, char lineDecoration) {
for (int i = 0; i < count; i++) {
const char *name = sel_getName(methods[i].name);
NSString *typeString = @( methods[i].types );
NSArray *types = ParseTypeString (typeString);
NSString *returnType = [types objectAtIndex: 0];
NSArray *noSelfAndSelector =
[types subarrayWithRange: (NSRange) {3, types.count - 3}];
NSString *typeDescription = [noSelfAndSelector componentsJoinedByString: @" "];
if (typeDescription.length == 0) typeDescription = @"nothing";
printf (" %c %s -> takes %s, returns %s\n", lineDecoration, name,
typeDescription.UTF8String, returnType.UTF8String);
}
} // DumpMethodDescriptions
static void ProtocolInfo (const char *protocolName) {
// There is no "look up protocol by name" like there is for classes (which is
// generally useful), so grab all the protocols and sift through them looking
// for |protocolName|
unsigned int protocolCount;
Protocol * __unsafe_unretained *protocols = objc_copyProtocolList (&protocolCount);
Protocol *protocol = NULL;
for (int i = 0; i < protocolCount; i++) {
const char *thisName = protocol_getName (protocols[i]);
if (strcmp(protocolName, thisName) == 0) {
protocol = protocols[i];
break;
}
}
free (protocols);
if (!protocol) {
printf ("could not find protocol named '%s'\n", protocolName);
exit (1);
}
unsigned int adopteeCount;
Protocol * __unsafe_unretained *adoptees =
protocol_copyProtocolList (protocol, &adopteeCount);
printf ("* Has %d adoptees\n", adopteeCount);
for (int i = 0; i < adopteeCount; i++) {
printf (" - %s\n", protocol_getName(adoptees[i]));
}
free (adoptees);
unsigned int methodCount;
struct objc_method_description *methods;
BOOL required, instance;
required = YES;
instance = YES;
methods =
protocol_copyMethodDescriptionList (protocol, required, instance, &methodCount);
printf ("* Required instance methods (%d)\n", methodCount);
DumpMethodDescriptions (methods, methodCount, '-');
free (methods);
required = NO;
instance = YES;
methods =
protocol_copyMethodDescriptionList (protocol, required, instance, &methodCount);
printf ("* Optional instance methods (%d)\n", methodCount);
DumpMethodDescriptions (methods, methodCount, '-');
free (methods);
required = YES;
instance = NO;
methods =
protocol_copyMethodDescriptionList (protocol, required, instance, &methodCount);
printf ("* Required class methods (%d)\n", methodCount);
DumpMethodDescriptions (methods, methodCount, '+');
free (methods);
required = NO;
instance = NO;
methods =
protocol_copyMethodDescriptionList (protocol, required, instance, &methodCount);
printf ("* Optional class methods (%d)\n", methodCount);
DumpMethodDescriptions (methods, methodCount, '+');
free (methods);
} // ProtocolInfo
static void DumpIvars (void) {
XXStuff *stuff = [[XXStuff alloc] init];
stuff.bounds2 = CGRectMake (1, 2, 3, 4);
CGRect pointedTo = CGRectMake (10, 20, 30, 40);
stuff.boundsPointer = &pointedTo;
stuff.blah = @"hello";
stuff.shoeSize = 10;
stuff.mikeysPonyFarm = [NSURL URLWithString: @"http://www.fanfiction.net/cartoon/My-Little-Pony/1/0/0/1/0/0/0/0/0/1/0/"]; // kid-rated. gotta keep it clean.
stuff.gilliganFactor = 8.2213;
stuff.ook = ^{ printf ("hi!\n"); };
stuff.blockHead = ^(NSFileHandle *handle) { printf ("hi 2!\n"); };
// Now dig into the ivars.
// Getting objects is easy
Ivar ponyIvar = class_getInstanceVariable ([stuff class], "_mikeysPonyFarm");
NSURL *url = object_getIvar (stuff, ponyIvar);
printf ("pony farm at %s\n", url.description.UTF8String);
// non-object ivars are kind of a pain
Ivar gilliganIvar = class_getInstanceVariable ([stuff class], "_gilliganFactor");
ptrdiff_t offset = ivar_getOffset(gilliganIvar);
unsigned char *stuffBytes = (unsigned char *)(__bridge void *)stuff;
CGFloat gilligans = * ((CGFloat *)(stuffBytes + offset));
printf ("%f gilligans\n", gilligans);
} // DumpIvars
static void Usage (const char *launchCommand) {
printf ("%s operation [optional-arg]\n", launchCommand);
printf ("Where operation is one of:\n");
printf (" listclasses -- list all known classes\n");
printf (" listprotocols -- list all known protocols\n");
printf (" dumpivars -- create an object, then introspect its ivars\n");
printf (" classinfo |classname| -- list lots of stuff about a class\n");
printf (" methodinfo |classname| -- list methods for a class\n");
printf (" protocolinfo |protocolname| -- list lots of stuff about a protocol\n");
} // Usage
int main (int argc, const char *argv[]) {
@autoreleasepool {
if (argc == 1) {
Usage (argv[0]);
return 1;
}
if (strcmp(argv[1], "listclasses") == 0) {
ListClasses ();
return 0;
}
if (strcmp(argv[1], "listprotocols") == 0) {
ListProtocols ();
return 0;
}
if (strcmp(argv[1], "classinfo") == 0) {
ClassInfo (argv[2]);
return 0;
}
if (strcmp(argv[1], "protocolinfo") == 0) {
ProtocolInfo (argv[2]);
return 0;
}
if (strcmp(argv[1], "methodinfo") == 0) {
MethodInfo (argv[2]);
return 0;
}
if (strcmp(argv[1], "dumpivars") == 0) {
DumpIvars ();
return 0;
}
Usage (argv[0]);
return 1;
}
} // main
#if 0
class_getClassVariable
http://lists.apple.com/archives/objc-language/2008/Feb/msg00021.html
// This returns another kind of encoding string : e.g. Q16@0:8 with sizes
// and offsets. Which according to email threads are wrong and should be
// ignored
// http://lists.apple.com/archives/objc-language/2009/Apr/msg00149.html
// const char *typeEncoding = method_getTypeEncoding (*methods);
// GC only
class_setIvarLayout
class_getIvarLayout
class_getWeakIvarLayout
class_setWeakIvarLayout
objc_getFutureClass - used in troll free bridging
"Do not call this function yourself"
(along with objc_duplicateClass - for KVO)
printf ("%s %s %s %s\n", @encode(unsigned char), @encode(unsigned char *), @encode(unsigned char **), @encode (unsigned char ****));
printf ("%s %s %s %s\n", @encode(int), @encode(int *), @encode(int **), @encode(int ****));
// return 0;
object_getInstanceVariable() works only with ivars of pointer types. Other types are not supported and may fail, as you noted.
object_getInstanceVariable() does not perform the correct ARC memory management for ivars of object types.
http://lists.apple.com/archives/objc-language/2013/May/msg00038.html
Use object_getIvar instead
Expectations vary. For object_getInstanceVariable(), we decided to disallow the unsafe thing in favor of the safer options. You can still get unsafe behavior if you ask for it more directly, such as using class_getIvar() and ivar_getOffset() and casting to void** instead of a pointer-to-object-pointer type.
class_createInstance
object_getIndexedIvars
#endif
#import <Foundation/Foundation.h>
// Returns an array of names of the types.
NSArray *ParseTypeString (NSString *typeString);
#import "typestring.h"
static NSString *StructEncoding (char **typeScan);
// Remove all numbers from the string. Some type encoding strings include
// offsets and/or sizes, and they're often wrong. yay?
static NSString *ScrubNumbers (NSString *string) {
NSCharacterSet *numbers = [NSCharacterSet decimalDigitCharacterSet];
NSString *numberFree = [[string componentsSeparatedByCharactersInSet: numbers]
componentsJoinedByString: @""];
return numberFree;
} // ScrubNumbers
// Convert simple types.
// |typeScan| is scooted over to account for any consumed characters
static NSString *SimpleEncoding (char **typeScan) {
typedef struct TypeMap {
unichar discriminator;
char *name;
} TypeMap;
static TypeMap s_typeMap[] = {
{ 'c', "char" },
{ 'i', "int" },
{ 's', "short" },
{ 'l', "long" },
{ 'q', "longlong" },
{ 'C', "unsigned char" },
{ 'I', "unsigned int" },
{ 'S', "unsigned short" },
{ 'L', "unsiged long" },
{ 'Q', "unsigned long long" },
{ 'f', "float" },
{ 'd', "double" },
{ 'B', "bool" },
{ 'v', "void" },
{ '*', "char*" },
{ '#', "class" },
{ ':', "selector" },
{ '?', "unknown" },
};
NSString *result = nil;
TypeMap *scan = s_typeMap;
TypeMap *stop = scan + sizeof(s_typeMap) / sizeof(*s_typeMap);
while (scan < stop) {
if (scan->discriminator == **typeScan) {
result = @( scan->name );
(*typeScan)++;
break;
}
scan++;
}
return result;
} // SimpleEncoding
// Process object/id/block types. Some type strings include the class name in "quotes"
// |typeScan| is scooted over to account for any consumed characters.
static NSString *ObjectEncoding (char **typeScan) {
assert (**typeScan == '@');
(*typeScan)++; // eat the '@'
NSString *result = @"id";
if (**typeScan == '"') {
(*typeScan)++; // eat the double-quote
char *closeQuote = *typeScan;
while (*closeQuote && *closeQuote != '"') {
closeQuote++;
}
*closeQuote = '\000';
result = [NSString stringWithUTF8String: *typeScan];
*closeQuote = '\"';
*typeScan = closeQuote;
} else if (**typeScan == '?') {
result = @"block";
(*typeScan)++;
}
return result;
} // ObjectEncoding
// Process pointer types. Recursive since pointers are people too.
// |typeScan| is scooted over to account for any consumed characters
static NSString *PointerEncoding (char **typeScan) {
assert (**typeScan == '^');
(*typeScan)++; // eat the '^'
NSString *result = @"";
if (**typeScan == '^') {
result = PointerEncoding (typeScan);
} else if (**typeScan == '{') {
result = StructEncoding (typeScan);
} else {
result = SimpleEncoding (typeScan);
}
result = [result stringByAppendingString: @"*"];
return result;
} // PointerEncoding
// Process structure types. Pull out the name of the first structure encountered
// and not worry about any embedded structures.
// |typeScan| is scooted over to account for any consumed characters
static NSString *StructEncoding (char **typeScan) {
assert (**typeScan == '{');
(*typeScan)++; // eat the '{'
NSString *result = @"";
// find the equal sign after the struct name
char *equalSign = *typeScan;
while (*equalSign && *equalSign != '=') {
equalSign++;
}
*equalSign = '\000';
result = [NSString stringWithUTF8String: *typeScan];
*equalSign = '=';
// Eat the rest of the potentially nested structures.
int openCount = 1;
while (**typeScan && openCount) {
if (**typeScan == '{') openCount++;
if (**typeScan == '}') openCount--;
(*typeScan)++;
}
return result;
} // StructEncoding
// Given an Objective-C type encoding string, return an array of human-readable
// strings that describe each of the types.
NSArray *ParseTypeString (NSString *rawTypeString) {
NSString *typeString = ScrubNumbers (rawTypeString);
char *base = strdup ([typeString UTF8String]);
char *scan = base;
NSMutableArray *chunks = [NSMutableArray array];
while (*scan) {
NSString *stuff = SimpleEncoding (&scan);
if (stuff) {
[chunks addObject: stuff];
continue;
}
if (*scan == '@') {
stuff = ObjectEncoding (&scan);
[chunks addObject: stuff];
continue;
}
if (*scan == '^') {
stuff = PointerEncoding (&scan);
[chunks addObject: stuff];
continue;
}
if (*scan == '{') {
stuff = StructEncoding (&scan);
[chunks addObject: stuff];
continue;
}
// If we hit this, more work needs to be done.
stuff = [NSString stringWithFormat: @"(that was unexpected: %c)", *scan];
scan++;
}
free (base);
return chunks;
} // ParseTypeString
#import "typestring.h"
// clang -g -fobjc-arc -Wall -framework Foundation -o typetest typestring.m typetest.m
// Chew through these without crashing. Eyeball the result.
static char *g_typestrings[] = {
"@16@0:8",
"@?16@0:8",
"Q16@0:8",
"Vv16@0:8",
"c16@0:8",
"d16@0:8",
"q16@0:8",
"v16@0:8",
"v24@0:8@16",
"v24@0:8@?16",
"v24@0:8d16",
"v24@0:8q16",
"v72@0:8@16@24Q32@40@48@56^v64",
"@16@0:8",
"@88@0:8^*16q24q32q40q48c56c60@64q72q80",
"@?16@0:8",
"Q16@0:8",
"^{CGRect={CGPoint=dd}{CGSize=dd}}16@0:8",
"d16@0:8",
"v16@0:8",
"v24@0:8@16",
"v24@0:8@?16",
"v24@0:8Q16",
"v24@0:8^{CGRect={CGPoint=dd}{CGSize=dd}}16",
"v24@0:8d16",
"v48@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16",
"{CGRect={CGPoint=dd}{CGSize=dd}}16@0:8",
"@\"<NSObject>\"",
"@\"NSURL\"",
"@?",
};
int main (void) {
@autoreleasepool {
char **scan = g_typestrings;
char **stop = scan + sizeof (g_typestrings) / sizeof (*g_typestrings);
while (scan < stop) {
printf ("%s\n", *scan);
NSArray *jazz = ParseTypeString ( @(*scan) );
NSLog (@"%s -> %@", *scan, jazz);
scan++;
}
}
} // main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment