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); }