欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Objective-C 奇巧淫技--IMS

程序员文章站 2023-11-02 18:10:58
奇技淫巧 指过于奇巧而无益的技艺与制品. ims指的是 instance method swizzling, 实例方法混淆. 下段代码是一个instance method swizzling和一...

奇技淫巧 指过于奇巧而无益的技艺与制品.
ims指的是 instance method swizzling, 实例方法混淆.
下段代码是一个instance method swizzling和一个method swizzling的例子:

// man.m
- (void)run
{
    nslog(@"%s, %@", __func__, _name);
}
- (void)jump
{
    nslog(@"%s, %@", __func__, _name);
}
- (void)handsup
{
    nslog(@"%s, %@", __func__, _name);
}
- (void)handsdown
{
    nslog(@"%s, %@", __func__, _name);
}

//  viewcontroller.m
- (void)viewdidload {
    ...
    man *a = [man manwithname:@"a"];
    man *b = [man manwithname:@"b"];

    [self swizzleinstancemethodwithinstance:a originalsel:@selector(run) replacementsel:@selector(jump)];
    [self swizzleinstancemethodwithclass:[man class] originalsel:@selector(handsup) replacementsel:@selector(handsdown)];

    [a run];
    [b run];

    [a handsup];
    [b handsup];
}


// 输出的结果是
2015-03-14 23:53:39.832 testruntime[2196:629365] -[man jump], a
2015-03-14 23:53:39.833 testruntime[2196:629365] -[man run], b
2015-03-14 23:53:39.833 testruntime[2196:629365] -[man handsdown], a
2015-03-14 23:53:39.833 testruntime[2196:629365] -[man handsdown], b

为什么run方法是只有对象a被替换了,handsup方法是都被替换了呢?
我们先来看下普通的method swizzling是如何实现的

- (void)swizzleinstancemethodwithclass:(class)clazz originalsel:(sel)original replacementsel:(sel)replacement
{
    method a = class_getinstancemethod(clazz, original);
    method b = class_getinstancemethod(clazz, replacement);
    // class_addmethod 为该类增加一个新方法
    if (class_addmethod(clazz, original, method_getimplementation(b), method_gettypeencoding(b)))
    {
        // 替换类方法的实现指针
        class_replacemethod(clazz, replacement, method_getimplementation(a), method_gettypeencoding(a));
    }
    else
    {
        // 交换2个方法的实现指针
        method_exchangeimplementations(a, b);
    }
}

instance method swizzling是用了类似kvo的办法.
先动态添加一个类mysubclass继承自原来的类,然后修改对象a的isa为新类,再替换掉新类的方法.

- (void)swizzleinstancemethodwithinstance:(id)object originalsel:(sel)original replacementsel:(sel)replacement
{
    class newclass = objc_allocateclasspair([object class], "mysubclass", 0);
    objc_registerclasspair(newclass);

    method a = class_getinstancemethod(newclass, original);
    method b = class_getinstancemethod([object class], replacement);
    if (class_addmethod(newclass, original, method_getimplementation(b), method_gettypeencoding(b)))
    {
        class_replacemethod(newclass, replacement, method_getimplementation(a), method_gettypeencoding(a));
    }
    else
    {
        method_exchangeimplementations(a, b);
    }

    object_setclass(object, newclass);
}