├── README ├── SwizzleKit.h └── SwizzleKit.m /README: -------------------------------------------------------------------------------- 1 | Welcome to the initial work on SwizzleKit 2 | 3 | SwizzleKit is intended to be a collection of macros, functions, categories and classes to facilitate a systematic way of extending closed sourced code. 4 | 5 | Rationale 6 | 7 | Writing plugins for closed sourced applications is difficult and has become more difficult with 10.6 in 64bit mode. 8 | 9 | Writing stable and successful plugins require a good degree of methodical work to do trivial task such as writing categories, accessing ivars in methods. SwizzleKit provides a formal way to do this so that theses tasks are bug free. 10 | 11 | If you discover problems in the code, comment and, if possible, let me know what is a better way to do these things. 12 | 13 | Use at your own risk. If using SwizzleKit results in horrible things, I absolve myself of all responsibility. 14 | 15 | Have Fun. -------------------------------------------------------------------------------- /SwizzleKit.h: -------------------------------------------------------------------------------- 1 | 2 | // SwizzleKit 3 | // Created by Scott Morrison on 12/01/09. 4 | // ------------------------------------------------------------------------ 5 | // Copyright (c) 2009, Scott Morrison . 6 | // Some licensing details are going to be here at some point in the future 7 | // 8 | // ------------------------------------------------------------------------ 9 | 10 | 11 | // Underimplemented!!!! 12 | // 13 | // This is a work in progress. somethings probaby won't work. 14 | // have fun! 15 | 16 | 17 | #import 18 | #import 19 | #include 20 | #include 21 | 22 | 23 | #define BUNDLE_IDENTIFIER @"com.yourBundle.identifier" 24 | 25 | 26 | // Unique Prefixes 27 | // 28 | // if you are using SwizzleKit for use in plugins into host applications, there is the possibility of multiple plugins using swizzlekit, leading to naming collisions. Defining a unique prefix should prevent this (unless, ofcourse, you use a prefix already in use. 29 | // usage: 30 | // 31 | // perfom a find and replace of UNIQUE_PREFIX with the unique prefix for your bundle \ 32 | 33 | 34 | 35 | 36 | // working with private classes necessitates performing lookups of hidden classes at runtime in order to properly extend classes and swizzle methods etc. 37 | // define a convenience macro to perform this frequently used call. 38 | 39 | #define CLS(className) NSClassFromString([NSString stringWithFormat:@"%s",#className]) 40 | 41 | // if you dynamicly subclass a hidden class at runtime, using another (NSObject) class as a template, there is a problem using [super method] message sending as the super keyword will 42 | // refer to the compiled superclass, not the target superclass. the SUPER(...) macro will look up the target superclass at runtime and send the message to this class. 43 | // NOTE this will return a NSObject * result. If you need to have a struct result, you will need to use objc_msgSendSuper_stret function. Do your research. 44 | 45 | #define SUPER(...) objc_msgSendSuper(&(struct objc_super){self, class_getSuperclass([self class])},_cmd, ##__VA_ARGS__) 46 | 47 | 48 | //respondsDirectlyToSelector method returns YES if this object implements the selector directly. 49 | // Returns NO if any superclass implements the selector or no superclass implemention. 50 | 51 | @interface NSObject (UNIQUE_PREFIXswizzleKit) 52 | -(BOOL)UNIQUE_PREFIXrespondsDirectlyToSelector:(SEL)aSelector; 53 | @end 54 | 55 | 56 | // describeClass is a quick and dirty function to be called in gdb to get the details of a class (ivars, methods, super methods etc) 57 | 58 | void UNIQUE_PREFIXdescribeClass(const char * clsName); 59 | 60 | 61 | // BACKTRACE MACRO 62 | // This is used in debugging and code analysis 63 | // adding BACKTRACE(NO|YES) to a swizzled method will log out the backtrace to console and continue (or break) 64 | #define BACKTRACE(shouldBreak) \ 65 | NSLog(@"----------- DEBUG ----------\nSelf: %@\n Thread: %@\nThreadDictionary: %@\nBackTrace %@",self, [NSThread currentThread],[[NSThread currentThread] threadDictionary],[NSThread abbreviatedCallStackSymbols]); \ 66 | if (shouldBreak){ \ 67 | Debugger(); \ 68 | } \ 69 | 70 | 71 | // the following macro will implement a maptable for a class for the maintenace of extra 'ivars' for an object. 72 | // in doing so it will swizzle the dealloc method for PREFIX_MapTable_dealloc 73 | // the PREFIX_MapTable_dealloc will call PREFIX_dealloc if it exists so that any other clean up can be done bereo the mapTable variabls are released. 74 | // It will also create a class Method +mapTable that will create the maptable on the first call (access though the accessors function above. 75 | 76 | // The mapTableVariables should be accessed ONLY through the use of the functions declared above 77 | // 78 | 79 | #define IMPLEMENT_MAPTABLE_VARIABLES_USING_PREFIX(prefix) \ 80 | static NSMapTable *_mappedViewerIVars = NULL; \ 81 | static NSLock * _mapTableLock = nil; \ 82 | \ 83 | +(NSMapTable* )mapTable{ \ 84 | if (!_mappedViewerIVars){ \ 85 | _mappedViewerIVars = NSCreateMapTableWithZone(NSNonOwnedPointerMapKeyCallBacks, NSObjectMapValueCallBacks, 3, [self zone]); \ 86 | _mapTableLock= [[NSLock alloc] init]; \ 87 | Method oldDeallocMethod = class_getInstanceMethod(self, @selector(dealloc)); \ 88 | Method newDeallocMethod = class_getInstanceMethod(self, @selector(##prefix_MapTable_dealloc)); \ 89 | __originalDeallocIMP = method_getImplementation(oldDeallocMethod); /* this may be handy to keep around */ \ 90 | NSAssert (oldDeallocMethod && newDeallocMethod ,@"Could not swizzle a dealloc method for MapTable Implementation"); \ 91 | /* try to add a dealloc method to this class -- if successful,then redirect the dealloc method to ##prefix_MapTable_dealloc */ \ 92 | /* Technique taken from Mike Ash http://mikeash.com/?page=pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html*/ \ 93 | if (class_addMethod(self, @selector(dealloc), method_getImplementation(newDeallocMethod), method_getTypeEncoding(newDeallocMethod))){ \ 94 | class_replaceMethod(self,@selector(##prefix_MapTable_dealloc),method_getImplementation(oldDeallocMethod), method_getTypeEncoding(newDeallocMethod)); \ 95 | } \ 96 | else { \ 97 | /* not successful in adding a dealloc method so just do a straightup swizzle. */ \ 98 | method_exchangeImplementations(oldDeallocMethod, newDeallocMethod); \ 99 | } \ 100 | } \ 101 | [_mapTableLock lock]; \ 102 | id mapTable = [_mappedViewerIVars retain]; \ 103 | [_mapTableLock unlock]; \ 104 | return [mapTable autorelease]; \ 105 | } \ 106 | \ 107 | - (void)##prefix_MapTable_dealloc{ \ 108 | if([self respondsDirectlyToSelector:@selector(##prefixdealloc)]) \ 109 | [self ##prefixdealloc]; \ 110 | [_mapTableLock lock]; \ 111 | if(_mappedViewerIVars){ \ 112 | NSMapRemove(_mappedViewerIVars,self); \ 113 | } \ 114 | [_mapTableLock unlock]; \ 115 | [self ##prefix_MapTable_dealloc]; \ 116 | } \ 117 | \ 118 | - (id) ##prefixvariables{ \ 119 | id anObject = self; \ 120 | static NSMapTable * mapTable = nil; \ 121 | if (!mapTable){ \ 122 | if ([[anObject class] respondsToSelector:@selector(mapTable)]){ \ 123 | mapTable = [[anObject class] mapTable]; \ 124 | } \ 125 | } \ 126 | id theValue = nil; \ 127 | if (mapTable){ \ 128 | NSMutableDictionary *aDict = nil; \ 129 | @synchronized(mapTable){ \ 130 | aDict = [NSMapGet(mapTable, anObject) retain]; \ 131 | } \ 132 | return [aDict autorelease]; \ 133 | } \ 134 | return nil; \ 135 | \ 136 | } \ 137 | - (id) ##prefixvariable:(NSString*) variableName{ \ 138 | id anObject = self; \ 139 | static NSMapTable * mapTable = nil; \ 140 | if (!mapTable){ \ 141 | if ([[anObject class] respondsToSelector:@selector(mapTable)]){ \ 142 | mapTable = [[anObject class] mapTable]; \ 143 | } \ 144 | } \ 145 | id theValue = nil; \ 146 | if (mapTable){ \ 147 | @synchronized(mapTable){ \ 148 | NSMutableDictionary *aDict; \ 149 | aDict = NSMapGet(mapTable, anObject); \ 150 | if (nil == aDict){ \ 151 | aDict = [NSMutableDictionary dictionary]; \ 152 | NSMapInsert(mapTable, anObject, aDict); \ 153 | } \ 154 | theValue = [aDict objectForKey:variableName ] ; \ 155 | } \ 156 | } \ 157 | return [[theValue retain] autorelease]; \ 158 | } \ 159 | - (void) set##prefixvariable:( NSString*)variableName toValue:(id) value{ \ 160 | id anObject= self; \ 161 | static NSMapTable * mapTable = nil; \ 162 | if (!mapTable){ \ 163 | if ([[anObject class] respondsToSelector:@selector(mapTable)]){ \ 164 | mapTable = [[anObject class] mapTable]; \ 165 | } \ 166 | } \ 167 | if (mapTable){ \ 168 | @synchronized(mapTable){ \ 169 | NSMutableDictionary *aDict; \ 170 | aDict = NSMapGet(mapTable, anObject); \ 171 | if (nil == aDict){ \ 172 | aDict = [NSMutableDictionary dictionary]; \ 173 | NSMapInsert(mapTable, anObject, aDict); \ 174 | } \ 175 | if (value){ \ 176 | [aDict setObject:value forKey:variableName]; \ 177 | } \ 178 | else{ \ 179 | [aDict removeObjectForKey:variableName]; \ 180 | } \ 181 | } \ 182 | } \ 183 | \ 184 | } \ 185 | 186 | 187 | id UNIQUE_PREFIXobject_getMapTableVariable(id anObject, const char* variableName); 188 | void UNIQUE_PREFIXobject_setMapTableVariable(id anObject, const char* variableName,id value); 189 | 190 | 191 | 192 | // declare functions for getting MapTabled variables. 193 | // The functions should be thread safe (using @synchronize) 194 | // note that because of the nature of mapTables, the setter will always retain the value passed in. 195 | // make copies prior to adding to the function 196 | // for assignment, wrap the value in an NSValue Object. 197 | 198 | 199 | 200 | // The following Macros standardize the creation of getters and setters for ivars 201 | #define IVAR_OBJECT_GETTER(getterName,ivarName) \ 202 | -(id)getterName{ \ 203 | static Ivar ivar =0; \ 204 | if (!ivar) \ 205 | ivar = class_getInstanceVariable([self class], #ivarName); \ 206 | return [[object_getIvar(self, ivar) retain] autorelease]; \ 207 | } \ 208 | 209 | #define IVAR_OBJECT_SETTER_RETAIN(setterName,ivarName) \ 210 | -(void)setterName:(id)value { \ 211 | static Ivar ivar =0; \ 212 | if (!ivar) \ 213 | ivar = class_getInstanceVariable([self class], #ivarName); \ 214 | id old = object_getIvar(self, ivar); \ 215 | static NSMutableString* key=0; \ 216 | if (!key) { \ 217 | key=[[NSMutableString alloc] initWithFormat:@"%s",#setterName]; \ 218 | NSString * keyInitial =[key substringWithRange:NSMakeRange(3,1)]; \ 219 | [key replaceCharactersInRange:NSMakeRange(0,4) withString:keyInitial]; \ 220 | }\ 221 | [self willChangeValueForKey: key]; \ 222 | object_setIvar(self,ivar,[value retain]); \ 223 | [self didChangeValueForKey: key]; \ 224 | [old release]; \ 225 | } \ 226 | 227 | #define IVAR_OBJECT_SETTER_COPY(setterName,ivarName) \ 228 | -(void)setterName:(id)value { \ 229 | static Ivar ivar =0; \ 230 | if (!ivar) \ 231 | ivar = class_getInstanceVariable([self class], #ivarName); \ 232 | id old = object_getIvar(self, ivar); \ 233 | static NSMutableString* key =0; \ 234 | if (!key) { \ 235 | key=[[NSMutableString alloc] initWithFormat:@"%s",#setterName]; \ 236 | NSString * keyInitial =[key substringWithRange:NSMakeRange(3,1)]; \ 237 | [key replaceCharactersInRange:NSMakeRange(0,4) withString:keyInitial]; \ 238 | }\ 239 | [self willChangeValueForKey: key]; \ 240 | object_setIvar(self,ivar,[value copy]); \ 241 | [self didChangeValueForKey: key]; \ 242 | [old release]; \ 243 | } \ 244 | 245 | #define IVAR_OBJECT_SETTER_ASSIGN(setterName,ivarName) \ 246 | -(void)setterName:(id)value { \ 247 | static Ivar ivar =0; \ 248 | if (!ivar) \ 249 | ivar = class_getInstanceVariable([self class], #ivarName); \ 250 | static NSMutableString* key =0; \ 251 | if (!key) { \ 252 | key=[[NSMutableString alloc] initWithFormat:@"%s",#setterName]; \ 253 | NSString * keyInitial =[key substringWithRange:NSMakeRange(3,1)]; \ 254 | [key replaceCharactersInRange:NSMakeRange(0,4) withString:keyInitial]; \ 255 | }\ 256 | [self willChangeValueForKey: key]; \ 257 | object_setIvar(self,ivar,value); \ 258 | [self didChangeValueForKey: key]; \ 259 | } \ 260 | 261 | #define IVAR_OBJECT_ACCCESSORS_RETAIN(getterName,setterName,ivarName) \ 262 | IVAR_OBJECT_GETTER(getterName,ivarName) \ 263 | IVAR_OBJECT_SETTER_RETAIN(setterName,ivarName) 264 | 265 | #define IVAR_OBJECT_ACCCESSORS_COPY(getterName,setterName,ivarName) \ 266 | IVAR_OBJECT_GETTER(getterName,ivarName) \ 267 | IVAR_OBJECT_SETTER_COPY(setterName,ivarName) 268 | 269 | #define IVAR_OBJECT_ACCCESSORS_ASSIGN(getterName,setterName,ivarName) \ 270 | IVAR_OBJECT_GETTER(getterName,ivarName) \ 271 | IVAR_OBJECT_SETTER_ASSIGN(setterName,ivarName) 272 | 273 | 274 | #define IVAR_TYPE_GETTER(getterName,ivarName,type) \ 275 | -(type)getterName{ \ 276 | static Ivar ivar =0; \ 277 | if (!ivar) \ 278 | ivar = class_getInstanceVariable([self class], #ivarName); \ 279 | NSInteger offset = ivar_getOffset(ivar); \ 280 | type result =*(type*) ((NSInteger)self + offset); \ 281 | return result; \ 282 | } 283 | 284 | #define IVAR_TYPE_SETTER(setterName,ivarName,type) \ 285 | -(void)setterName:(type)value{ \ 286 | static Ivar ivar =0; \ 287 | if (!ivar) \ 288 | ivar = class_getInstanceVariable([self class], #ivarName); \ 289 | static NSMutableString* key=0; \ 290 | if (!key) { \ 291 | key=[[NSMutableString alloc] initWithFormat:@"%s",#setterName]; \ 292 | NSString * keyInitial =[key substringWithRange:NSMakeRange(3,1)]; \ 293 | [key replaceCharactersInRange:NSMakeRange(0,4) withString:keyInitial]; \ 294 | }\ 295 | NSInteger offset = ivar_getOffset(ivar); \ 296 | [self willChangeValueForKey: key]; \ 297 | *(type*)((NSInteger)self + offset)=value; \ 298 | [self didChangeValueForKey: key]; \ 299 | } 300 | 301 | 302 | 303 | @interface UNIQUE_PREFIXSwizzler : NSObject{ 304 | } 305 | +(void)setPrefix:(NSString*)prefix; 306 | +(void)setProviderSuffix:(NSString*)suffix; 307 | +(Class)subclass:(Class)baseClass usingClassName:(NSString*)subclassName providerClass:(Class)providerClass; 308 | +(void)extendClass:(Class) targetClass withMethodsFromClass:(Class)providerClass; 309 | +(BOOL)addClassMethodName:(NSString *)methodName fromProviderClass:(Class)providerClass toClass:(Class)targetClass; 310 | +(BOOL)addInstanceMethodName:(NSString *)methodName fromProviderClass:(Class)providerClass toClass:(Class)targetClass; 311 | +(IMP)swizzleClassMethod:(NSString*)methodName forClass:(Class)targetClass; 312 | +(IMP)swizzleInstanceMethod:(NSString*)methodName forClass:(Class)targetClass; 313 | 314 | 315 | @end 316 | 317 | 318 | @end 319 | 320 | 321 | @interface NSThread (SwizzleKit) 322 | +(NSArray*)abbreviatedCallStackSymbols; 323 | @end 324 | 325 | 326 | -------------------------------------------------------------------------------- /SwizzleKit.m: -------------------------------------------------------------------------------- 1 | 2 | // SwizzleKit 3 | // Created by Scott Morrison on 12/01/09. 4 | // ------------------------------------------------------------------------ 5 | // Copyright (c) 2009, Scott Morrison All rights reserved. 6 | // 7 | // 8 | // ------------------------------------------------------------------------ 9 | 10 | #import "SwizzleKit.m" 11 | #define SWIZZLE_PREFIX @"UNIQUE_PREFIX" 12 | #define PROVIDER_SUFFIX @"UNIQUE_PREFIX" 13 | 14 | @implementation NSObject (UNIQUE_PREFIXSwizzleKit) 15 | 16 | -(BOOL)UNIQUE_PREFIXrespondsDirectlyToSelector:(SEL)aSelector{ 17 | BOOL responds = NO; 18 | unsigned int methodCount = 0; 19 | Method * methods = nil; 20 | 21 | // extend instance Methods 22 | methods = class_copyMethodList([self class], &methodCount); 23 | int ci= methodCount; 24 | while (methods && ci--){ 25 | if (method_getName(methods[ci]) == aSelector){ 26 | responds = YES; 27 | break; 28 | } 29 | } 30 | free(methods); 31 | return responds; 32 | } 33 | 34 | @end 35 | 36 | 37 | id UNIQUE_PREFIXobject_getMapTableVariable(id anObject, const char* variableName){ 38 | static NSMapTable * mapTable = nil; 39 | if (!mapTable){ 40 | if ([[anObject class] respondsToSelector:@selector(mapTable)]){ 41 | mapTable = [[anObject class] mapTable]; 42 | } 43 | } 44 | id theValue = nil; 45 | if (mapTable){ 46 | @synchronized(mapTable){ 47 | NSMutableDictionary *aDict; 48 | aDict = NSMapGet(mapTable, anObject); 49 | if (nil == aDict){ 50 | aDict = [NSMutableDictionary dictionary]; 51 | NSMapInsert(mapTable, anObject, aDict); 52 | } 53 | theValue = [aDict objectForKey:[NSString stringWithFormat:@"%s",variableName] ] ; 54 | } 55 | } 56 | return theValue; 57 | } 58 | 59 | void UNIQUE_PREFIXobject_setMapTableVariable(id anObject, const char* variableName,id value){ 60 | static NSMapTable * mapTable = nil; 61 | if (!mapTable){ 62 | if ([[anObject class] respondsToSelector:@selector(mapTable)]){ 63 | mapTable = [[anObject class] mapTable]; 64 | } 65 | } 66 | if (mapTable){ 67 | @synchronized(mapTable){ 68 | NSMutableDictionary *aDict; 69 | aDict = NSMapGet(mapTable, anObject); 70 | if (nil == aDict){ 71 | aDict = [NSMutableDictionary dictionary]; 72 | NSMapInsert(mapTable, anObject, aDict); 73 | } 74 | if (value){ 75 | [aDict setObject:value forKey:[NSString stringWithFormat:@"%s",variableName]]; 76 | } 77 | else{ 78 | [aDict removeObjectForKey:[NSString stringWithFormat:@"%s",variableName]]; 79 | } 80 | } 81 | } 82 | 83 | } 84 | 85 | void UNIQUE_PREFIXdescribeClass(const char * clsName){ 86 | Class aClass = objc_getClass(clsName); 87 | if (aClass){ 88 | NSMutableString * logString = [NSMutableString string]; 89 | Class superClass = class_getSuperclass(aClass); 90 | const char * superClassName = class_getName(superClass); 91 | [logString appendFormat:@"@interface %s : %s\n{",clsName,superClassName]; 92 | unsigned int ivarCount = 0; 93 | NSUInteger ci =0; 94 | Ivar * ivars = class_copyIvarList(aClass, &ivarCount); 95 | for (ci=0;ci stackFrames from the backtrace 348 | void* callstack[128]; 349 | int i, frames = backtrace(callstack, 128); 350 | char** strs = backtrace_symbols(callstack, frames); 351 | NSMutableArray *callStack = [[NSMutableArray alloc] initWithCapacity:frames]; 352 | int maxFrame = MIN(frames,frameCount+1); 353 | 354 | for (i = 1; i < maxFrame; ++i) { 355 | NSString * frameString = [[NSString alloc] initWithUTF8String:strs[i]]; 356 | NSScanner * scanner = [NSScanner scannerWithString:frameString]; 357 | NSString * dummy = nil; 358 | [scanner scanCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString: &dummy]; 359 | NSString * frameNumber = nil; 360 | [scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&frameNumber]; 361 | [scanner scanCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString: &dummy]; 362 | NSString * module = nil; 363 | [scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&module]; 364 | [scanner scanCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString: &dummy]; 365 | NSString * address = nil; 366 | [scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&address]; 367 | [scanner scanCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString: &dummy]; 368 | NSString * method = nil; 369 | [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&method]; 370 | [callStack addObject:[NSString stringWithFormat:@"%3-s %18-s %@",[frameNumber UTF8String],[module UTF8String],method]]; 371 | 372 | } 373 | free(strs); 374 | 375 | return [callStack autorelease]; 376 | 377 | } 378 | @end 379 | 380 | --------------------------------------------------------------------------------