-
诶呀,起因:这两天在项目中遇到了个
bug
,我的大leader
把代码改了。。我又不敢说啥。。传说中的黑魔法(iOS - Method swizzling)
他不会用,准备稍微写写嘲讽他😆。 -
网上也有好多相关的文章,但他不查,这事闹的~~ (谁让人家是领导呢。。)
1. Method swizzling
的简单使用
-
- 常见的需求:在进来页面后添加打点统计~
这个时候,当然也可以手动添加。不过肯定会添加到你呻吟。
-
- 嘿嘿,适当的时机引入了
Method swizzling
,可谓舒服的要死
- 嘿嘿,适当的时机引入了
-
- 简单写下使用代码
兄弟们,我就把注释直接写在代码中了。(今天主要是想跟兄弟们分析下源码~)
//我们先创建一个分类
#import "UIViewController+XG.h”
//引入运行时
#import <objc/runtime.h>
@implementation UIViewController (XG)
// 只有在load里面 swizzling才是最安全的~
+ (void) load {
//swizzling 改变了全局状态,我们要保证他执行一次,而且在每个线程运行时都是可用的,GCD单例就比较完美了
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//实例方法,我们用这个
Class class = [self class];
// @selector
SEL originalSeletor = @selector(viewDidAppear:);
SEL swizzlingSelector = @selector(xg_textViewDidAppear:);
//method
Method originalMethod = class_getInstanceMethod(class, originalSeletor);
Method swizzlingMethod = class_getInstanceMethod(class, swizzlingSelector);
//类方法
// Class class2 = object_getClass((id)self);
// Method originalMethod2 = class_getClassMethod(class2, <#SEL _Nonnull name#>)
// Method swizzlingMethod2 = class_getClassMethod(class2, <#SEL _Nonnull name#>)
// sel (绑定method) -> imp
// 加入方法是否成功
BOOL didAddMethod = class_addMethod(class, originalSeletor, method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod));
if (didAddMethod) {
//成功的话。替换操作
class_replaceMethod(class, swizzlingSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else{
//没有成功的话直接交换
method_exchangeImplementations(originalMethod, swizzlingMethod);
}
});
}
- (void) xg_textViewDidAppear:(BOOL)animated{
[self xg_textViewDidAppear:animated];
NSLog(@"xg_textViewDidAppear : %@",self);
}
@end
这个时候进入界面的时候就会打印了~
-
- 直接看输出吧。
Method swizzling
使用起来还是挺舒服的~~
2. Method swizzling
源码
本来我打算看
汇编
调用的,不过看见新出来了objc4-818.2源码
,心血来潮弄了个工程调试了~
2.1. 锁定源码
-
- 大多数兄弟们分析问题都会看源码。这个做法是非常的👍。(我是😆,经常打着看源码的幌子划水)
-
- 我们来分析源码了~
- 2.1 主要方法有三个
class_addMethod
、class_replaceMethod
、method_exchangeImplementations
-
- 我们先分析
class_addMethod
和class_replaceMethod
- 我们先分析
源码实现~
BOOL
class_addMethod(Class cls, SEL name, IMP imp, const char *types)
{
if (!cls) return NO;
mutex_locker_t lock(runtimeLock);
return ! addMethod(cls, name, imp, types ?: "", NO);
}
IMP
class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
{
if (!cls) return nil;
mutex_locker_t lock(runtimeLock);
return addMethod(cls, name, imp, types ?: "", YES);
}
-
- 区别分析~
-
- 断点调试~(我新建一个
XGPerson
,然后调用方法)
- 断点调试~(我新建一个
他们最终都会走到
addMethod
方法
2.2. Method swizzling
源码分析
-
addMethod
方法
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;
}
-
- 兄弟们,我画了
思维导图
,虽然画的比较low
,哈哈
- 兄弟们,我画了
-
method_exchangeImplementations
源码分析
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);
}
这个代码写的就明明白白了~
而且这个也非常生动形象的说明了 sel 和 imp 的关系。
3. 结语
- 希望这篇文章对大家有帮助吧(我个菜鸡可能没有资格说~),重要的是,希望这篇文章被leader看见~😆