Swizzling a single instance, not a class

前端 未结 3 1964
我寻月下人不归
我寻月下人不归 2021-02-04 04:05

I have a category on NSObject which supposed to so some stuff. When I call it on an object, I would like to override its dealloc method to do some cleanups.

I wanted to

相关标签:
3条回答
  • 2021-02-04 04:37

    I made a swizzling API that also features instance specific swizzling. I think this is exactly what you're looking for: https://github.com/JonasGessner/JGMethodSwizzler

    It works by creating a dynamic subclass for the specific instance that you're swizzling at runtime.

    0 讨论(0)
  • 2021-02-04 04:48

    Method selection is based on the class of an object instance, so method swizzling affects all instances of the same class - as you discovered.

    But you can change the class of an instance, but you must be careful! Here is the outline, assume you have a class:

    @instance MyPlainObject : NSObject
    
    - (void) doSomething;
    
    @end
    

    Now if for just some of the instances of MyPlainObject you'd like to alter the behaviour of doSomething you first define a subclass:

    @instance MyFancyObject: MyPlainObject
    
    - (void) doSomething;
    
    @end
    

    Now you can clearly make instances of MyFancyObject, but what we need to do is take a pre-existing instance of MyPlainObject and make it into a MyFancyObject so we get the new behaviour. For that we can swizzle the class, add the following to MyFancyObject:

    static Class myPlainObjectClass;
    static Class myFancyObjectClass;
    
    + (void)initialize
    {
       myPlainObjectClass = objc_getClass("MyPlainObject");
       myFancyObjectClass = objc_getClass("MyFancyObject");
    }
    
    + (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy
    {
       object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass);
    }
    

    Now for any original instance of MyPlainClass you can switch to behave as a MyFancyClass, and vice-versa:

    MyPlainClass *mpc = [MyPlainClass new];
    
    ...
    
    // masquerade as MyFancyClass
    [MyFancyClass changeKind:mpc fancy:YES]
    
    ... // mpc behaves as a MyFancyClass
    
    // revert to true nature
    [MyFancyClass changeKind:mpc: fancy:NO];
    

    (Some) of the caveats:

    You can only do this if the subclass overrides or adds methods, and adds static (class) variables.

    You also need a sub-class for ever class you wish to change the behaviour of, you can't have a single class which can change the behaviour of many different classes.

    0 讨论(0)
  • 2021-02-04 04:59

    You can't really do this since objects don't have their own method tables. Only classes have method tables and if you change those it will affect every object of that class. There is a straightforward way around this though: Changing the class of your object at runtime to a dynamically created subclass. This technique, also called isa-swizzling, is used by Apple to implement automatic KVO.

    This is a powerful method and it has its uses. But for your case there is an easier method using associated objects. Basically you use objc_setAssociatedObject to associate another object to your first object which does the cleanup in its dealloc. You can find more details in this blog post on Cocoa is my Girlfriend.

    0 讨论(0)
提交回复
热议问题