Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to swizzle a method of a private class

I have a private class (both declared & defined within .m) as an addition to an implementation of a different class, that happens to use that private class internally.

I'd like to swizzle one of the methods of that private class.

I defined a category and did the usual:

+(void)load 
{
    Method original, swizzled;

    original = class_getInstanceMethod(objc_getClass("SomePrivateClass"), @selector(somePrivateMethod:));
    swizzled = class_getInstanceMethod(self, @selector(swizzled_somePrivateMethod:));
    method_exchangeImplementations(original, swizzled); 
}

The issue is that my implementation obviously doesn't know anything about this private class and self refers to the class I am adding the category to, whichever class that might be. So I have no way of calling the original implementation and in general working with the private class.

What is the proper approach to tackle this?

like image 258
Jacob K Avatar asked Mar 12 '14 19:03

Jacob K


People also ask

Can you swizzle in Swift?

Starting from version 5.1, Swift provides its own native version of method swizzling that does not rely on Objective-C's message passing. When using the new Xcode 11, the dynamic modifier can be used on arbitrary functions and methods.

How does method swizzling work?

Method swizzling is the process of changing the implementation of an existing selector. It's a technique made possible by the fact that method invocations in Objective-C can be changed at runtime, by changing how selectors are mapped to underlying functions in a class's dispatch table.


1 Answers

Managed to get this to work, it's pretty simple actually.

So the way I did it:

  • made a NSObject category: @interface NSObject(PrivateSwizzleCategory)
  • swizzled:

    +(void)load
    {
        Method original, swizzled;
    
        original = class_getInstanceMethod(objc_getClass("SomePrivateClass"), @selector(somePrivateMethod:));
        swizzled = class_getInstanceMethod(self, @selector(swizzled_somePrivateMethod:));   
        method_exchangeImplementations(original, swizzled);
    }
    
  • To call the original implementation, I had to cast self to NSObject:

    id ret = [(NSObject *)self swizzled_somePrivateMethod:someParam];
    
  • To access private properties of the private class, I used valueForKey on self:

    id privateProperty = [self valueForKey:@"__privateProperty"];
    

Everything works!

like image 84
Jacob K Avatar answered Sep 25 '22 05:09

Jacob K