My main resource for reverse engineering UICollection is https://github.com/steipete/iOS6-Runtime-Headers.
// For debugging purposes only
#ifdef DEBUG
NSString *_PSPDFPrintIvars(id obj, NSMutableSet *recursiveSet);
void PSPDFPrintIvars(id obj, BOOL printRecursive) {
NSString *ivarString = _PSPDFPrintIvars(obj, printRecursive ? [NSMutableSet set] : nil);
NSLog(@"%@", ivarString);
}
NSMutableDictionary *_PSDPFConvertIvarsToDictionary(Class class, id obj);
NSMutableDictionary *_PSDPFConvertIvarsToDictionary(Class class, id obj) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned int ivarCount;
Ivar *ivars = class_copyIvarList(class, &ivarCount);
for (int i = 0; i < ivarCount; i++) {
NSString *name = @(ivar_getName(ivars[i]));
id value;
@try { value = [obj valueForKey:name]; }
@catch (NSException *exception) {
value = [exception reason];
}
value = value ?: [NSNull null];
[dict setObject:value forKey:name];
}
free(ivars);
return dict;
}
NSString *_PSPDFPrintIvars(id obj, NSMutableSet *recursiveSet) {
NSMutableString *string = [NSMutableString string];
[recursiveSet addObject:obj];
// query ivar list
NSMutableSet *ivarDictionaries = [NSMutableSet set];
NSMutableDictionary *dict = _PSDPFConvertIvarsToDictionary([obj class], obj);
Class superClass = class_getSuperclass([obj class]);
while(superClass && ![superClass isEqual:[NSObject class]]) {
NSDictionary *superClassIvarDict = _PSDPFConvertIvarsToDictionary(superClass, obj);
if ([superClassIvarDict count]) {
dict[NSStringFromClass(superClass)] = superClassIvarDict;
if ([NSStringFromClass(superClass) hasPrefix:@"UICollection"] || [NSStringFromClass(superClass) hasPrefix:@"_UI"]) {
[ivarDictionaries addObject:superClassIvarDict];
}
}
superClass = class_getSuperclass(superClass);
}
if ([dict count]) {
[string appendFormat:@"<%@= %@>", NSStringFromClass([obj class]), [dict description]];
}
// dig deeper if recursive is enabled
if (recursiveSet) {
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id dictObj, BOOL *stop) {
if ([ivarDictionaries containsObject:dictObj]) {
[dictObj enumerateKeysAndObjectsUsingBlock:^(id key, id dictObj, BOOL *stop) {
if (![recursiveSet containsObject:dictObj] && (![dictObj isKindOfClass:[NSDictionary class]] || [dictObj count] > 0) && ![dictObj isKindOfClass:[NSSet set]] && ![dictObj isKindOfClass:NSClassFromString(@"NSConcreteValue")]) {
if ([[dictObj class] conformsToProtocol:@protocol(NSFastEnumeration)]) {
for (id anObj in dictObj) {
[string appendString:_PSPDFPrintIvars(anObj, recursiveSet)];
}
}else {
[string appendString:_PSPDFPrintIvars(dictObj, recursiveSet)];
}
}
}];
}else {
if (![recursiveSet containsObject:dictObj] && (![dictObj isKindOfClass:[NSDictionary class]] || [dictObj count] > 0) && ![dictObj isKindOfClass:[NSSet set]] && ![dictObj isKindOfClass:NSClassFromString(@"NSConcreteValue")]) {
if ([[dictObj class] conformsToProtocol:@protocol(NSFastEnumeration)]) {
for (id anObj in dictObj) {
[string appendString:_PSPDFPrintIvars(anObj, recursiveSet)];
}
}else {
[string appendString:_PSPDFPrintIvars(dictObj, recursiveSet)];
}
}
}
}];
}
// custom stuff (e.g. get a id * variable that's not KVO compliant)
if([NSStringFromClass([obj class]) isEqualToString:@"UICollectionViewData"]) {
id *outvalue;
object_getInstanceVariable(obj, "_globalItems", (void **)&outvalue);
NSLog(@"done");
}
return string;
}
#endif