方法交换的原理
在本类开始, 顺着继承链, 在方法列表中, 查找要交换的Method
, 查找到后交换Method
的imp
指针
方法交换注意事项
在交换方法时, 要注意是否会影响到父类的方法实现.
方法交换模板
基础版
// 如果originalSelector或者swizzledSelector对应的Method, 不在自己的方法列表中, 会导致父类的方法收到影响
void swizzle1(Class aClass, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
if(originalMethod == nil || swizzledMethod == nil) {
return;
}
method_exchangeImplementations(originalMethod, swizzledMethod);
}
大众版 (RN / AFN都用的这个)
// 如果swizzledSelector对应的Method, 不在自己的方法列表中, 会导致父类的方法收到影响
void swizzle2(Class aClass, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
if(originalMethod == nil || swizzledMethod == nil) {
return;
}
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(aClass,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
建议版
void swizzle(Class aClass, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
if(originalMethod == nil || swizzledMethod == nil) {
return;
}
class_addMethod(aClass,
originalSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
class_addMethod(aClass,
swizzledSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
method_exchangeImplementations(class_getInstanceMethod(aClass, originalSelector), class_getInstanceMethod(aClass, swizzledSelector));
}
方法交换应用
- 页面的pv/pd 统计 (方法交换
viewDidAppear / viewDidDisappear
) - 防止按钮短时间点击多次 (交换
sendAction:to:forEvent:
)
DEMO
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person: NSObject
@property (nonatomic, assign) int age;
- (void)personInstanceMethod;
- (void)personInstanceMethod2;
@end
@implementation Person
- (void)personInstanceMethod {
NSLog(@"personInstanceMethod %d", self.age);
}
- (void)personInstanceMethod2 {
NSLog(@"personInstanceMethod2 %d", self.age);
}
@end
@interface Student : Person
@property (nonatomic, assign) int score;
- (void)studentInstanceMethod;
@end
@implementation Student
// 如果originalSelector或者swizzledSelector对应的Method, 不在自己的方法列表中, 会导致父类的方法收到影响
void swizzle1(Class aClass, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
if(originalMethod == nil || swizzledMethod == nil) {
return;
}
method_exchangeImplementations(originalMethod, swizzledMethod);
}
// 如果swizzledSelector对应的Method, 不在自己的方法列表中, 会导致父类的方法收到影响
void swizzle2(Class aClass, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
if(originalMethod == nil || swizzledMethod == nil) {
return;
}
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(aClass,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
void swizzle3(Class aClass, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
if(originalMethod == nil || swizzledMethod == nil) {
return;
}
class_addMethod(aClass,
originalSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
class_addMethod(aClass,
swizzledSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
method_exchangeImplementations(class_getInstanceMethod(aClass, originalSelector), class_getInstanceMethod(aClass, swizzledSelector));
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// swizzle1(self, @selector(studentInstanceMethod), @selector(personInstanceMethod2));
// swizzle2(self, @selector(studentInstanceMethod), @selector(personInstanceMethod2));
swizzle3(self, @selector(studentInstanceMethod), @selector(personInstanceMethod2));
});
}
- (void)studentInstanceMethod {
NSLog(@"studentInstanceMethod %d",self.score);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
[[Person new] personInstanceMethod];
[[Person new] personInstanceMethod2];
[[Student new] studentInstanceMethod];
}
return 0;
}
源码:
查找方法
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
auto const methods = cls->data()->methods();
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list_inline(*mlists, sel);
if (m) return m;
}
return nil;
}
static method_t *
getMethod_nolock(Class cls, SEL sel)
{
method_t *m = nil;
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
cls = cls->getSuperclass();
}
return m;
}
方法添加
static IMP
addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
{
IMP result = nil;
runtimeLock.assertLocked();
checkIsKnownClass(cls);
ASSERT(types);
ASSERT(cls->isRealized());
method_t *m;
if ((m = getMethodNoSuper_nolock(cls, name))) {
// already exists
if (!replace) {
result = m->imp(false);
} else {
result = _method_setImplementation(cls, m, imp);
}
} else {
// fixme optimize
method_list_t *newlist;
newlist = (method_list_t *)calloc(method_list_t::byteSize(method_t::bigSize, 1), 1);
newlist->entsizeAndFlags =
(uint32_t)sizeof(struct method_t::big) | fixed_up_method_list;
newlist->count = 1;
auto &first = newlist->begin()->big();
first.name = name;
first.types = strdupIfMutable(types);
first.imp = imp;
addMethods_finish(cls, newlist);
result = nil;
}
return result;
}
方法交换
void method_exchangeImplementations(Method m1, Method m2)
{
if (!m1 || !m2) return;
mutex_locker_t lock(runtimeLock);
IMP imp1 = m1->imp(false);
IMP imp2 = m2->imp(false);
SEL sel1 = m1->name();
SEL sel2 = m2->name();
m1->setImp(imp2);
m2->setImp(imp1);
// RR/AWZ updates are slow because class is unknown
// Cache updates are slow because class is unknown
// fixme build list of classes whose Methods are known externally?
flushCaches(nil, __func__, [sel1, sel2, imp1, imp2](Class c){
return c->cache.shouldFlush(sel1, imp1) || c->cache.shouldFlush(sel2, imp2);
});
adjustCustomFlagsForMethodChange(nil, m1);
adjustCustomFlagsForMethodChange(nil, m2);
}