├── .gitignore
├── LICENSE
├── README.md
├── RSSwizzle.podspec
├── RSSwizzle
├── RSSwizzle.h
└── RSSwizzle.m
└── RSSwizzleTests
├── RSSwizzleTests-OSX
├── RSSwizzleTests-OSX-Info.plist
├── RSSwizzleTests-OSX-Prefix.pch
└── en.lproj
│ └── InfoPlist.strings
├── RSSwizzleTests-iOS
├── RSSwizzleTests-iOS-Info.plist
├── RSSwizzleTests-iOS-Prefix.pch
└── en.lproj
│ └── InfoPlist.strings
├── RSSwizzleTests.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
└── RSSwizzleTests
├── RSSwizzleTests.m
├── RSTestsLog.h
└── RSTestsLog.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | .DS_Store
3 | */build/*
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | profile
14 | *.moved-aside
15 | DerivedData
16 | .idea/
17 | *.hmap
18 | *.xccheckout
19 |
20 | #CocoaPods
21 | Pods
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Yan Rabovik
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # RSSwizzle
3 | Safe method swizzling done right.
4 |
5 | ## Motivation
6 |
7 | Classical method swizzling with `method_exchangeImplementations` is quite simple, but it has a lot of limitations:
8 |
9 | * It is safe only if swizzling is done in the `+load` method. If you need to swizzle methods during application's lifetime you should take into account that third-party code may do swizzling of the same method in another thread at the same time.
10 | * The swizzled method must be implemented by the class itself and not by superclasses. Workarounds by copying implementation from the superclass do not really work. Original implementation in the superclass must be fetched at the time of calling, not at the time of swizzling ([1][774],[2][775]).
11 | * The swizzled method implementation must not rely on the `_cmd` argument. _(And generally you can not be sure in it ([5][cmd]).)_
12 | * Naming conflicts are possible ([3][SO]).
13 |
14 | For more details see discussions in: [1][774], [2][775], [3][SO], [4][TH], [5][cmd].
15 |
16 | **RSSwizzle** avoids all these known pitfalls.
17 |
18 | ## Usage
19 |
20 | Original implementation must always be called from the new implementation. And because of the the fact that for safe and robust swizzling original implementation must be dynamically fetched at the time of calling and not at the time of swizzling ([1][774],[2][775]), swizzling API is slightly unusual.
21 |
22 | Example for swizzling `-(int)calculate:(int)number;` method:
23 |
24 | ```objective-c
25 | RSSwizzleInstanceMethod(classToSwizzle,
26 | @selector(calculate:),
27 | RSSWReturnType(int),
28 | RSSWArguments(int number),
29 | RSSWReplacement(
30 | {
31 | // The following code will be used as the new implementation.
32 |
33 | // Calling original implementation.
34 | int res = RSSWCallOriginal(number);
35 | // Returning modified return value.
36 | return res + 1;
37 | }), 0, NULL);
38 | ```
39 |
40 | #### Alternative API
41 |
42 | Alternatively you may use an API without macros, though it is a little bit complicated.
43 |
44 | You should pass a factory block that returns the block for the new implementation of the swizzled method. And use `swizzleInfo` argument to retrieve and call original implementation.
45 |
46 | Example for swizzling `-(int)calculate:(int)number;` method:
47 |
48 | ```objective-c
49 | SEL selector = @selector(calculate:);
50 | [RSSwizzle
51 | swizzleInstanceMethod:selector
52 | inClass:classToSwizzle
53 | newImpFactory:^id(RSSwizzleInfo *swizzleInfo) {
54 | // This block will be used as the new implementation.
55 | return ^int(__unsafe_unretained id self, int num){
56 | // You MUST always cast implementation to the correct function pointer.
57 | int (*originalIMP)(__unsafe_unretained id, SEL, int);
58 | originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
59 | // Calling original implementation.
60 | int res = originalIMP(self,selector,num);
61 | // Returning modified return value.
62 | return res + 1;
63 | };
64 | }
65 | mode:RSSwizzleModeAlways
66 | key:NULL];
67 | ```
68 |
69 | #### Class Method Swizzling
70 | Class method swizzling is done with a similar API.
71 |
72 | Example for swizzling `+(int)calculate:(int)number;` method:
73 |
74 | ```objective-c
75 | RSSwizzleClassMethod(classToSwizzle,
76 | @selector(calculate:),
77 | RSSWReturnType(int),
78 | RSSWArguments(int number),
79 | RSSWReplacement(
80 | {
81 | // Calling original implementation.
82 | int res = RSSWCallOriginal(number);
83 | // Returning modified return value.
84 | return res + 1;
85 | }));
86 | ```
87 |
88 |
89 | #### Modes
90 |
91 | Swizzling frequently goes along with checking whether this particular class (or one of its superclasses) has been already swizzled. Here the `mode` and `key` parameters can help.
92 | Possible mode values:
93 |
94 | * `RSSwizzleModeAlways` **RSSwizzle** always does swizzling regardless of the given `key`.
95 | * `RSSwizzleModeOncePerClass` **RSSwizzle** does not do swizzling if the same class has been swizzled earlier with the same `key`.
96 | * `RSSwizzleModeOncePerClassAndSuperclasses` **RSSwizzle** does not do swizzling if the same class or one of its superclasses have been swizzled earlier with the same `key`.
97 |
98 | Here is an example of swizzling `-(void)dealloc;` only in case when neither class and no one of its superclasses has been already swizzled with the given `key`:
99 |
100 | ```objective-c
101 | static const void *key = &key;
102 | SEL selector = NSSelectorFromString(@"dealloc");
103 | RSSwizzleInstanceMethod(classToSwizzle,
104 | selector,
105 | RSSWReturnType(void),
106 | RSSWArguments(),
107 | RSSWReplacement(
108 | {
109 | NSLog(@"Deallocating %@.",self);
110 | RSSWCallOriginal();
111 | }), RSSwizzleModeOncePerClassAndSuperclasses, key);
112 | ```
113 |
114 | > **Note:** `RSSwizzleModeOncePerClassAndSuperclasses ` mode does not guarantees that your implementation will be called only once per method call. If the order of swizzling is: first inherited class, second superclass; then both swizzlings will be done and the new implementation will be called twice.
115 |
116 | #### Thread safety
117 |
118 | **RSSwizzle** is fully thread safe. You do not need any additional synchronization.
119 |
120 | ## CocoaPods
121 | Add `RSSwizzle` to your _Podfile_.
122 |
123 | ## Requirements
124 | * iOS 5.0+
125 | * Mac OS X 10.7+
126 | * ARC
127 |
128 | ## Author
129 | Yan Rabovik ([@rabovik][twitter] on twitter)
130 |
131 | ## License
132 | MIT License.
133 |
134 | [twitter]: https://twitter.com/rabovik
135 | [cmd]: http://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html#comment-e2c2af6395d9e8fca559895bbd434ee8
136 | [SO]: http://stackoverflow.com/a/8636521/441735
137 | [774]: https://github.com/ReactiveCocoa/ReactiveCocoa/pull/774
138 | [775]: https://github.com/ReactiveCocoa/ReactiveCocoa/pull/775
139 | [TH]: https://github.com/th-in-gs/THObserversAndBinders/commit/cabe12dece2faabf5e58759363ac603be963c889#L1R231
--------------------------------------------------------------------------------
/RSSwizzle.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "RSSwizzle"
3 | s.version = "0.1.0"
4 | s.summary = "Safe method swizzling done right."
5 | s.homepage = "https://github.com/rabovik/RSSwizzle"
6 | s.license = 'MIT'
7 | s.author = { "Yan Rabovik" => "yan@rabovik.ru" }
8 | s.source = { :git => "https://github.com/rabovik/RSSwizzle.git", :tag => "#{s.version}" }
9 | s.ios.deployment_target = '5.0'
10 | s.osx.deployment_target = '10.7'
11 | s.source_files = 'RSSwizzle', 'RSSwizzle/**/*.{h,m}'
12 | s.frameworks = 'Foundation'
13 | s.requires_arc = true
14 | end
15 |
--------------------------------------------------------------------------------
/RSSwizzle/RSSwizzle.h:
--------------------------------------------------------------------------------
1 | //
2 | // RSSwizzle.h
3 | // RSSwizzleTests
4 | //
5 | // Created by Yan Rabovik on 05.09.13.
6 | //
7 | //
8 |
9 | #import
10 |
11 | #pragma mark - Macros Based API
12 |
13 | /// A macro for wrapping the return type of the swizzled method.
14 | #define RSSWReturnType(type) type
15 |
16 | /// A macro for wrapping arguments of the swizzled method.
17 | #define RSSWArguments(arguments...) _RSSWArguments(arguments)
18 |
19 | /// A macro for wrapping the replacement code for the swizzled method.
20 | #define RSSWReplacement(code...) code
21 |
22 | /// A macro for casting and calling original implementation.
23 | /// May be used only in RSSwizzleInstanceMethod or RSSwizzleClassMethod macros.
24 | #define RSSWCallOriginal(arguments...) _RSSWCallOriginal(arguments)
25 |
26 | #pragma mark └ Swizzle Instance Method
27 |
28 | /**
29 | Swizzles the instance method of the class with the new implementation.
30 |
31 | Example for swizzling `-(int)calculate:(int)number;` method:
32 |
33 | @code
34 |
35 | RSSwizzleInstanceMethod(classToSwizzle,
36 | @selector(calculate:),
37 | RSSWReturnType(int),
38 | RSSWArguments(int number),
39 | RSSWReplacement(
40 | {
41 | // Calling original implementation.
42 | int res = RSSWCallOriginal(number);
43 | // Returning modified return value.
44 | return res + 1;
45 | }), 0, NULL);
46 |
47 | @endcode
48 |
49 | Swizzling frequently goes along with checking whether this particular class (or one of its superclasses) has been already swizzled. Here the `RSSwizzleMode` and `key` parameters can help. See +[RSSwizzle swizzleInstanceMethod:inClass:newImpFactory:mode:key:] for details.
50 |
51 | Swizzling is fully thread-safe.
52 |
53 | @param classToSwizzle The class with the method that should be swizzled.
54 |
55 | @param selector Selector of the method that should be swizzled.
56 |
57 | @param RSSWReturnType The return type of the swizzled method wrapped in the RSSWReturnType macro.
58 |
59 | @param RSSWArguments The arguments of the swizzled method wrapped in the RSSWArguments macro.
60 |
61 | @param RSSWReplacement The code of the new implementation of the swizzled method wrapped in the RSSWReplacement macro.
62 |
63 | @param RSSwizzleMode The mode is used in combination with the key to indicate whether the swizzling should be done for the given class. You can pass 0 for RSSwizzleModeAlways.
64 |
65 | @param key The key is used in combination with the mode to indicate whether the swizzling should be done for the given class. May be NULL if the mode is RSSwizzleModeAlways.
66 |
67 | @return YES if successfully swizzled and NO if swizzling has been already done for given key and class (or one of superclasses, depends on the mode).
68 |
69 | */
70 | #define RSSwizzleInstanceMethod(classToSwizzle, \
71 | selector, \
72 | RSSWReturnType, \
73 | RSSWArguments, \
74 | RSSWReplacement, \
75 | RSSwizzleMode, \
76 | key) \
77 | _RSSwizzleInstanceMethod(classToSwizzle, \
78 | selector, \
79 | RSSWReturnType, \
80 | _RSSWWrapArg(RSSWArguments), \
81 | _RSSWWrapArg(RSSWReplacement), \
82 | RSSwizzleMode, \
83 | key)
84 |
85 | #pragma mark └ Swizzle Class Method
86 |
87 | /**
88 | Swizzles the class method of the class with the new implementation.
89 |
90 | Example for swizzling `+(int)calculate:(int)number;` method:
91 |
92 | @code
93 |
94 | RSSwizzleClassMethod(classToSwizzle,
95 | @selector(calculate:),
96 | RSSWReturnType(int),
97 | RSSWArguments(int number),
98 | RSSWReplacement(
99 | {
100 | // Calling original implementation.
101 | int res = RSSWCallOriginal(number);
102 | // Returning modified return value.
103 | return res + 1;
104 | }));
105 |
106 | @endcode
107 |
108 | Swizzling is fully thread-safe.
109 |
110 | @param classToSwizzle The class with the method that should be swizzled.
111 |
112 | @param selector Selector of the method that should be swizzled.
113 |
114 | @param RSSWReturnType The return type of the swizzled method wrapped in the RSSWReturnType macro.
115 |
116 | @param RSSWArguments The arguments of the swizzled method wrapped in the RSSWArguments macro.
117 |
118 | @param RSSWReplacement The code of the new implementation of the swizzled method wrapped in the RSSWReplacement macro.
119 |
120 | */
121 | #define RSSwizzleClassMethod(classToSwizzle, \
122 | selector, \
123 | RSSWReturnType, \
124 | RSSWArguments, \
125 | RSSWReplacement) \
126 | _RSSwizzleClassMethod(classToSwizzle, \
127 | selector, \
128 | RSSWReturnType, \
129 | _RSSWWrapArg(RSSWArguments), \
130 | _RSSWWrapArg(RSSWReplacement))
131 |
132 | #pragma mark - Main API
133 |
134 | /**
135 | A function pointer to the original implementation of the swizzled method.
136 | */
137 | typedef void (*RSSwizzleOriginalIMP)(void /* id, SEL, ... */ );
138 |
139 | /**
140 | RSSwizzleInfo is used in the new implementation block to get and call original implementation of the swizzled method.
141 | */
142 | @interface RSSwizzleInfo : NSObject
143 |
144 | /**
145 | Returns the original implementation of the swizzled method.
146 |
147 | It is actually either an original implementation if the swizzled class implements the method itself; or a super implementation fetched from one of the superclasses.
148 |
149 | @note You must always cast returned implementation to the appropriate function pointer when calling.
150 |
151 | @return A function pointer to the original implementation of the swizzled method.
152 | */
153 | -(RSSwizzleOriginalIMP)getOriginalImplementation;
154 |
155 | /// The selector of the swizzled method.
156 | @property (nonatomic, readonly) SEL selector;
157 |
158 | @end
159 |
160 | /**
161 | A factory block returning the block for the new implementation of the swizzled method.
162 |
163 | You must always obtain original implementation with swizzleInfo and call it from the new implementation.
164 |
165 | @param swizzleInfo An info used to get and call the original implementation of the swizzled method.
166 |
167 | @return A block that implements a method.
168 | Its signature should be: `method_return_type ^(id self, method_args...)`.
169 | The selector is not available as a parameter to this block.
170 | */
171 | typedef id (^RSSwizzleImpFactoryBlock)(RSSwizzleInfo *swizzleInfo);
172 |
173 | typedef NS_ENUM(NSUInteger, RSSwizzleMode) {
174 | /// RSSwizzle always does swizzling.
175 | RSSwizzleModeAlways = 0,
176 | /// RSSwizzle does not do swizzling if the same class has been swizzled earlier with the same key.
177 | RSSwizzleModeOncePerClass = 1,
178 | /// RSSwizzle does not do swizzling if the same class or one of its superclasses have been swizzled earlier with the same key.
179 | /// @note There is no guarantee that your implementation will be called only once per method call. If the order of swizzling is: first inherited class, second superclass, then both swizzlings will be done and the new implementation will be called twice.
180 | RSSwizzleModeOncePerClassAndSuperclasses = 2
181 | };
182 |
183 | @interface RSSwizzle : NSObject
184 |
185 | #pragma mark └ Swizzle Instance Method
186 |
187 | /**
188 | Swizzles the instance method of the class with the new implementation.
189 |
190 | Original implementation must always be called from the new implementation. And because of the the fact that for safe and robust swizzling original implementation must be dynamically fetched at the time of calling and not at the time of swizzling, swizzling API is a little bit complicated.
191 |
192 | You should pass a factory block that returns the block for the new implementation of the swizzled method. And use swizzleInfo argument to retrieve and call original implementation.
193 |
194 | Example for swizzling `-(int)calculate:(int)number;` method:
195 |
196 | @code
197 |
198 | SEL selector = @selector(calculate:);
199 | [RSSwizzle
200 | swizzleInstanceMethod:selector
201 | inClass:classToSwizzle
202 | newImpFactory:^id(RSSWizzleInfo *swizzleInfo) {
203 | // This block will be used as the new implementation.
204 | return ^int(__unsafe_unretained id self, int num){
205 | // You MUST always cast implementation to the correct function pointer.
206 | int (*originalIMP)(__unsafe_unretained id, SEL, int);
207 | originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
208 | // Calling original implementation.
209 | int res = originalIMP(self,selector,num);
210 | // Returning modified return value.
211 | return res + 1;
212 | };
213 | }
214 | mode:RSSwizzleModeAlways
215 | key:NULL];
216 |
217 | @endcode
218 |
219 | Swizzling frequently goes along with checking whether this particular class (or one of its superclasses) has been already swizzled. Here the `mode` and `key` parameters can help.
220 |
221 | Here is an example of swizzling `-(void)dealloc;` only in case when neither class and no one of its superclasses has been already swizzled with our key. However "Deallocating ..." message still may be logged multiple times per method call if swizzling was called primarily for an inherited class and later for one of its superclasses.
222 |
223 | @code
224 |
225 | static const void *key = &key;
226 | SEL selector = NSSelectorFromString(@"dealloc");
227 | [RSSwizzle
228 | swizzleInstanceMethod:selector
229 | inClass:classToSwizzle
230 | newImpFactory:^id(RSSWizzleInfo *swizzleInfo) {
231 | return ^void(__unsafe_unretained id self){
232 | NSLog(@"Deallocating %@.",self);
233 |
234 | void (*originalIMP)(__unsafe_unretained id, SEL);
235 | originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
236 | originalIMP(self,selector);
237 | };
238 | }
239 | mode:RSSwizzleModeOncePerClassAndSuperclasses
240 | key:key];
241 |
242 | @endcode
243 |
244 | Swizzling is fully thread-safe.
245 |
246 | @param selector Selector of the method that should be swizzled.
247 |
248 | @param classToSwizzle The class with the method that should be swizzled.
249 |
250 | @param factoryBlock The factory block returning the block for the new implementation of the swizzled method.
251 |
252 | @param mode The mode is used in combination with the key to indicate whether the swizzling should be done for the given class.
253 |
254 | @param key The key is used in combination with the mode to indicate whether the swizzling should be done for the given class. May be NULL if the mode is RSSwizzleModeAlways.
255 |
256 | @return YES if successfully swizzled and NO if swizzling has been already done for given key and class (or one of superclasses, depends on the mode).
257 | */
258 | +(BOOL)swizzleInstanceMethod:(SEL)selector
259 | inClass:(Class)classToSwizzle
260 | newImpFactory:(RSSwizzleImpFactoryBlock)factoryBlock
261 | mode:(RSSwizzleMode)mode
262 | key:(const void *)key;
263 |
264 | #pragma mark └ Swizzle Class method
265 |
266 | /**
267 | Swizzles the class method of the class with the new implementation.
268 |
269 | Original implementation must always be called from the new implementation. And because of the the fact that for safe and robust swizzling original implementation must be dynamically fetched at the time of calling and not at the time of swizzling, swizzling API is a little bit complicated.
270 |
271 | You should pass a factory block that returns the block for the new implementation of the swizzled method. And use swizzleInfo argument to retrieve and call original implementation.
272 |
273 | Example for swizzling `+(int)calculate:(int)number;` method:
274 |
275 | @code
276 |
277 | SEL selector = @selector(calculate:);
278 | [RSSwizzle
279 | swizzleClassMethod:selector
280 | inClass:classToSwizzle
281 | newImpFactory:^id(RSSWizzleInfo *swizzleInfo) {
282 | // This block will be used as the new implementation.
283 | return ^int(__unsafe_unretained id self, int num){
284 | // You MUST always cast implementation to the correct function pointer.
285 | int (*originalIMP)(__unsafe_unretained id, SEL, int);
286 | originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
287 | // Calling original implementation.
288 | int res = originalIMP(self,selector,num);
289 | // Returning modified return value.
290 | return res + 1;
291 | };
292 | }];
293 |
294 | @endcode
295 |
296 | Swizzling is fully thread-safe.
297 |
298 | @param selector Selector of the method that should be swizzled.
299 |
300 | @param classToSwizzle The class with the method that should be swizzled.
301 |
302 | @param factoryBlock The factory block returning the block for the new implementation of the swizzled method.
303 | */
304 | +(void)swizzleClassMethod:(SEL)selector
305 | inClass:(Class)classToSwizzle
306 | newImpFactory:(RSSwizzleImpFactoryBlock)factoryBlock;
307 |
308 | @end
309 |
310 | #pragma mark - Implementation details
311 | // Do not write code that depends on anything below this line.
312 |
313 | // Wrapping arguments to pass them as a single argument to another macro.
314 | #define _RSSWWrapArg(args...) args
315 |
316 | #define _RSSWDel2Arg(a1, a2, args...) a1, ##args
317 | #define _RSSWDel3Arg(a1, a2, a3, args...) a1, a2, ##args
318 |
319 | // To prevent comma issues if there are no arguments we add one dummy argument
320 | // and remove it later.
321 | #define _RSSWArguments(arguments...) DEL, ##arguments
322 |
323 | #define _RSSwizzleInstanceMethod(classToSwizzle, \
324 | selector, \
325 | RSSWReturnType, \
326 | RSSWArguments, \
327 | RSSWReplacement, \
328 | RSSwizzleMode, \
329 | KEY) \
330 | [RSSwizzle \
331 | swizzleInstanceMethod:selector \
332 | inClass:[classToSwizzle class] \
333 | newImpFactory:^id(RSSwizzleInfo *swizzleInfo) { \
334 | RSSWReturnType (*originalImplementation_)(_RSSWDel3Arg(__unsafe_unretained id, \
335 | SEL, \
336 | RSSWArguments)); \
337 | SEL selector_ = selector; \
338 | return ^RSSWReturnType (_RSSWDel2Arg(__unsafe_unretained id self, \
339 | RSSWArguments)) \
340 | { \
341 | RSSWReplacement \
342 | }; \
343 | } \
344 | mode:RSSwizzleMode \
345 | key:KEY];
346 |
347 | #define _RSSwizzleClassMethod(classToSwizzle, \
348 | selector, \
349 | RSSWReturnType, \
350 | RSSWArguments, \
351 | RSSWReplacement) \
352 | [RSSwizzle \
353 | swizzleClassMethod:selector \
354 | inClass:[classToSwizzle class] \
355 | newImpFactory:^id(RSSwizzleInfo *swizzleInfo) { \
356 | RSSWReturnType (*originalImplementation_)(_RSSWDel3Arg(__unsafe_unretained id, \
357 | SEL, \
358 | RSSWArguments)); \
359 | SEL selector_ = selector; \
360 | return ^RSSWReturnType (_RSSWDel2Arg(__unsafe_unretained id self, \
361 | RSSWArguments)) \
362 | { \
363 | RSSWReplacement \
364 | }; \
365 | }];
366 |
367 | #define _RSSWCallOriginal(arguments...) \
368 | ((__typeof(originalImplementation_))[swizzleInfo \
369 | getOriginalImplementation])(self, \
370 | selector_, \
371 | ##arguments)
372 |
--------------------------------------------------------------------------------
/RSSwizzle/RSSwizzle.m:
--------------------------------------------------------------------------------
1 | //
2 | // RSSwizzle.m
3 | // RSSwizzleTests
4 | //
5 | // Created by Yan Rabovik on 05.09.13.
6 | //
7 | //
8 |
9 | #import "RSSwizzle.h"
10 | #import
11 | #import
12 |
13 | #if !__has_feature(objc_arc)
14 | #error This code needs ARC. Use compiler option -fobjc-arc
15 | #endif
16 |
17 | #pragma mark - Block Helpers
18 | #if !defined(NS_BLOCK_ASSERTIONS)
19 |
20 | // See http://clang.llvm.org/docs/Block-ABI-Apple.html#high-level
21 | struct Block_literal_1 {
22 | void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
23 | int flags;
24 | int reserved;
25 | void (*invoke)(void *, ...);
26 | struct Block_descriptor_1 {
27 | unsigned long int reserved; // NULL
28 | unsigned long int size; // sizeof(struct Block_literal_1)
29 | // optional helper functions
30 | void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
31 | void (*dispose_helper)(void *src); // IFF (1<<25)
32 | // required ABI.2010.3.16
33 | const char *signature; // IFF (1<<30)
34 | } *descriptor;
35 | // imported variables
36 | };
37 |
38 | enum {
39 | BLOCK_HAS_COPY_DISPOSE = (1 << 25),
40 | BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
41 | BLOCK_IS_GLOBAL = (1 << 28),
42 | BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
43 | BLOCK_HAS_SIGNATURE = (1 << 30),
44 | };
45 | typedef int BlockFlags;
46 |
47 | static const char *blockGetType(id block){
48 | struct Block_literal_1 *blockRef = (__bridge struct Block_literal_1 *)block;
49 | BlockFlags flags = blockRef->flags;
50 |
51 | if (flags & BLOCK_HAS_SIGNATURE) {
52 | void *signatureLocation = blockRef->descriptor;
53 | signatureLocation += sizeof(unsigned long int);
54 | signatureLocation += sizeof(unsigned long int);
55 |
56 | if (flags & BLOCK_HAS_COPY_DISPOSE) {
57 | signatureLocation += sizeof(void(*)(void *dst, void *src));
58 | signatureLocation += sizeof(void (*)(void *src));
59 | }
60 |
61 | const char *signature = (*(const char **)signatureLocation);
62 | return signature;
63 | }
64 |
65 | return NULL;
66 | }
67 |
68 | static BOOL blockIsCompatibleWithMethodType(id block, const char *methodType){
69 |
70 | const char *blockType = blockGetType(block);
71 |
72 | NSMethodSignature *blockSignature;
73 |
74 | if (0 == strncmp(blockType, (const char *)"@\"", 2)) {
75 | // Block return type includes class name for id types
76 | // while methodType does not include.
77 | // Stripping out return class name.
78 | char *quotePtr = strchr(blockType+2, '"');
79 | if (NULL != quotePtr) {
80 | ++quotePtr;
81 | char filteredType[strlen(quotePtr) + 2];
82 | memset(filteredType, 0, sizeof(filteredType));
83 | *filteredType = '@';
84 | strncpy(filteredType + 1, quotePtr, sizeof(filteredType) - 2);
85 |
86 | blockSignature = [NSMethodSignature signatureWithObjCTypes:filteredType];
87 | }else{
88 | return NO;
89 | }
90 | }else{
91 | blockSignature = [NSMethodSignature signatureWithObjCTypes:blockType];
92 | }
93 |
94 | NSMethodSignature *methodSignature =
95 | [NSMethodSignature signatureWithObjCTypes:methodType];
96 |
97 | if (!blockSignature || !methodSignature) {
98 | return NO;
99 | }
100 |
101 | if (blockSignature.numberOfArguments != methodSignature.numberOfArguments){
102 | return NO;
103 | }
104 |
105 | if (strcmp(blockSignature.methodReturnType, methodSignature.methodReturnType) != 0) {
106 | return NO;
107 | }
108 |
109 | for (int i=0; i
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ru.rabovik.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests-OSX/RSSwizzleTests-OSX-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #ifdef __OBJC__
8 | #import
9 | #endif
10 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests-OSX/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests-iOS/RSSwizzleTests-iOS-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ru.rabovik.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests-iOS/RSSwizzleTests-iOS-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #ifdef __OBJC__
8 | #import
9 | #import
10 | #endif
11 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests-iOS/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1A1D75C217D8CD58001FDCDC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A1D75C117D8CD58001FDCDC /* SenTestingKit.framework */; };
11 | 1A1D75C417D8CD58001FDCDC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A1D75C317D8CD58001FDCDC /* Foundation.framework */; };
12 | 1A1D75C617D8CD58001FDCDC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A1D75C517D8CD58001FDCDC /* UIKit.framework */; };
13 | 1A1D75CC17D8CD58001FDCDC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1A1D75CA17D8CD58001FDCDC /* InfoPlist.strings */; };
14 | 1A1D75D817D8CD70001FDCDC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A1D75C117D8CD58001FDCDC /* SenTestingKit.framework */; };
15 | 1A1D75DE17D8CD70001FDCDC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1A1D75DC17D8CD70001FDCDC /* InfoPlist.strings */; };
16 | 1A1D75E817D8CDF5001FDCDC /* RSSwizzleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A1D75E717D8CDF5001FDCDC /* RSSwizzleTests.m */; };
17 | 1A1D75E917D8CDF5001FDCDC /* RSSwizzleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A1D75E717D8CDF5001FDCDC /* RSSwizzleTests.m */; };
18 | 1A1D75ED17D8CF0D001FDCDC /* RSTestsLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A1D75EC17D8CF0D001FDCDC /* RSTestsLog.m */; };
19 | 1A1D75EE17D8CF0D001FDCDC /* RSTestsLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A1D75EC17D8CF0D001FDCDC /* RSTestsLog.m */; };
20 | 1A1D75F117D8D37A001FDCDC /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A1D75F017D8D37A001FDCDC /* RSSwizzle.m */; };
21 | 1A1D75F217D8D37A001FDCDC /* RSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A1D75F017D8D37A001FDCDC /* RSSwizzle.m */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXFileReference section */
25 | 1A1D75BE17D8CD58001FDCDC /* RSSwizzleTests-iOS.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RSSwizzleTests-iOS.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
26 | 1A1D75C117D8CD58001FDCDC /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
27 | 1A1D75C317D8CD58001FDCDC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
28 | 1A1D75C517D8CD58001FDCDC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
29 | 1A1D75C917D8CD58001FDCDC /* RSSwizzleTests-iOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RSSwizzleTests-iOS-Info.plist"; sourceTree = ""; };
30 | 1A1D75CB17D8CD58001FDCDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
31 | 1A1D75CF17D8CD58001FDCDC /* RSSwizzleTests-iOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RSSwizzleTests-iOS-Prefix.pch"; sourceTree = ""; };
32 | 1A1D75D717D8CD70001FDCDC /* RSSwizzleTests-OSX.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RSSwizzleTests-OSX.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
33 | 1A1D75DB17D8CD70001FDCDC /* RSSwizzleTests-OSX-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RSSwizzleTests-OSX-Info.plist"; sourceTree = ""; };
34 | 1A1D75DD17D8CD70001FDCDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
35 | 1A1D75E117D8CD70001FDCDC /* RSSwizzleTests-OSX-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RSSwizzleTests-OSX-Prefix.pch"; sourceTree = ""; };
36 | 1A1D75E717D8CDF5001FDCDC /* RSSwizzleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RSSwizzleTests.m; path = RSSwizzleTests/RSSwizzleTests.m; sourceTree = ""; };
37 | 1A1D75EB17D8CF0D001FDCDC /* RSTestsLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RSTestsLog.h; path = RSSwizzleTests/RSTestsLog.h; sourceTree = ""; };
38 | 1A1D75EC17D8CF0D001FDCDC /* RSTestsLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RSTestsLog.m; path = RSSwizzleTests/RSTestsLog.m; sourceTree = ""; };
39 | 1A1D75EF17D8D37A001FDCDC /* RSSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RSSwizzle.h; path = ../RSSwizzle/RSSwizzle.h; sourceTree = ""; };
40 | 1A1D75F017D8D37A001FDCDC /* RSSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RSSwizzle.m; path = ../RSSwizzle/RSSwizzle.m; sourceTree = ""; };
41 | /* End PBXFileReference section */
42 |
43 | /* Begin PBXFrameworksBuildPhase section */
44 | 1A1D75BB17D8CD58001FDCDC /* Frameworks */ = {
45 | isa = PBXFrameworksBuildPhase;
46 | buildActionMask = 2147483647;
47 | files = (
48 | 1A1D75C217D8CD58001FDCDC /* SenTestingKit.framework in Frameworks */,
49 | 1A1D75C617D8CD58001FDCDC /* UIKit.framework in Frameworks */,
50 | 1A1D75C417D8CD58001FDCDC /* Foundation.framework in Frameworks */,
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | 1A1D75D417D8CD70001FDCDC /* Frameworks */ = {
55 | isa = PBXFrameworksBuildPhase;
56 | buildActionMask = 2147483647;
57 | files = (
58 | 1A1D75D817D8CD70001FDCDC /* SenTestingKit.framework in Frameworks */,
59 | );
60 | runOnlyForDeploymentPostprocessing = 0;
61 | };
62 | /* End PBXFrameworksBuildPhase section */
63 |
64 | /* Begin PBXGroup section */
65 | 1A1D75B317D8CD19001FDCDC = {
66 | isa = PBXGroup;
67 | children = (
68 | 1A1D75EA17D8CE5D001FDCDC /* RSSwizzle */,
69 | 1A1D75E517D8CDA9001FDCDC /* RSSwizzleTests */,
70 | 1A1D75E617D8CDBB001FDCDC /* Supporting Files */,
71 | 1A1D75C017D8CD58001FDCDC /* Frameworks */,
72 | 1A1D75BF17D8CD58001FDCDC /* Products */,
73 | );
74 | sourceTree = "";
75 | };
76 | 1A1D75BF17D8CD58001FDCDC /* Products */ = {
77 | isa = PBXGroup;
78 | children = (
79 | 1A1D75BE17D8CD58001FDCDC /* RSSwizzleTests-iOS.octest */,
80 | 1A1D75D717D8CD70001FDCDC /* RSSwizzleTests-OSX.octest */,
81 | );
82 | name = Products;
83 | sourceTree = "";
84 | };
85 | 1A1D75C017D8CD58001FDCDC /* Frameworks */ = {
86 | isa = PBXGroup;
87 | children = (
88 | 1A1D75C117D8CD58001FDCDC /* SenTestingKit.framework */,
89 | 1A1D75C317D8CD58001FDCDC /* Foundation.framework */,
90 | 1A1D75C517D8CD58001FDCDC /* UIKit.framework */,
91 | );
92 | name = Frameworks;
93 | sourceTree = "";
94 | };
95 | 1A1D75C717D8CD58001FDCDC /* RSSwizzleTests-iOS */ = {
96 | isa = PBXGroup;
97 | children = (
98 | 1A1D75C817D8CD58001FDCDC /* Supporting Files */,
99 | );
100 | path = "RSSwizzleTests-iOS";
101 | sourceTree = "";
102 | };
103 | 1A1D75C817D8CD58001FDCDC /* Supporting Files */ = {
104 | isa = PBXGroup;
105 | children = (
106 | 1A1D75C917D8CD58001FDCDC /* RSSwizzleTests-iOS-Info.plist */,
107 | 1A1D75CA17D8CD58001FDCDC /* InfoPlist.strings */,
108 | 1A1D75CF17D8CD58001FDCDC /* RSSwizzleTests-iOS-Prefix.pch */,
109 | );
110 | name = "Supporting Files";
111 | sourceTree = "";
112 | };
113 | 1A1D75D917D8CD70001FDCDC /* RSSwizzleTests-OSX */ = {
114 | isa = PBXGroup;
115 | children = (
116 | 1A1D75DA17D8CD70001FDCDC /* Supporting Files */,
117 | );
118 | path = "RSSwizzleTests-OSX";
119 | sourceTree = "";
120 | };
121 | 1A1D75DA17D8CD70001FDCDC /* Supporting Files */ = {
122 | isa = PBXGroup;
123 | children = (
124 | 1A1D75DB17D8CD70001FDCDC /* RSSwizzleTests-OSX-Info.plist */,
125 | 1A1D75DC17D8CD70001FDCDC /* InfoPlist.strings */,
126 | 1A1D75E117D8CD70001FDCDC /* RSSwizzleTests-OSX-Prefix.pch */,
127 | );
128 | name = "Supporting Files";
129 | sourceTree = "";
130 | };
131 | 1A1D75E517D8CDA9001FDCDC /* RSSwizzleTests */ = {
132 | isa = PBXGroup;
133 | children = (
134 | 1A1D75E717D8CDF5001FDCDC /* RSSwizzleTests.m */,
135 | 1A1D75EB17D8CF0D001FDCDC /* RSTestsLog.h */,
136 | 1A1D75EC17D8CF0D001FDCDC /* RSTestsLog.m */,
137 | );
138 | name = RSSwizzleTests;
139 | sourceTree = "";
140 | };
141 | 1A1D75E617D8CDBB001FDCDC /* Supporting Files */ = {
142 | isa = PBXGroup;
143 | children = (
144 | 1A1D75D917D8CD70001FDCDC /* RSSwizzleTests-OSX */,
145 | 1A1D75C717D8CD58001FDCDC /* RSSwizzleTests-iOS */,
146 | );
147 | name = "Supporting Files";
148 | sourceTree = "";
149 | };
150 | 1A1D75EA17D8CE5D001FDCDC /* RSSwizzle */ = {
151 | isa = PBXGroup;
152 | children = (
153 | 1A1D75EF17D8D37A001FDCDC /* RSSwizzle.h */,
154 | 1A1D75F017D8D37A001FDCDC /* RSSwizzle.m */,
155 | );
156 | name = RSSwizzle;
157 | sourceTree = "";
158 | };
159 | /* End PBXGroup section */
160 |
161 | /* Begin PBXNativeTarget section */
162 | 1A1D75BD17D8CD58001FDCDC /* RSSwizzleTests-iOS */ = {
163 | isa = PBXNativeTarget;
164 | buildConfigurationList = 1A1D75D217D8CD58001FDCDC /* Build configuration list for PBXNativeTarget "RSSwizzleTests-iOS" */;
165 | buildPhases = (
166 | 1A1D75BA17D8CD58001FDCDC /* Sources */,
167 | 1A1D75BB17D8CD58001FDCDC /* Frameworks */,
168 | 1A1D75BC17D8CD58001FDCDC /* Resources */,
169 | );
170 | buildRules = (
171 | );
172 | dependencies = (
173 | );
174 | name = "RSSwizzleTests-iOS";
175 | productName = "RSSwizzleTests-iOS";
176 | productReference = 1A1D75BE17D8CD58001FDCDC /* RSSwizzleTests-iOS.octest */;
177 | productType = "com.apple.product-type.bundle";
178 | };
179 | 1A1D75D617D8CD70001FDCDC /* RSSwizzleTests-OSX */ = {
180 | isa = PBXNativeTarget;
181 | buildConfigurationList = 1A1D75E217D8CD70001FDCDC /* Build configuration list for PBXNativeTarget "RSSwizzleTests-OSX" */;
182 | buildPhases = (
183 | 1A1D75D317D8CD70001FDCDC /* Sources */,
184 | 1A1D75D417D8CD70001FDCDC /* Frameworks */,
185 | 1A1D75D517D8CD70001FDCDC /* Resources */,
186 | );
187 | buildRules = (
188 | );
189 | dependencies = (
190 | );
191 | name = "RSSwizzleTests-OSX";
192 | productName = "RSSwizzleTests-OSX";
193 | productReference = 1A1D75D717D8CD70001FDCDC /* RSSwizzleTests-OSX.octest */;
194 | productType = "com.apple.product-type.bundle";
195 | };
196 | /* End PBXNativeTarget section */
197 |
198 | /* Begin PBXProject section */
199 | 1A1D75B417D8CD19001FDCDC /* Project object */ = {
200 | isa = PBXProject;
201 | attributes = {
202 | LastUpgradeCheck = 0500;
203 | };
204 | buildConfigurationList = 1A1D75B717D8CD19001FDCDC /* Build configuration list for PBXProject "RSSwizzleTests" */;
205 | compatibilityVersion = "Xcode 3.2";
206 | developmentRegion = English;
207 | hasScannedForEncodings = 0;
208 | knownRegions = (
209 | en,
210 | );
211 | mainGroup = 1A1D75B317D8CD19001FDCDC;
212 | productRefGroup = 1A1D75BF17D8CD58001FDCDC /* Products */;
213 | projectDirPath = "";
214 | projectRoot = "";
215 | targets = (
216 | 1A1D75BD17D8CD58001FDCDC /* RSSwizzleTests-iOS */,
217 | 1A1D75D617D8CD70001FDCDC /* RSSwizzleTests-OSX */,
218 | );
219 | };
220 | /* End PBXProject section */
221 |
222 | /* Begin PBXResourcesBuildPhase section */
223 | 1A1D75BC17D8CD58001FDCDC /* Resources */ = {
224 | isa = PBXResourcesBuildPhase;
225 | buildActionMask = 2147483647;
226 | files = (
227 | 1A1D75CC17D8CD58001FDCDC /* InfoPlist.strings in Resources */,
228 | );
229 | runOnlyForDeploymentPostprocessing = 0;
230 | };
231 | 1A1D75D517D8CD70001FDCDC /* Resources */ = {
232 | isa = PBXResourcesBuildPhase;
233 | buildActionMask = 2147483647;
234 | files = (
235 | 1A1D75DE17D8CD70001FDCDC /* InfoPlist.strings in Resources */,
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | };
239 | /* End PBXResourcesBuildPhase section */
240 |
241 | /* Begin PBXSourcesBuildPhase section */
242 | 1A1D75BA17D8CD58001FDCDC /* Sources */ = {
243 | isa = PBXSourcesBuildPhase;
244 | buildActionMask = 2147483647;
245 | files = (
246 | 1A1D75E817D8CDF5001FDCDC /* RSSwizzleTests.m in Sources */,
247 | 1A1D75ED17D8CF0D001FDCDC /* RSTestsLog.m in Sources */,
248 | 1A1D75F117D8D37A001FDCDC /* RSSwizzle.m in Sources */,
249 | );
250 | runOnlyForDeploymentPostprocessing = 0;
251 | };
252 | 1A1D75D317D8CD70001FDCDC /* Sources */ = {
253 | isa = PBXSourcesBuildPhase;
254 | buildActionMask = 2147483647;
255 | files = (
256 | 1A1D75E917D8CDF5001FDCDC /* RSSwizzleTests.m in Sources */,
257 | 1A1D75EE17D8CF0D001FDCDC /* RSTestsLog.m in Sources */,
258 | 1A1D75F217D8D37A001FDCDC /* RSSwizzle.m in Sources */,
259 | );
260 | runOnlyForDeploymentPostprocessing = 0;
261 | };
262 | /* End PBXSourcesBuildPhase section */
263 |
264 | /* Begin PBXVariantGroup section */
265 | 1A1D75CA17D8CD58001FDCDC /* InfoPlist.strings */ = {
266 | isa = PBXVariantGroup;
267 | children = (
268 | 1A1D75CB17D8CD58001FDCDC /* en */,
269 | );
270 | name = InfoPlist.strings;
271 | sourceTree = "";
272 | };
273 | 1A1D75DC17D8CD70001FDCDC /* InfoPlist.strings */ = {
274 | isa = PBXVariantGroup;
275 | children = (
276 | 1A1D75DD17D8CD70001FDCDC /* en */,
277 | );
278 | name = InfoPlist.strings;
279 | sourceTree = "";
280 | };
281 | /* End PBXVariantGroup section */
282 |
283 | /* Begin XCBuildConfiguration section */
284 | 1A1D75B817D8CD19001FDCDC /* Debug */ = {
285 | isa = XCBuildConfiguration;
286 | buildSettings = {
287 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
288 | };
289 | name = Debug;
290 | };
291 | 1A1D75B917D8CD19001FDCDC /* Release */ = {
292 | isa = XCBuildConfiguration;
293 | buildSettings = {
294 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
295 | };
296 | name = Release;
297 | };
298 | 1A1D75D017D8CD58001FDCDC /* Debug */ = {
299 | isa = XCBuildConfiguration;
300 | buildSettings = {
301 | ALWAYS_SEARCH_USER_PATHS = NO;
302 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
303 | CLANG_CXX_LIBRARY = "libc++";
304 | CLANG_ENABLE_MODULES = YES;
305 | CLANG_ENABLE_OBJC_ARC = YES;
306 | CLANG_WARN_BOOL_CONVERSION = YES;
307 | CLANG_WARN_CONSTANT_CONVERSION = YES;
308 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
309 | CLANG_WARN_EMPTY_BODY = YES;
310 | CLANG_WARN_ENUM_CONVERSION = YES;
311 | CLANG_WARN_INT_CONVERSION = YES;
312 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
313 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
314 | COPY_PHASE_STRIP = NO;
315 | FRAMEWORK_SEARCH_PATHS = (
316 | "$(SDKROOT)/Developer/Library/Frameworks",
317 | "$(inherited)",
318 | "$(DEVELOPER_FRAMEWORKS_DIR)",
319 | );
320 | GCC_C_LANGUAGE_STANDARD = gnu99;
321 | GCC_DYNAMIC_NO_PIC = NO;
322 | GCC_OPTIMIZATION_LEVEL = 0;
323 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
324 | GCC_PREFIX_HEADER = "RSSwizzleTests-iOS/RSSwizzleTests-iOS-Prefix.pch";
325 | GCC_PREPROCESSOR_DEFINITIONS = (
326 | "DEBUG=1",
327 | "$(inherited)",
328 | );
329 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
330 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
331 | GCC_WARN_UNDECLARED_SELECTOR = YES;
332 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
333 | GCC_WARN_UNUSED_FUNCTION = YES;
334 | GCC_WARN_UNUSED_VARIABLE = YES;
335 | INFOPLIST_FILE = "RSSwizzleTests-iOS/RSSwizzleTests-iOS-Info.plist";
336 | IPHONEOS_DEPLOYMENT_TARGET = 5.0;
337 | ONLY_ACTIVE_ARCH = YES;
338 | PRODUCT_NAME = "$(TARGET_NAME)";
339 | SDKROOT = iphoneos;
340 | WRAPPER_EXTENSION = octest;
341 | };
342 | name = Debug;
343 | };
344 | 1A1D75D117D8CD58001FDCDC /* Release */ = {
345 | isa = XCBuildConfiguration;
346 | buildSettings = {
347 | ALWAYS_SEARCH_USER_PATHS = NO;
348 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
349 | CLANG_CXX_LIBRARY = "libc++";
350 | CLANG_ENABLE_MODULES = YES;
351 | CLANG_ENABLE_OBJC_ARC = YES;
352 | CLANG_WARN_BOOL_CONVERSION = YES;
353 | CLANG_WARN_CONSTANT_CONVERSION = YES;
354 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
355 | CLANG_WARN_EMPTY_BODY = YES;
356 | CLANG_WARN_ENUM_CONVERSION = YES;
357 | CLANG_WARN_INT_CONVERSION = YES;
358 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
360 | COPY_PHASE_STRIP = YES;
361 | ENABLE_NS_ASSERTIONS = YES;
362 | FRAMEWORK_SEARCH_PATHS = (
363 | "$(SDKROOT)/Developer/Library/Frameworks",
364 | "$(inherited)",
365 | "$(DEVELOPER_FRAMEWORKS_DIR)",
366 | );
367 | GCC_C_LANGUAGE_STANDARD = gnu99;
368 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
369 | GCC_PREFIX_HEADER = "RSSwizzleTests-iOS/RSSwizzleTests-iOS-Prefix.pch";
370 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
371 | GCC_WARN_UNDECLARED_SELECTOR = YES;
372 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
373 | GCC_WARN_UNUSED_FUNCTION = YES;
374 | GCC_WARN_UNUSED_VARIABLE = YES;
375 | INFOPLIST_FILE = "RSSwizzleTests-iOS/RSSwizzleTests-iOS-Info.plist";
376 | IPHONEOS_DEPLOYMENT_TARGET = 5.0;
377 | PRODUCT_NAME = "$(TARGET_NAME)";
378 | SDKROOT = iphoneos;
379 | VALIDATE_PRODUCT = YES;
380 | WRAPPER_EXTENSION = octest;
381 | };
382 | name = Release;
383 | };
384 | 1A1D75E317D8CD70001FDCDC /* Debug */ = {
385 | isa = XCBuildConfiguration;
386 | buildSettings = {
387 | ALWAYS_SEARCH_USER_PATHS = NO;
388 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
389 | CLANG_CXX_LIBRARY = "libc++";
390 | CLANG_ENABLE_MODULES = YES;
391 | CLANG_ENABLE_OBJC_ARC = YES;
392 | CLANG_WARN_BOOL_CONVERSION = YES;
393 | CLANG_WARN_CONSTANT_CONVERSION = YES;
394 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
395 | CLANG_WARN_EMPTY_BODY = YES;
396 | CLANG_WARN_ENUM_CONVERSION = YES;
397 | CLANG_WARN_INT_CONVERSION = YES;
398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
400 | COPY_PHASE_STRIP = NO;
401 | FRAMEWORK_SEARCH_PATHS = (
402 | "$(DEVELOPER_FRAMEWORKS_DIR)",
403 | "$(inherited)",
404 | );
405 | GCC_C_LANGUAGE_STANDARD = gnu99;
406 | GCC_DYNAMIC_NO_PIC = NO;
407 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
408 | GCC_OPTIMIZATION_LEVEL = 0;
409 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
410 | GCC_PREFIX_HEADER = "RSSwizzleTests-OSX/RSSwizzleTests-OSX-Prefix.pch";
411 | GCC_PREPROCESSOR_DEFINITIONS = (
412 | "DEBUG=1",
413 | "$(inherited)",
414 | );
415 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
416 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
417 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
418 | GCC_WARN_UNDECLARED_SELECTOR = YES;
419 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
420 | GCC_WARN_UNUSED_FUNCTION = YES;
421 | GCC_WARN_UNUSED_VARIABLE = YES;
422 | INFOPLIST_FILE = "RSSwizzleTests-OSX/RSSwizzleTests-OSX-Info.plist";
423 | MACOSX_DEPLOYMENT_TARGET = 10.7;
424 | ONLY_ACTIVE_ARCH = YES;
425 | PRODUCT_NAME = "$(TARGET_NAME)";
426 | SDKROOT = macosx;
427 | WRAPPER_EXTENSION = octest;
428 | };
429 | name = Debug;
430 | };
431 | 1A1D75E417D8CD70001FDCDC /* Release */ = {
432 | isa = XCBuildConfiguration;
433 | buildSettings = {
434 | ALWAYS_SEARCH_USER_PATHS = NO;
435 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
436 | CLANG_CXX_LIBRARY = "libc++";
437 | CLANG_ENABLE_MODULES = YES;
438 | CLANG_ENABLE_OBJC_ARC = YES;
439 | CLANG_WARN_BOOL_CONVERSION = YES;
440 | CLANG_WARN_CONSTANT_CONVERSION = YES;
441 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
442 | CLANG_WARN_EMPTY_BODY = YES;
443 | CLANG_WARN_ENUM_CONVERSION = YES;
444 | CLANG_WARN_INT_CONVERSION = YES;
445 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
446 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
447 | COPY_PHASE_STRIP = YES;
448 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
449 | ENABLE_NS_ASSERTIONS = YES;
450 | FRAMEWORK_SEARCH_PATHS = (
451 | "$(DEVELOPER_FRAMEWORKS_DIR)",
452 | "$(inherited)",
453 | );
454 | GCC_C_LANGUAGE_STANDARD = gnu99;
455 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
456 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
457 | GCC_PREFIX_HEADER = "RSSwizzleTests-OSX/RSSwizzleTests-OSX-Prefix.pch";
458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
460 | GCC_WARN_UNDECLARED_SELECTOR = YES;
461 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
462 | GCC_WARN_UNUSED_FUNCTION = YES;
463 | GCC_WARN_UNUSED_VARIABLE = YES;
464 | INFOPLIST_FILE = "RSSwizzleTests-OSX/RSSwizzleTests-OSX-Info.plist";
465 | MACOSX_DEPLOYMENT_TARGET = 10.7;
466 | PRODUCT_NAME = "$(TARGET_NAME)";
467 | SDKROOT = macosx;
468 | WRAPPER_EXTENSION = octest;
469 | };
470 | name = Release;
471 | };
472 | /* End XCBuildConfiguration section */
473 |
474 | /* Begin XCConfigurationList section */
475 | 1A1D75B717D8CD19001FDCDC /* Build configuration list for PBXProject "RSSwizzleTests" */ = {
476 | isa = XCConfigurationList;
477 | buildConfigurations = (
478 | 1A1D75B817D8CD19001FDCDC /* Debug */,
479 | 1A1D75B917D8CD19001FDCDC /* Release */,
480 | );
481 | defaultConfigurationIsVisible = 0;
482 | defaultConfigurationName = Release;
483 | };
484 | 1A1D75D217D8CD58001FDCDC /* Build configuration list for PBXNativeTarget "RSSwizzleTests-iOS" */ = {
485 | isa = XCConfigurationList;
486 | buildConfigurations = (
487 | 1A1D75D017D8CD58001FDCDC /* Debug */,
488 | 1A1D75D117D8CD58001FDCDC /* Release */,
489 | );
490 | defaultConfigurationIsVisible = 0;
491 | defaultConfigurationName = Release;
492 | };
493 | 1A1D75E217D8CD70001FDCDC /* Build configuration list for PBXNativeTarget "RSSwizzleTests-OSX" */ = {
494 | isa = XCConfigurationList;
495 | buildConfigurations = (
496 | 1A1D75E317D8CD70001FDCDC /* Debug */,
497 | 1A1D75E417D8CD70001FDCDC /* Release */,
498 | );
499 | defaultConfigurationIsVisible = 0;
500 | defaultConfigurationName = Release;
501 | };
502 | /* End XCConfigurationList section */
503 | };
504 | rootObject = 1A1D75B417D8CD19001FDCDC /* Project object */;
505 | }
506 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests/RSSwizzleTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // RSSwizzleTests.m
3 | // RSSwizzleTests
4 | //
5 | // Created by Yan Rabovik on 05.09.13.
6 | //
7 | //
8 |
9 | #import
10 | #import "RSTestsLog.h"
11 | #import "RSSwizzle.h"
12 | #import
13 |
14 | #pragma mark - HELPER CLASSES -
15 |
16 | @interface RSSwizzleTestClass_A : NSObject @end
17 | @implementation RSSwizzleTestClass_A
18 | -(int)calc:(int)num{ return num; }
19 | -(BOOL)methodReturningBOOL{ return YES; };
20 | -(void)methodWithArgument:(id)arg{};
21 | -(void)methodForAlwaysSwizzling{};
22 | -(void)methodForSwizzlingOncePerClass{};
23 | -(void)methodForSwizzlingOncePerClassOrSuperClasses{};
24 | -(NSString *)string{ return @"ABC"; }
25 | +(NSNumber *)sumFloat:(float)floatSummand withDouble:(double)doubleSummand{
26 | return @(floatSummand + doubleSummand);
27 | }
28 | @end
29 |
30 | @interface RSSwizzleTestClass_B : RSSwizzleTestClass_A @end
31 | @implementation RSSwizzleTestClass_B @end
32 |
33 | @interface RSSwizzleTestClass_C : RSSwizzleTestClass_B @end
34 | @implementation RSSwizzleTestClass_C
35 | -(void)dealloc{ RSTestsLog(@"C-"); };
36 | -(int)calc:(int)num{ return [super calc:num] * 3; }
37 | @end
38 |
39 | @interface RSSwizzleTestClass_D : RSSwizzleTestClass_C @end
40 | @implementation RSSwizzleTestClass_D @end
41 |
42 | @interface RSSwizzleTestClass_D2 : RSSwizzleTestClass_C @end
43 | @implementation RSSwizzleTestClass_D2 @end
44 |
45 | #pragma mark - HELPER FUNCTIONS -
46 |
47 | static void swizzleVoidMethod(Class classToSwizzle,
48 | SEL selector,
49 | dispatch_block_t blockBefore,
50 | RSSwizzleMode mode,
51 | const void *key)
52 | {
53 | RSSwizzleInstanceMethod(classToSwizzle,
54 | selector,
55 | RSSWReturnType(void),
56 | RSSWArguments(),
57 | RSSWReplacement(
58 | {
59 | blockBefore();
60 | RSSWCallOriginal();
61 | }), mode, key);
62 | }
63 |
64 | static void swizzleDealloc(Class classToSwizzle, dispatch_block_t blockBefore){
65 | SEL selector = NSSelectorFromString(@"dealloc");
66 | swizzleVoidMethod(classToSwizzle, selector, blockBefore, RSSwizzleModeAlways, NULL);
67 | }
68 |
69 | static void swizzleNumber(Class classToSwizzle, int(^transformationBlock)(int)){
70 | RSSwizzleInstanceMethod(classToSwizzle,
71 | @selector(calc:),
72 | RSSWReturnType(int),
73 | RSSWArguments(int num),
74 | RSSWReplacement(
75 | {
76 | int res = RSSWCallOriginal(num);
77 | return transformationBlock(res);
78 | }), RSSwizzleModeAlways, NULL);
79 | }
80 |
81 | #pragma mark - TESTS -
82 |
83 | @interface RSSwizzleTests : SenTestCase @end
84 |
85 | @implementation RSSwizzleTests
86 |
87 | #pragma mark - Setup
88 |
89 | +(void)setUp{
90 | [self swizzleDeallocs];
91 | [self swizzleCalc];
92 | }
93 |
94 | -(void)setUp{
95 | [super setUp];
96 | CLEAR_LOG();
97 | }
98 |
99 | #pragma mark - Dealloc Swizzling
100 |
101 | +(void)swizzleDeallocs{
102 | // 1) Swizzling a class that does not implement the method...
103 | swizzleDealloc([RSSwizzleTestClass_D class], ^{
104 | RSTestsLog(@"d-");
105 | });
106 | // ...should not break swizzling of its superclass.
107 | swizzleDealloc([RSSwizzleTestClass_C class], ^{
108 | RSTestsLog(@"c-");
109 | });
110 | // 2) Swizzling a class that does not implement the method
111 | // should not affect classes with the same superclass.
112 | swizzleDealloc([RSSwizzleTestClass_D2 class], ^{
113 | RSTestsLog(@"d2-");
114 | });
115 |
116 | // 3) We should be able to swizzle classes several times...
117 | swizzleDealloc([RSSwizzleTestClass_D class], ^{
118 | RSTestsLog(@"d'-");
119 | });
120 | // ...and nothing should be breaked up.
121 | swizzleDealloc([RSSwizzleTestClass_C class], ^{
122 | RSTestsLog(@"c'-");
123 | });
124 |
125 | // 4) Swizzling a class inherited from NSObject and does not
126 | // implementing the method.
127 | swizzleDealloc([RSSwizzleTestClass_A class], ^{
128 | RSTestsLog(@"a");
129 | });
130 | }
131 |
132 | -(void)testDeallocSwizzling
133 | {
134 | @autoreleasepool {
135 | id object = [RSSwizzleTestClass_D new];
136 | object = nil;
137 | }
138 | ASSERT_LOG_IS(@"d'-d-c'-c-C-a");
139 | }
140 |
141 | #pragma mark - Calc: Swizzling
142 |
143 | +(void)swizzleCalc{
144 |
145 | swizzleNumber([RSSwizzleTestClass_C class], ^int(int num){
146 | return num + 17;
147 | });
148 |
149 | swizzleNumber([RSSwizzleTestClass_D class], ^int(int num){
150 | return num * 11;
151 | });
152 | swizzleNumber([RSSwizzleTestClass_C class], ^int(int num){
153 | return num * 5;
154 | });
155 | swizzleNumber([RSSwizzleTestClass_D class], ^int(int num){
156 | return num - 20;
157 | });
158 |
159 | swizzleNumber([RSSwizzleTestClass_A class], ^int(int num){
160 | return num * -1;
161 | });
162 | }
163 |
164 | -(void)testCalcSwizzling
165 | {
166 | RSSwizzleTestClass_D *object = [RSSwizzleTestClass_D new];
167 | int res = [object calc:2];
168 | STAssertTrue(res == ((2 * (-1) * 3) + 17) * 5 * 11 - 20, @"%d",res);
169 | }
170 |
171 | #pragma mark - String Swizzling
172 | -(void)testStringSwizzling{
173 | SEL selector = @selector(string);
174 | RSSwizzleTestClass_A *a = [RSSwizzleTestClass_A new];
175 |
176 | RSSwizzleInstanceMethod([a class],
177 | selector,
178 | RSSWReturnType(NSString *),
179 | RSSWArguments(),
180 | RSSWReplacement(
181 | {
182 | NSString *res = RSSWCallOriginal();
183 | return [res stringByAppendingString:@"DEF"];
184 | }), RSSwizzleModeAlways, NULL);
185 |
186 | STAssertTrue([[a string] isEqualToString:@"ABCDEF"], nil);
187 | }
188 |
189 | #pragma mark - Class Swizzling
190 | -(void)testClassSwizzling{
191 | RSSwizzleClassMethod([RSSwizzleTestClass_B class],
192 | @selector(sumFloat:withDouble:),
193 | RSSWReturnType(NSNumber *),
194 | RSSWArguments(float floatSummand, double doubleSummand),
195 | RSSWReplacement(
196 | {
197 | NSNumber *result = RSSWCallOriginal(floatSummand, doubleSummand);
198 | return @([result doubleValue]* 2.);
199 | }));
200 |
201 | STAssertEqualObjects(@(2.), [RSSwizzleTestClass_A sumFloat:0.5 withDouble:1.5 ], nil);
202 | STAssertEqualObjects(@(4.), [RSSwizzleTestClass_B sumFloat:0.5 withDouble:1.5 ], nil);
203 | STAssertEqualObjects(@(4.), [RSSwizzleTestClass_C sumFloat:0.5 withDouble:1.5 ], nil);
204 | }
205 |
206 | #pragma mark - Test Assertions
207 | #if !defined(NS_BLOCK_ASSERTIONS)
208 | -(void)testThrowsOnSwizzlingNonexistentMethod{
209 | SEL selector = NSSelectorFromString(@"nonexistent");
210 | RSSwizzleImpFactoryBlock factoryBlock = ^id(RSSwizzleInfo *swizzleInfo){
211 | return ^(__unsafe_unretained id self){
212 | void (*originalIMP)(__unsafe_unretained id, SEL);
213 | originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
214 | originalIMP(self,selector);
215 | };
216 | };
217 | STAssertThrows([RSSwizzle
218 | swizzleInstanceMethod:selector
219 | inClass:[RSSwizzleTestClass_A class]
220 | newImpFactory:factoryBlock
221 | mode:RSSwizzleModeAlways
222 | key:NULL], nil);
223 | }
224 |
225 | -(void)testThrowsOnSwizzlingWithIncorrectImpType{
226 | // Different return types
227 | RSSwizzleImpFactoryBlock voidNoArgFactory =
228 | ^id(RSSwizzleInfo *swizzleInfo)
229 | {
230 | return ^(__unsafe_unretained id self){};
231 | };
232 | STAssertThrows([RSSwizzle
233 | swizzleInstanceMethod:@selector(methodReturningBOOL)
234 | inClass:[RSSwizzleTestClass_A class]
235 | newImpFactory:voidNoArgFactory
236 | mode:RSSwizzleModeAlways
237 | key:NULL], nil);
238 | // Different arguments count
239 | STAssertThrows([RSSwizzle
240 | swizzleInstanceMethod:@selector(methodWithArgument:)
241 | inClass:[RSSwizzleTestClass_A class]
242 | newImpFactory:voidNoArgFactory
243 | mode:RSSwizzleModeAlways
244 | key:NULL], nil);
245 | // Different arguments type
246 | RSSwizzleImpFactoryBlock voidIntArgFactory =
247 | ^id(RSSwizzleInfo *swizzleInfo)
248 | {
249 | return ^int(__unsafe_unretained id self){ return 0; };
250 | };
251 | STAssertThrows([RSSwizzle
252 | swizzleInstanceMethod:@selector(methodWithArgument:)
253 | inClass:[RSSwizzleTestClass_A class]
254 | newImpFactory:voidIntArgFactory
255 | mode:RSSwizzleModeAlways
256 | key:NULL], nil);
257 | }
258 |
259 | -(void)testThrowsOnPassingIncorrectImpFactory{
260 | STAssertThrows([RSSwizzle
261 | swizzleInstanceMethod:@selector(methodWithArgument:)
262 | inClass:[RSSwizzleTestClass_A class]
263 | newImpFactory:^id(id x){ return nil; }
264 | mode:RSSwizzleModeAlways
265 | key:NULL], nil);
266 | }
267 | #endif
268 |
269 | #pragma mark - Mode tests
270 |
271 | -(void)testAlwaysSwizzlingMode{
272 | for (int i = 3; i > 0; --i) {
273 | swizzleVoidMethod([RSSwizzleTestClass_A class],
274 | @selector(methodForAlwaysSwizzling), ^{
275 | RSTestsLog(@"A");
276 | },
277 | RSSwizzleModeAlways,
278 | NULL);
279 | swizzleVoidMethod([RSSwizzleTestClass_B class],
280 | @selector(methodForAlwaysSwizzling), ^{
281 | RSTestsLog(@"B");
282 | },
283 | RSSwizzleModeAlways,
284 | NULL);
285 | }
286 |
287 | RSSwizzleTestClass_B *object = [RSSwizzleTestClass_B new];
288 | [object methodForAlwaysSwizzling];
289 | ASSERT_LOG_IS(@"BBBAAA");
290 | }
291 |
292 | -(void)testSwizzleOncePerClassMode{
293 | static void *key = &key;
294 | for (int i = 3; i > 0; --i) {
295 | swizzleVoidMethod([RSSwizzleTestClass_A class],
296 | @selector(methodForSwizzlingOncePerClass), ^{
297 | RSTestsLog(@"A");
298 | },
299 | RSSwizzleModeOncePerClass,
300 | key);
301 | swizzleVoidMethod([RSSwizzleTestClass_B class],
302 | @selector(methodForSwizzlingOncePerClass), ^{
303 | RSTestsLog(@"B");
304 | },
305 | RSSwizzleModeOncePerClass,
306 | key);
307 | }
308 | RSSwizzleTestClass_B *object = [RSSwizzleTestClass_B new];
309 | [object methodForSwizzlingOncePerClass];
310 | ASSERT_LOG_IS(@"BA");
311 | }
312 |
313 | -(void)testSwizzleOncePerClassOrSuperClassesMode{
314 | static void *key = &key;
315 | for (int i = 3; i > 0; --i) {
316 | swizzleVoidMethod([RSSwizzleTestClass_A class],
317 | @selector(methodForSwizzlingOncePerClassOrSuperClasses), ^{
318 | RSTestsLog(@"A");
319 | },
320 | RSSwizzleModeOncePerClassAndSuperclasses,
321 | key);
322 | swizzleVoidMethod([RSSwizzleTestClass_B class],
323 | @selector(methodForSwizzlingOncePerClassOrSuperClasses), ^{
324 | RSTestsLog(@"B");
325 | },
326 | RSSwizzleModeOncePerClassAndSuperclasses,
327 | key);
328 | }
329 | RSSwizzleTestClass_B *object = [RSSwizzleTestClass_B new];
330 | [object methodForSwizzlingOncePerClassOrSuperClasses];
331 | ASSERT_LOG_IS(@"A");
332 | }
333 |
334 | @end
335 |
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests/RSTestsLog.h:
--------------------------------------------------------------------------------
1 | @interface RSTestsLog : NSObject
2 | +(void)log:(NSString *)string;
3 | +(void)clear;
4 | +(BOOL)is:(NSString *)compareString;
5 | +(NSString *)logString;
6 | @end
7 |
8 | #define ASSERT_LOG_IS(STRING) STAssertTrue([RSTestsLog is:STRING], @"LOG IS @\"%@\" INSTEAD",[RSTestsLog logString])
9 | #define CLEAR_LOG() ([RSTestsLog clear])
10 | #define RSTestsLog(STRING) [RSTestsLog log:STRING]
--------------------------------------------------------------------------------
/RSSwizzleTests/RSSwizzleTests/RSTestsLog.m:
--------------------------------------------------------------------------------
1 | #import "RSTestsLog.h"
2 | #import
3 |
4 | @implementation RSTestsLog
5 |
6 | static NSMutableString *_logString = nil;
7 | +(void)log:(NSString *)string{
8 | if (!_logString) {
9 | _logString = [NSMutableString new];
10 | }
11 | [_logString appendString:string];
12 | NSLog(@"%@",string);
13 | }
14 | +(void)clear{
15 | _logString = [NSMutableString new];
16 | }
17 | +(BOOL)is:(NSString *)compareString{
18 | return [compareString isEqualToString:_logString];
19 | }
20 | +(NSString *)logString{
21 | return _logString;
22 | }
23 |
24 | @end
25 |
26 |
27 | @interface RSTestsLogTests : SenTestCase @end
28 |
29 | @implementation RSTestsLogTests
30 |
31 | - (void)testLog{
32 | [RSTestsLog clear];
33 | RSTestsLog(@"A");
34 | RSTestsLog(@"B");
35 | RSTestsLog(@"C");
36 | STAssertTrue([[RSTestsLog logString] isEqualToString:@"ABC"], @"%@",[RSTestsLog logString]);
37 | }
38 |
39 | -(void)testAssertLogIs{
40 | [RSTestsLog clear];
41 | RSTestsLog(@"A");
42 | RSTestsLog(@"C");
43 | ASSERT_LOG_IS(@"AC");
44 | }
45 |
46 | @end
47 |
--------------------------------------------------------------------------------