root/trunk/cocoa/JRSwizzle/JRSwizzle.m

Revision 270, 4.2 kB (checked in by wolf, 1 year ago)

[NEW] JRSwizzle.

Line 
1 /*******************************************************************************
2         JRSwizzle.m
3                 Copyright (c) 2007 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
4                 Some rights reserved: <http://opensource.org/licenses/mit-license.php>
5
6         ***************************************************************************/
7
8 #import "JRSwizzle.h"
9 #import <objc/objc-class.h>
10
11 #define SetNSError(ERROR_VAR, FORMAT,...)       \
12         if (ERROR_VAR) {        \
13                 NSString *errStr = [@"+[NSObject(JRSwizzle) jr_swizzleMethod:withMethod:error:]: " stringByAppendingFormat:FORMAT,##__VA_ARGS__];       \
14                 *ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \
15                                                                                  code:-1        \
16                                                                          userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \
17         }
18
19 @implementation NSObject (JRSwizzle)
20
21 + (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ {
22 #if OBJC_API_VERSION >= 2
23         Method origMethod = class_getInstanceMethod(self, origSel_);
24         if (!origMethod) {
25                 SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);
26                 return NO;
27         }
28        
29         Method altMethod = class_getInstanceMethod(self, altSel_);
30         if (!altMethod) {
31                 SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);
32                 return NO;
33         }
34        
35         class_addMethod(self,
36                                         origSel_,
37                                         class_getMethodImplementation(self, origSel_),
38                                         method_getTypeEncoding(origMethod));
39         class_addMethod(self,
40                                         altSel_,
41                                         class_getMethodImplementation(self, altSel_),
42                                         method_getTypeEncoding(altMethod));
43        
44         method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_));
45         return YES;
46 #else
47         //      Scan for non-inherited methods.
48         Method directOriginalMethod = NULL, directAlternateMethod = NULL;
49        
50         void *iterator = NULL;
51         struct objc_method_list *mlist = class_nextMethodList(self, &iterator);
52         while (mlist) {
53                 int method_index = 0;
54                 for (; method_index < mlist->method_count; method_index++) {
55                         if (mlist->method_list[method_index].method_name == origSel_) {
56                                 assert(!directOriginalMethod);
57                                 directOriginalMethod = &mlist->method_list[method_index];
58                         }
59                         if (mlist->method_list[method_index].method_name == altSel_) {
60                                 assert(!directAlternateMethod);
61                                 directAlternateMethod = &mlist->method_list[method_index];
62                         }
63                 }
64                 mlist = class_nextMethodList(self, &iterator);
65         }
66        
67         //      If either method is inherited, copy it up to the target class to make it non-inherited.
68         if (!directOriginalMethod || !directAlternateMethod) {
69                 Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL;
70                 if (!directOriginalMethod) {
71                         inheritedOriginalMethod = class_getInstanceMethod(self, origSel_);
72                         if (!inheritedOriginalMethod) {
73                                 SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]);
74                                 return NO;
75                         }
76                 }
77                 if (!directAlternateMethod) {
78                         inheritedAlternateMethod = class_getInstanceMethod(self, altSel_);
79                         if (!inheritedAlternateMethod) {
80                                 SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]);
81                                 return NO;
82                         }
83                 }
84                
85                 int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1;
86                 struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1)));
87                 hoisted_method_list->method_count = hoisted_method_count;
88                 Method hoisted_method = hoisted_method_list->method_list;
89                
90                 if (!directOriginalMethod) {
91                         bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method));
92                         directOriginalMethod = hoisted_method++;
93                 }
94                 if (!directAlternateMethod) {
95                         bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method));
96                         directAlternateMethod = hoisted_method;
97                 }
98                 class_addMethods(self, hoisted_method_list);
99         }
100        
101         //      Swizzle.
102         IMP temp = directOriginalMethod->method_imp;
103         directOriginalMethod->method_imp = directAlternateMethod->method_imp;
104         directAlternateMethod->method_imp = temp;
105        
106         return YES;
107 #endif
108 }
109
110 + (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ {
111         assert(0);
112         return NO;
113 }
114
115 @end
Note: See TracBrowser for help on using the browser.