Method Swizzling
Transcription
Method Swizzling
Method Swizzling The good, the bad and the ugly Some background 1. The Objec,ve-‐C message model • You are not calling func,ons, you are passing messages. 2. Power to know your “environment” • You can get informa,on about if a message call is possible before do it 3. Power to change it • You may create methods, classes, variables on the fly, delete them 2 So, what is method swizzling? Formally this is method swizzling: – Replace a selector implementa,on with another implementa,on temporarily or permanently* 3 OK, but for what I can use it? • Do you do unit test? • Have you tried to make a network connec,on from a unit test? • How did you do it? • OCMock uses it • What if I tell you, You can just swizzle any method you want to, from ANY class 4 But wait, there is more! ● ● ● ● ● ● ● ● You can also intercept push no,fica,ons, Sent to background events Sent to foreground Open URLs Handle URLs Proxy request Change persistency storages Possibili,es, possibili,es everywhere 5 An example Class to Swizzle Class with replacement @implementa,on A -‐(void) foo{ NSLog(@”hi”); } @end @implementa,on A(Swizzled) -‐(void) bar{ NSLog(@”bye”); } @end 6 An Example (cont.) Class clazz = [A class]; Method method = class_getInstanceMethod(clazz, @selector(foo)); class_replaceMethod(clazz, @selector(foo), class_getMethodImplementa,on(clazz, @selector(bar)), method_getTypeEncoding(method)); 7 An Example (cont.) ● ● ● ● ● ● [[[A alloc] init] foo] => bye; [[[A alloc] init] bar] => bye; Selector != Method != IMP Selector, the highest level of abstrac,on Method, metainfo about the selector IMP, the actual code of the selector. 8 Of course, nothing is free ● ● ● ● ● Method swizzling is not atomic. Changes behavior of un-‐owned code. Possible naming conflicts. Swizzling changes the method's arguments. The order of swizzles maoers. 9 Method swizzling + ARC ugly mix 1. Remember the sample with class A 2. OK, what if you want to swizzle the method to add some func,onality besides what foo does? 3. You not only need to replace, you have to create a new method. 10 Method swizzling + ARC ugly mix (cont.) What you want to do: The issues: @implementa,on A(Swizzled) -‐(void) bar{ [self __foo]; NSLog(@”bye”); } @end ● ● 11 Here, the compiler will complain that __foo is not defined in any interface, well, not yet ;) To cheat the compiler, you need to define a category, just w/the @interface Method swizzling + ARC ugly mix (cont.) Class clazz = [A class]; Method method = class_getInstanceMethod(clazz, @selector(foo)); class_addMethod(clazz, @selector(__foo), class_getMethodImplementa,on(clazz, @selector(foo)), method_getTypeEncoding(method)); class_replaceMethod(clazz, @selector(foo), class_getMethodImplementa,on(clazz, @selector(bar)), method_getTypeEncoding(method)); 12 Method swizzling + ARC ugly mix (cont.) ● ● ● [[A alloc] init] foo] => hi bye [[A alloc] init] __foo] => hi [[A alloc] init] bar] => hi bye 13 Not to say the obvious ● ● ● ● Difficult to understand (looks recursive) Difficult to debug Scope can be fuzzy. You might be a ghost, but it's scary when people can see you. 14 Conclusions ● ● ● ● The Good: you can modify your environment at please, and provide abstrac,on over certain processes. The Bad: side effects, name conflicts, trick the compiler. The Ugly: nasty errors, very hard to debug, because, basically you don't know where the code is. Booom line: with great power comes great responsibility. 15