├── Compatibility.txt ├── NSObject+Frankenstein.h ├── NSObject+Frankenstein.m └── do-git /Compatibility.txt: -------------------------------------------------------------------------------- 1 | #pragma mark Useful Compatibility Tips 2 | 3 | #if TARGET_IPHONE_SIMULATOR 4 | // NSLog( @"Running in the simulator!" ); 5 | #else 6 | // NSLog( @"Running on the device!" ); 7 | #endif 8 | 9 | // Tip #1 Compile-time checks for OS 10 | /* 11 | #ifdef _USE_OS_3_OR_LATER 12 | // 3.0 or later defs 13 | #endif 14 | #if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 15 | // Pre-3.0 Code 16 | #else 17 | // 3.0 Code 18 | #endif 19 | 20 | also 21 | typedef enum { 22 | #if __IPHONE_3_2 <= __IPHONE_OS_VERSION_MAX_ALLOWED 23 | UIUserInterfaceIdiomPhone, // iPhone and iPod touch style UI 24 | UIUserInterfaceIdiomPad, // iPad style UI 25 | #endif 26 | } UIUserInterfaceIdiom; 27 | 28 | Built in sys versions include: 29 | #define __IPHONE_2_0 20000 30 | #define __IPHONE_2_1 20100 31 | #define __IPHONE_2_2 20200 32 | #define __IPHONE_3_0 30000 33 | #define __IPHONE_3_1 30100 34 | #define __IPHONE_3_2 30200 35 | #define __IPHONE_NA 99999 36 | */ 37 | 38 | // Tip #2 Run-time checks 39 | /* 40 | // Sys prefix 41 | if ([[[UIDevice currentDevice] systemVersion] hasPrefix:@"2."]) 42 | [cell setText:celltext]; 43 | else if ([[[UIDevice currentDevice] systemVersion] hasPrefix:@"3."]) 44 | [[cell textLabel] setText:celltext]; 45 | 46 | // key/value coding 47 | UILabel *label = (UILabel *)[cell valueForKey:@"textLabel"]; 48 | if (label) [label setText:celltext]; 49 | 50 | // Class existence 51 | Class c = NSClassFromString(classname); 52 | if (c) {...} 53 | */ 54 | 55 | // Tip 3 Extract constants 56 | /* 57 | Add these to OTHER_CFLAGS in the Build tab: -save-temps 58 | */ -------------------------------------------------------------------------------- /NSObject+Frankenstein.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | 8 | @import Foundation; 9 | 10 | #define VALUE(struct) ({ __typeof__(struct) __struct = struct; [NSValue valueWithBytes:&__struct objCType:@encode(__typeof__(__struct))]; }) 11 | 12 | #define MAKELIVE(_CLASSNAME_) Class _CLASSNAME_ = NSClassFromString((NSString *)CFSTR(#_CLASSNAME_)); 13 | 14 | // thanks Landon Fuller 15 | #define VERIFIED_CLASS(_CLASSNAME_) ((_CLASSNAME_ *) NSClassFromString(@"" # _CLASSNAME_)) 16 | 17 | typedef void(^BasicBlockType)(); 18 | 19 | @interface NSObject (Frankenstein) 20 | // Return all superclasses of class or object 21 | + (NSArray *) superclasses; 22 | - (NSArray *) superclasses; 23 | 24 | // Examine 25 | + (NSString *) dump; 26 | - (NSString *) dump; 27 | 28 | // Selector Utilities 29 | - (NSInvocation *) invocationWithSelectorAndArguments: (SEL) selector,...; 30 | - (BOOL) performSelector: (SEL) selector withReturnValueAndArguments: (void *) result, ...; 31 | - (const char *) returnTypeForSelector:(SEL)selector; 32 | 33 | // Request return value from performing selector 34 | - (id) objectByPerformingSelectorWithArguments: (SEL) selector, ...; 35 | - (__autoreleasing id) objectByPerformingSelector:(SEL)selector withObject:(id) object1 withObject: (id) object2; 36 | - (id) objectByPerformingSelector:(SEL)selector withObject:(id) object1; 37 | - (id) objectByPerformingSelector:(SEL)selector; 38 | 39 | // Delay Utilities 40 | void _PerformBlockAfterDelay(BasicBlockType block, NSTimeInterval delay); 41 | + (void) performBlock:(void (^)(void)) block afterDelay: (NSTimeInterval) delay; 42 | - (void) performBlock:(void (^)(void)) block afterDelay: (NSTimeInterval) delay; 43 | - (void) performSelector: (SEL) selector withCPointer: (void *) cPointer afterDelay: (NSTimeInterval) delay; 44 | - (void) performSelector: (SEL) selector afterDelay: (NSTimeInterval) delay; 45 | - (void) performSelector: (SEL) selector withDelayAndArguments: (NSTimeInterval) delay,...; 46 | 47 | // Return Values, allowing non-object returns 48 | - (NSValue *) valueByPerformingSelector:(SEL)selector withObject:(id) object1 withObject: (id) object2; 49 | - (NSValue *) valueByPerformingSelector:(SEL)selector withObject:(id) object1; 50 | - (NSValue *) valueByPerformingSelector:(SEL)selector; 51 | 52 | // Access to object essentials for run-time checks. Stored by class in dictionary. 53 | @property (readonly) NSDictionary *selectors; 54 | @property (readonly) NSDictionary *properties; 55 | @property (readonly) NSDictionary *ivars; 56 | @property (readonly) NSDictionary *protocols; 57 | 58 | // Check for properties, ivar. Use respondsToSelector: and conformsToProtocol: as well 59 | - (BOOL) hasProperty: (NSString *) propertyName; 60 | - (BOOL) hasIvar: (NSString *) ivarName; 61 | + (BOOL) classExists: (NSString *) className; 62 | + (id) instanceOfClassNamed: (NSString *) className; 63 | 64 | // Attempt selector if possible 65 | - (id) safePerformSelector: (SEL) selector withObject: (NSObject *) object1 withObject: (NSObject *) object2; 66 | - (id) safePerformSelector: (SEL) selector withObject: (NSObject *) object1; 67 | - (id) safePerformSelector: (SEL) selector; 68 | 69 | // Choose the first selector that the object responds to 70 | // - (SEL) chooseSelector: (SEL) aSelector, ...; 71 | @end -------------------------------------------------------------------------------- /NSObject+Frankenstein.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | 5 | */ 6 | 7 | 8 | #import "NSObject+Frankenstein.h" 9 | @import ObjectiveC; 10 | 11 | // add category on NSObject to performBlockAfterDelay:block: ?? 12 | 13 | @implementation NSObject (Frankenstein) 14 | 15 | #pragma mark - Superclasses 16 | 17 | + (NSArray *) superclasses 18 | { 19 | if ([self isEqual:[NSObject class]]) return @[self]; 20 | 21 | Class theClass = self; 22 | NSMutableArray *results = [NSMutableArray arrayWithObject:theClass]; 23 | 24 | do 25 | { 26 | theClass = [theClass superclass]; 27 | [results addObject:theClass]; 28 | } 29 | while (![theClass isEqual:[NSObject class]]) ; 30 | 31 | return results; 32 | } 33 | 34 | // Return an array of an object's superclasses 35 | - (NSArray *) superclasses 36 | { 37 | return [[self class] superclasses]; 38 | } 39 | 40 | #pragma mark - Selectors 41 | 42 | // Return an invocation based on a selector and variadic arguments 43 | - (NSInvocation *) invocationWithSelector: (SEL) selector andArguments:(va_list) arguments 44 | { 45 | if (![self respondsToSelector:selector]) return NULL; 46 | 47 | NSMethodSignature *ms = [self methodSignatureForSelector:selector]; 48 | if (!ms) return NULL; 49 | 50 | NSInvocation *inv = [NSInvocation invocationWithMethodSignature:ms]; 51 | if (!inv) return NULL; 52 | 53 | [inv setTarget:self]; 54 | [inv setSelector:selector]; 55 | 56 | int argcount = 2; 57 | int totalArgs = [ms numberOfArguments]; 58 | 59 | while (argcount < totalArgs) 60 | { 61 | char *argtype = (char *)[ms getArgumentTypeAtIndex:argcount]; 62 | if (strcmp(argtype, @encode(id)) == 0) 63 | { 64 | id argument = va_arg(arguments, id); 65 | [inv setArgument:&argument atIndex:argcount++]; 66 | } 67 | else if ( 68 | (strcmp(argtype, @encode(char)) == 0) || 69 | (strcmp(argtype, @encode(unsigned char)) == 0) || 70 | (strcmp(argtype, @encode(short)) == 0) || 71 | (strcmp(argtype, @encode(unsigned short)) == 0) | 72 | (strcmp(argtype, @encode(int)) == 0) || 73 | (strcmp(argtype, @encode(unsigned int)) == 0) 74 | ) 75 | { 76 | int i = va_arg(arguments, int); 77 | [inv setArgument:&i atIndex:argcount++]; 78 | } 79 | else if ( 80 | (strcmp(argtype, @encode(long)) == 0) || 81 | (strcmp(argtype, @encode(unsigned long)) == 0) 82 | ) 83 | { 84 | long l = va_arg(arguments, long); 85 | [inv setArgument:&l atIndex:argcount++]; 86 | } 87 | else if ( 88 | (strcmp(argtype, @encode(long long)) == 0) || 89 | (strcmp(argtype, @encode(unsigned long long)) == 0) 90 | ) 91 | { 92 | long long l = va_arg(arguments, long long); 93 | [inv setArgument:&l atIndex:argcount++]; 94 | } 95 | else if ( 96 | (strcmp(argtype, @encode(float)) == 0) || 97 | (strcmp(argtype, @encode(double)) == 0) 98 | ) 99 | { 100 | double d = va_arg(arguments, double); 101 | [inv setArgument:&d atIndex:argcount++]; 102 | } 103 | else if (strcmp(argtype, @encode(Class)) == 0) 104 | { 105 | Class c = va_arg(arguments, Class); 106 | [inv setArgument:&c atIndex:argcount++]; 107 | } 108 | else if (strcmp(argtype, @encode(SEL)) == 0) 109 | { 110 | SEL s = va_arg(arguments, SEL); 111 | [inv setArgument:&s atIndex:argcount++]; 112 | } 113 | else if (strcmp(argtype, @encode(char *)) == 0) 114 | { 115 | char *s = va_arg(arguments, char *); 116 | [inv setArgument:s atIndex:argcount++]; 117 | } 118 | else if (strcmp(argtype, @encode(CGRect)) == 0) 119 | { 120 | CGRect arg = va_arg(arguments, CGRect); 121 | [inv setArgument:&arg atIndex:argcount++]; 122 | } 123 | else if (strcmp(argtype, @encode(CGPoint)) == 0) 124 | { 125 | CGPoint arg = va_arg(arguments, CGPoint); 126 | [inv setArgument:&arg atIndex:argcount++]; 127 | } 128 | else if (strcmp(argtype, @encode(CGSize)) == 0) 129 | { 130 | CGSize arg = va_arg(arguments, CGSize); 131 | [inv setArgument:&arg atIndex:argcount++]; 132 | } 133 | else if (strcmp(argtype, @encode(CGAffineTransform)) == 0) 134 | { 135 | CGAffineTransform arg = va_arg(arguments, CGAffineTransform); 136 | [inv setArgument:&arg atIndex:argcount++]; 137 | } 138 | else if (strcmp(argtype, @encode(NSRange)) == 0) 139 | { 140 | NSRange arg = va_arg(arguments, NSRange); 141 | [inv setArgument:&arg atIndex:argcount++]; 142 | } 143 | else if (strcmp(argtype, @encode(UIOffset)) == 0) 144 | { 145 | UIOffset arg = va_arg(arguments, UIOffset); 146 | [inv setArgument:&arg atIndex:argcount++]; 147 | } 148 | else if (strcmp(argtype, @encode(UIEdgeInsets)) == 0) 149 | { 150 | UIEdgeInsets arg = va_arg(arguments, UIEdgeInsets); 151 | [inv setArgument:&arg atIndex:argcount++]; 152 | } 153 | else 154 | { 155 | // assume its a pointer and punt 156 | NSLog(@"Punting... %s", argtype); 157 | void *ptr = va_arg(arguments, void *); 158 | [inv setArgument:ptr atIndex:argcount++]; 159 | } 160 | } 161 | 162 | if (argcount != totalArgs) 163 | { 164 | printf("Invocation argument count mismatch: %d expected, %d sent\n", [ms numberOfArguments], argcount); 165 | return NULL; 166 | } 167 | 168 | return inv; 169 | } 170 | 171 | // Return an invocation with the given arguments 172 | - (NSInvocation *) invocationWithSelectorAndArguments: (SEL) selector, ... 173 | { 174 | va_list arglist; 175 | va_start(arglist, selector); 176 | NSInvocation *inv = [self invocationWithSelector:selector andArguments:arglist]; 177 | va_end(arglist); 178 | return inv; 179 | } 180 | 181 | // Peform the selector using va_list arguments 182 | - (BOOL) performSelector: (SEL) selector withReturnValue: (void *) result andArguments: (va_list) arglist 183 | { 184 | NSInvocation *inv = [self invocationWithSelector:selector andArguments:arglist]; 185 | if (!inv) return NO; 186 | [inv invoke]; 187 | if (result) 188 | [inv getReturnValue:result]; 189 | return YES; 190 | } 191 | 192 | // Perform a selector with an arbitrary number of arguments 193 | // Thanks to Kevin Ballard for assist! 194 | - (BOOL) performSelector: (SEL) selector withReturnValueAndArguments: (void *) result, ... 195 | { 196 | va_list arglist; 197 | va_start(arglist, result); 198 | NSInvocation *inv = [self invocationWithSelector:selector andArguments:arglist]; 199 | if (!inv) return NO; 200 | [inv invoke]; 201 | if (result) [inv getReturnValue:result]; 202 | va_end(arglist); 203 | return YES; 204 | } 205 | 206 | // Returning objects by performing selectors 207 | - (id) objectByPerformingSelectorWithArguments: (SEL) selector, ... 208 | { 209 | id result; 210 | va_list arglist; 211 | va_start(arglist, selector); 212 | [self performSelector:selector withReturnValue:&result andArguments:arglist]; 213 | va_end(arglist); 214 | return result; 215 | } 216 | 217 | - (__autoreleasing id) objectByPerformingSelector:(SEL)selector withObject:(id) object1 withObject: (id) object2 218 | { 219 | if (![self respondsToSelector:selector]) return nil; 220 | 221 | // Retrieve method signature and return type 222 | NSMethodSignature *ms = [self methodSignatureForSelector:selector]; 223 | const char *returnType = [ms methodReturnType]; 224 | 225 | // Create invocation using method signature and invoke it 226 | NSInvocation *inv = [NSInvocation invocationWithMethodSignature:ms]; 227 | [inv setTarget:self]; 228 | [inv setSelector:selector]; 229 | if (object1) [inv setArgument:&object1 atIndex:2]; 230 | if (object2) [inv setArgument:&object2 atIndex:3]; 231 | [inv invoke]; 232 | 233 | // return object 234 | if (strcmp(returnType, @encode(id)) == 0) 235 | { 236 | id __autoreleasing object = nil; 237 | [inv getReturnValue:&object]; 238 | return object; 239 | } 240 | 241 | // return double 242 | if ((strcmp(returnType, @encode(float)) == 0) || 243 | (strcmp(returnType, @encode(double)) == 0)) 244 | { 245 | double f; 246 | [inv getReturnValue:&f]; 247 | return [NSNumber numberWithDouble:f]; 248 | } 249 | 250 | // return NSNumber version of byte. Use valueBy version for recovering chars 251 | if ((strcmp(returnType, @encode(char)) == 0) || 252 | (strcmp(returnType, @encode(unsigned char)) == 0)) 253 | { 254 | unsigned char c; 255 | [inv getReturnValue:&c]; 256 | return [NSNumber numberWithInt:(unsigned int)c]; 257 | } 258 | 259 | // return c-string 260 | if (strcmp(returnType, @encode (char*)) == 0) 261 | { 262 | char *s = NULL; 263 | [inv getReturnValue:s]; 264 | return [NSString stringWithCString:s encoding:NSUTF8StringEncoding]; 265 | } 266 | 267 | // return integer 268 | long l; 269 | [inv getReturnValue:&l]; 270 | return [NSNumber numberWithLong:l]; 271 | } 272 | 273 | - (id) objectByPerformingSelector:(SEL)selector withObject:(id) object1 274 | { 275 | return [self objectByPerformingSelector:selector withObject:object1 withObject:nil]; 276 | } 277 | 278 | - (id) objectByPerformingSelector:(SEL)selector 279 | { 280 | return [self objectByPerformingSelector:selector withObject:nil withObject:nil]; 281 | } 282 | 283 | #pragma mark - Delayed Selectors 284 | 285 | // Delayed selectors 286 | - (void) performSelector: (SEL) selector withCPointer: (void *) cPointer afterDelay: (NSTimeInterval) delay 287 | { 288 | NSMethodSignature *ms = [self methodSignatureForSelector:selector]; 289 | NSInvocation *inv = [NSInvocation invocationWithMethodSignature:ms]; 290 | 291 | [inv setTarget:self]; 292 | [inv setSelector:selector]; 293 | [inv setArgument:cPointer atIndex:2]; 294 | [inv performSelector:@selector(invoke) withObject:self afterDelay:delay]; 295 | } 296 | 297 | - (void) performSelector: (SEL) selector afterDelay: (NSTimeInterval) delay 298 | { 299 | [self performSelector:selector withObject:nil afterDelay: delay]; 300 | } 301 | 302 | // private. only sent to an invocation 303 | - (void) getReturnValue: (void *) result 304 | { 305 | NSInvocation *inv = (NSInvocation *) self; 306 | [inv invoke]; 307 | if (result) [inv getReturnValue:result]; 308 | } 309 | 310 | // Delayed selector 311 | - (void) performSelector: (SEL) selector withDelayAndArguments: (NSTimeInterval) delay,... 312 | { 313 | va_list arglist; 314 | va_start(arglist, delay); 315 | NSInvocation *inv = [self invocationWithSelector:selector andArguments:arglist]; 316 | va_end(arglist); 317 | 318 | if (!inv) return; 319 | [inv performSelector:@selector(invoke) afterDelay:delay]; 320 | } 321 | 322 | #pragma mark - Delayed Block 323 | 324 | // Thanks August 325 | 326 | void _PerformBlockAfterDelay(BasicBlockType block, NSTimeInterval delay) 327 | { 328 | if (!block) return; 329 | dispatch_time_t targetTime = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC); 330 | dispatch_after(targetTime, dispatch_get_main_queue(), ^(void){ 331 | block(); 332 | }); 333 | } 334 | 335 | + (void) performBlock:(void (^)(void)) block afterDelay: (NSTimeInterval) delay 336 | { 337 | _PerformBlockAfterDelay(block, delay); 338 | } 339 | 340 | - (void) performBlock:(void (^)(void)) block afterDelay: (NSTimeInterval) delay 341 | { 342 | _PerformBlockAfterDelay(block, delay); 343 | } 344 | 345 | #pragma mark - values 346 | 347 | - (NSValue *) valueByPerformingSelector:(SEL)selector withObject:(id) object1 withObject: (id) object2 348 | { 349 | if (![self respondsToSelector:selector]) return nil; 350 | 351 | // Retrieve method signature and return type 352 | NSMethodSignature *ms = [self methodSignatureForSelector:selector]; 353 | const char *returnType = [ms methodReturnType]; 354 | 355 | // Create invocation using method signature and invoke it 356 | NSInvocation *inv = [NSInvocation invocationWithMethodSignature:ms]; 357 | [inv setTarget:self]; 358 | [inv setSelector:selector]; 359 | if (object1) [inv setArgument:&object1 atIndex:2]; 360 | if (object2) [inv setArgument:&object2 atIndex:3]; 361 | [inv invoke]; 362 | 363 | // Place results into value 364 | NSUInteger length = [ms methodReturnLength]; 365 | void *bytes = malloc(length); 366 | [inv getReturnValue:bytes]; 367 | NSValue *returnValue = [NSValue valueWithBytes: bytes objCType: returnType]; 368 | free(bytes); 369 | return returnValue; 370 | } 371 | 372 | - (NSValue *) valueByPerformingSelector:(SEL)selector withObject:(id) object1 373 | { 374 | return [self valueByPerformingSelector:selector withObject:object1 withObject:nil]; 375 | } 376 | 377 | - (NSValue *) valueByPerformingSelector:(SEL)selector 378 | { 379 | return [self valueByPerformingSelector:selector withObject:nil withObject:nil]; 380 | } 381 | 382 | #pragma mark - Class Bits 383 | 384 | 385 | // Return an array of all an object's selectors 386 | + (NSArray *) getSelectorListForClass 387 | { 388 | NSMutableArray *selectors = [NSMutableArray array]; 389 | unsigned int num; 390 | Method *methods = class_copyMethodList(self, &num); 391 | for (int i = 0; i < num; i++) 392 | [selectors addObject:NSStringFromSelector(method_getName(methods[i]))]; 393 | free(methods); 394 | return selectors; 395 | } 396 | 397 | // Return a dictionary with class/selectors entries, all the way up to NSObject 398 | - (NSDictionary *) selectors 399 | { 400 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 401 | [dict setObject:[[self class] getSelectorListForClass] forKey:NSStringFromClass([self class])]; 402 | for (Class cl in [self superclasses]) 403 | [dict setObject:[cl getSelectorListForClass] forKey:NSStringFromClass(cl)]; 404 | return dict; 405 | } 406 | 407 | // Return an array of all an object's properties 408 | + (NSArray *) getPropertyListForClass 409 | { 410 | NSMutableArray *propertyNames = [NSMutableArray array]; 411 | unsigned int num; 412 | objc_property_t *properties = class_copyPropertyList(self, &num); 413 | for (int i = 0; i < num; i++) 414 | [propertyNames addObject:[NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding]]; 415 | free(properties); 416 | return propertyNames; 417 | } 418 | 419 | // Return a dictionary with class/selectors entries, all the way up to NSObject 420 | - (NSDictionary *) properties 421 | { 422 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 423 | [dict setObject:[[self class] getPropertyListForClass] forKey:NSStringFromClass([self class])]; 424 | for (Class cl in [self superclasses]) 425 | [dict setObject:[cl getPropertyListForClass] forKey:NSStringFromClass(cl)]; 426 | return dict; 427 | } 428 | 429 | // Return an array of all an object's properties 430 | + (NSArray *) getIvarListForClass 431 | { 432 | NSMutableArray *ivarNames = [NSMutableArray array]; 433 | unsigned int num; 434 | Ivar *ivars = class_copyIvarList(self, &num); 435 | for (int i = 0; i < num; i++) 436 | [ivarNames addObject:[NSString stringWithCString:ivar_getName(ivars[i]) encoding:NSUTF8StringEncoding]]; 437 | free(ivars); 438 | return ivarNames; 439 | } 440 | 441 | // Return a dictionary with class/selectors entries, all the way up to NSObject 442 | - (NSDictionary *) ivars 443 | { 444 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 445 | [dict setObject:[[self class] getIvarListForClass] forKey:NSStringFromClass([self class])]; 446 | for (Class cl in [self superclasses]) 447 | [dict setObject:[cl getIvarListForClass] forKey:NSStringFromClass(cl)]; 448 | return dict; 449 | } 450 | 451 | // Return an array of all an object's properties 452 | + (NSArray *) getProtocolListForClass 453 | { 454 | NSMutableArray *protocolNames = [NSMutableArray array]; 455 | unsigned int num; 456 | Protocol *const *protocols = class_copyProtocolList(self, &num); 457 | for (int i = 0; i < num; i++) 458 | [protocolNames addObject:[NSString stringWithCString:protocol_getName(protocols[i]) encoding:NSUTF8StringEncoding]]; 459 | free((void *)protocols); 460 | return protocolNames; 461 | } 462 | 463 | // Return a dictionary with class/selectors entries, all the way up to NSObject 464 | - (NSDictionary *) protocols 465 | { 466 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 467 | [dict setObject:[[self class] getProtocolListForClass] forKey:NSStringFromClass([self class])]; 468 | for (Class cl in [self superclasses]) 469 | [dict setObject:[cl getProtocolListForClass] forKey:NSStringFromClass(cl)]; 470 | return dict; 471 | } 472 | 473 | // Runtime checks of properties, etc. 474 | - (BOOL) hasProperty: (NSString *) propertyName 475 | { 476 | NSMutableSet *set = [NSMutableSet set]; 477 | NSDictionary *dict = self.properties; 478 | for (NSArray *properties in [dict allValues]) 479 | [set addObjectsFromArray:properties]; 480 | return [set containsObject:propertyName]; 481 | } 482 | 483 | // Tests whether ivar exists 484 | - (BOOL) hasIvar: (NSString *) ivarName 485 | { 486 | NSMutableSet *set = [NSMutableSet set]; 487 | NSDictionary *dict = self.ivars; 488 | for (NSArray *ivars in [dict allValues]) 489 | [set addObjectsFromArray:ivars]; 490 | return [set containsObject:ivarName]; 491 | } 492 | 493 | // Tests class 494 | + (BOOL) classExists: (NSString *) className 495 | { 496 | return (NSClassFromString(className) != nil); 497 | } 498 | 499 | // Return instance from class 500 | + (id) instanceOfClassNamed: (NSString *) className 501 | { 502 | if (NSClassFromString(className) != nil) 503 | return [[NSClassFromString(className) alloc] init]; 504 | else 505 | return nil; 506 | } 507 | 508 | // Return a C-string with a selector's return type 509 | // may extend this idea to return a class 510 | - (const char *) returnTypeForSelector:(SEL)selector 511 | { 512 | NSMethodSignature *ms = [self methodSignatureForSelector:selector]; 513 | return [ms methodReturnType]; 514 | } 515 | 516 | /* 517 | 518 | Notes: 519 | 520 | objc_property_t prop = class_getProperty(self.class, "foo"); 521 | char *setterName = property_copyAttributeValue(prop, "S"); 522 | printf("%s\n", setterName); 523 | char *getterName = property_copyAttributeValue(prop, "G"); 524 | printf("%s\n", getterName); 525 | 526 | see http://svn.gna.org/svn/gnustep/libs/libobjc2/trunk/properties.m 527 | T == property_getTypeEncoding(property) 528 | D == dynamic/synthesized 529 | V = property_getIVar(property) 530 | S = property->setter_name 531 | G = property->getter_name 532 | R - readonly, W - weak, C - copy, &, retain/strong, N - Nonatomic 533 | 534 | */ 535 | 536 | // A work in progress 537 | + (NSString *) typeForString: (const char *) typeName 538 | { 539 | NSString *typeNameString = @(typeName); 540 | if ([typeNameString hasPrefix:@"@\""]) 541 | { 542 | NSRange r = NSMakeRange(2, typeNameString.length - 3); 543 | NSString *format = [NSString stringWithFormat:@"(%@ *)", [typeNameString substringWithRange:r]]; 544 | return format; 545 | } 546 | 547 | if ([typeNameString isEqualToString:@"v"]) 548 | return @"(void)"; 549 | 550 | if ([typeNameString isEqualToString:@"@"]) 551 | return @"(id)"; 552 | 553 | if ([typeNameString isEqualToString:@"^v"]) 554 | return @"(void *)"; 555 | 556 | if ([typeNameString isEqualToString:@"c"]) 557 | return @"(BOOL)"; 558 | 559 | if ([typeNameString isEqualToString:@"i"]) 560 | return @"(int)"; 561 | 562 | if ([typeNameString isEqualToString:@"s"]) 563 | return @"(short)"; 564 | 565 | if ([typeNameString isEqualToString:@"l"]) 566 | return @"(long)"; 567 | 568 | if ([typeNameString isEqualToString:@"q"]) 569 | return @"(long long)"; 570 | 571 | if ([typeNameString isEqualToString:@"I"]) 572 | return @"(unsigned int)"; 573 | 574 | if ([typeNameString isEqualToString:@"L"]) 575 | return @"(unsigned long)"; 576 | 577 | if ([typeNameString isEqualToString:@"Q"]) 578 | return @"(unsigned long long)"; 579 | 580 | if ([typeNameString isEqualToString:@"f"]) 581 | return @"(float)"; 582 | 583 | if ([typeNameString isEqualToString:@"d"]) 584 | return @"(double)"; 585 | 586 | if ([typeNameString isEqualToString:@"B"]) 587 | return @"(bool)"; 588 | 589 | if ([typeNameString isEqualToString:@"*"]) 590 | return @"(char *)"; 591 | 592 | if ([typeNameString isEqualToString:@"#"]) 593 | return @"(Class)"; 594 | 595 | if ([typeNameString isEqualToString:@":"]) 596 | return @"(SEL)"; 597 | 598 | if ([typeNameString isEqualToString:@(@encode(CGPoint))]) 599 | return @"(CGPoint)"; 600 | 601 | if ([typeNameString isEqualToString:@(@encode(CGSize))]) 602 | return @"(CGSize)"; 603 | 604 | if ([typeNameString isEqualToString:@(@encode(CGRect))]) 605 | return @"(CGRect)"; 606 | 607 | if ([typeNameString isEqualToString:@(@encode(CGAffineTransform))]) 608 | return @"(CGAffineTransform)"; 609 | 610 | if ([typeNameString isEqualToString:@(@encode(UIEdgeInsets))]) 611 | return @"(UIEdgeInsets)"; 612 | 613 | if ([typeNameString isEqualToString:@(@encode(NSRange))]) 614 | return @"(NSRange)"; 615 | 616 | if ([typeNameString isEqualToString:@(@encode(CFStringRef))]) 617 | return @"(CFStringRef)"; 618 | 619 | if ([typeNameString isEqualToString:@(@encode(NSZone *))]) 620 | return @"(NSZone *)"; 621 | 622 | // if ([typeNameString isEqualToString:@(@encode(CGAffineTransform))]) 623 | // return @"(CGAffineTransform)"; 624 | 625 | 626 | /* 627 | [array type] An array 628 | {name=type...} A structure 629 | (name=type...) A union 630 | bnum A bit field of num bits 631 | ^type A pointer to type 632 | ? An unknown type (among other things, this code is used for function pointers) 633 | */ 634 | 635 | return [NSString stringWithFormat:@"(%@)", typeNameString]; 636 | } 637 | 638 | + (NSString *) dump 639 | { 640 | NSMutableString *dump = [NSMutableString string]; 641 | 642 | [dump appendFormat:@"%@ ", [[self.superclasses valueForKey:@"description"] componentsJoinedByString:@" : "]]; 643 | 644 | NSDictionary *protocols = [self protocols]; 645 | NSMutableSet *protocolSet = [NSMutableSet set]; 646 | for (NSString *key in protocols.allKeys) 647 | [protocolSet addObjectsFromArray:protocols[key]]; 648 | [dump appendFormat:@"<%@>\n", [protocolSet.allObjects componentsJoinedByString:@", "]]; 649 | 650 | [dump appendString:@"{\n"]; 651 | unsigned int num; 652 | Ivar *ivars = class_copyIvarList(self, &num); 653 | for (int i = 0; i < num; i++) 654 | { 655 | const char *ivname = ivar_getName(ivars[i]); 656 | const char *typename = ivar_getTypeEncoding(ivars[i]); 657 | [dump appendFormat:@" %@ %s\n", [self typeForString:typename], ivname]; 658 | } 659 | free(ivars); 660 | [dump appendString:@"}\n\n"]; 661 | 662 | BOOL hasProperty = NO; 663 | NSArray *properties = [self getPropertyListForClass]; 664 | for (NSString *property in properties) 665 | { 666 | hasProperty = YES; 667 | objc_property_t prop = class_getProperty(self, property.UTF8String); 668 | 669 | [dump appendString:@" @property "]; 670 | 671 | char *nonatomic = property_copyAttributeValue(prop, "N"); 672 | char *readonly = property_copyAttributeValue(prop, "R"); 673 | char *copyAt = property_copyAttributeValue(prop, "C"); 674 | char *strong = property_copyAttributeValue(prop, "&"); 675 | NSMutableArray *attributes = [NSMutableArray array]; 676 | if (nonatomic) [attributes addObject:@"nonatomic"]; 677 | [attributes addObject:strong ? @"strong" : @"assign"]; 678 | [attributes addObject:readonly ? @"readonly" : @"readwrite"]; 679 | if (copyAt) [attributes addObject:@"copy"]; 680 | [dump appendFormat:@"(%@) ", [attributes componentsJoinedByString:@", "]]; 681 | free(nonatomic); 682 | free(readonly); 683 | free(copyAt); 684 | free(strong); 685 | 686 | char *typeName = property_copyAttributeValue(prop, "T"); 687 | [dump appendFormat:@"%@ ", [self typeForString:typeName]]; 688 | free(typeName); 689 | 690 | char *setterName = property_copyAttributeValue(prop, "S"); 691 | char *getterName = property_copyAttributeValue(prop, "G"); 692 | if (setterName || getterName) 693 | [dump appendFormat:@"(setter=%s, getter=%s)", setterName, getterName]; 694 | [dump appendFormat:@" %@\n", property]; 695 | free(setterName); 696 | free(getterName); 697 | } 698 | if (hasProperty) [dump appendString:@"\n"]; 699 | 700 | // Thanks Sam M for arg offset advice. 701 | 702 | Method *clMethods = class_copyMethodList(objc_getMetaClass(self.description.UTF8String), &num); 703 | for (int i = 0; i < num; i++) 704 | { 705 | char returnType[1024]; 706 | method_getReturnType(clMethods[i], returnType, 1024); 707 | NSString *rType = [self typeForString:returnType]; 708 | [dump appendFormat:@"+ %@ ", rType]; 709 | 710 | NSString *selectorString = NSStringFromSelector(method_getName(clMethods[i])); 711 | NSArray *components = [selectorString componentsSeparatedByString:@":"]; 712 | int argCount = method_getNumberOfArguments(clMethods[i]) - 2; 713 | if (argCount > 0) 714 | { 715 | for (unsigned int j = 0; j < argCount; j++) 716 | { 717 | NSString *arg = @"argument"; 718 | char argType[1024]; 719 | method_getArgumentType(clMethods[i], j + 2, argType, 1024); 720 | NSString *typeStr = [self typeForString:argType]; 721 | [dump appendFormat:@"%@:%@%@ ", components[j], typeStr, arg]; 722 | } 723 | [dump appendString:@"\n"]; 724 | } 725 | else 726 | { 727 | [dump appendFormat:@"%@\n", selectorString]; 728 | } 729 | } 730 | free(clMethods); 731 | 732 | [dump appendString:@"\n"]; 733 | Method *methods = class_copyMethodList(self, &num); 734 | for (int i = 0; i < num; i++) 735 | { 736 | char returnType[1024]; 737 | method_getReturnType(methods[i], returnType, 1024); 738 | NSString *rType = [self typeForString:returnType]; 739 | [dump appendFormat:@"- %@ ", rType]; 740 | 741 | NSString *selectorString = NSStringFromSelector(method_getName(methods[i])); 742 | NSArray *components = [selectorString componentsSeparatedByString:@":"]; 743 | int argCount = method_getNumberOfArguments(methods[i]) - 2; 744 | if (argCount > 0) 745 | { 746 | for (unsigned int j = 0; j < argCount; j++) 747 | { 748 | NSString *arg = @"argument"; 749 | char argType[1024]; 750 | method_getArgumentType(methods[i], j + 2, argType, 1024); 751 | NSString *typeStr = [self typeForString:argType]; 752 | [dump appendFormat:@"%@:%@%@ ", components[j], typeStr, arg]; 753 | } 754 | [dump appendString:@"\n"]; 755 | } 756 | else 757 | { 758 | [dump appendFormat:@"%@\n", selectorString]; 759 | } 760 | } 761 | free(methods); 762 | 763 | return dump; 764 | } 765 | 766 | - (NSString *) dump 767 | { 768 | return [[self class] dump]; 769 | } 770 | 771 | // Choose the first selector that an object can respond to 772 | // Thank Kevin Ballard for assist! 773 | - (SEL) chooseSelector: (SEL) aSelector, ... 774 | { 775 | if ([self respondsToSelector:aSelector]) return aSelector; 776 | 777 | va_list selectors; 778 | va_start(selectors, aSelector); 779 | SEL selector = va_arg(selectors, SEL); 780 | while (selector) 781 | { 782 | if ([self respondsToSelector:selector]) return selector; 783 | selector = va_arg(selectors, SEL); 784 | } 785 | 786 | return NULL; 787 | } 788 | 789 | #pragma mark - Safe Perform 790 | 791 | #pragma clang diagnostic push 792 | #pragma clang diagnostic ignored "-Wundeclared-selector" 793 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 794 | - (id) safePerformSelector: (SEL) selector withObject: (NSObject *) object1 withObject: (NSObject *) object2 795 | { 796 | if ([self respondsToSelector:selector]) 797 | return [self performSelector:selector withObject:object1 withObject:object2]; 798 | return nil; 799 | } 800 | 801 | - (id) safePerformSelector: (SEL) selector withObject: (NSObject *) object1 802 | { 803 | return [self safePerformSelector:selector withObject:object1 withObject:nil]; 804 | } 805 | 806 | - (id) safePerformSelector: (SEL) selector 807 | { 808 | return [self safePerformSelector:selector withObject:nil withObject:nil]; 809 | } 810 | #pragma clang diagnostic pop 811 | @end -------------------------------------------------------------------------------- /do-git: -------------------------------------------------------------------------------- 1 | #! /bin/csh -f 2 | 3 | if ($#argv != 1) then 4 | echo "Usage $0 commit-message" 5 | exit 1 6 | endif 7 | 8 | rm -rf build 9 | rm .DS_Store 10 | rm */.DS_Store 11 | rm -rf *.xcodeproj/ericasadun.* 12 | git add -A 13 | git commit -m "$1" 14 | git push 15 | --------------------------------------------------------------------------------