iOS中的 Method_Swizzling

黑魔法 Method_Swizzling

  • 原理: Method_Swizzling是发生在运行时的,主要用于在运行时将两个Method进行交换,我们可以将Method Swizzle代码写到任何地方,但是只有在Method_Swizzling这段Method Swizzle代码执行完毕之后互换才起作用。
  • Method_Swizzling交换时机:尽可能在+load方法中实现
    • +load的执行时机:+load 方法会在加载类的时候就被调用,也就是 ios 应用启动的时候,就会加载所有的类,main函数之前,就会调用每个类的 +load 方法
    • 子类的+load方法会在它的所有父类(不包括父类分类中的+load)的+load方法执行之后执行
    • 分类的+load方法会在所有的主类的+load方法执行之后执行
    • 不同的类之间的+load方法的调用顺序是不确定的
    • + initialize:方法类似一个懒加载,initialize是在类或者其子类的第一个方法被调用前调用,且默认只加载一次;+ initialize 的调用发生在 +init 方法之前。
    • +load 和 + initialize 差异比较
+(void)load +(void)initialize
执行时机 在main函数之前执行 在类的方法被第一次调用时执行
若自身未定义,是否沿用父类的方法?
类别中的定义 全都执行,但后于类中的方法 覆盖类中的方法,只执行一次
  • 原子性:使用dispatch_once来执行方法交换,这样可以保证只运行一次
  • Method_Swizzling的两种实现
    • 直接交换: (在子类中交换方法,原始方法子类未实现,父类实现时,会将父类中的方法也交换)
BOOL simple_Swizzle(Class aClass,SEL originalSel, SEL swizzleSel)
{
         Method originalMethod = class_getInstanceMethod(aClass, originalSel);
         Method swizzleMethod = class_getInstanceMethod(aClass,swizzleSel);
         method_exchangeImplementations(originalMethod, swizzleMethod);
         return YES;   
}
  • 示例: A类有方法work,B类继承自A类,有方法b_work,此时在B类分类的+load方法中交换work和b_work,并在b_work中调自身(回调回work),此时B对象b,调用work,实际调用的是b_work后再调用work(由于B继承自A,所以可以找得到work方法),A对象a,调用work,实际调用也是b_work,由于A类未实现b_work方法,出现崩溃
  • 先添加再交换:(保证只在子类中交换方法,不影响父类)
  • 这种方法会先尝试给自己添加work方法实现,如果B类没有实现work方法,class_addMethod会成功添加一个新的属于son自己的work方法,同时将本来要swizzle的方法的实现直接复制进work里,然后再将父类的IMP给swizzle
BOOL best_Swizzle(Class aClass, SEL originalSel, SEL swizzleSel)
{
         // 如果originalSel没有实现过,class_getInstanceMethod无法找到该方法,所以originalMethod为nil
         Method originalMethod = class_getInstanceMethod(aClass, originalSel);
         Method swizzleMethod = class_getInstanceMethod(aClass, swizzleSel);
         BOOL didAddMethod = class_addMethod(aClass, originalSel, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
         if (didAddMethod) {
             //  当originalMethod为nil时,这里的class_replaceMethod将不做替换,所以swizzleSel方法里的实现还是自己原来的实现
             class_replaceMethod(aClass, swizzleSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
         } else {
             method_exchangeImplementations(originalMethod, swizzleMethod);
         }
         return YES;
}
  • swizzle一个子类到父类到根类都没有实现过的方法,会出现死循环,死循环是由于didAddMethod成功了,所以调用originalSel实际调用的是swizzleSel,class_replaceMethod交换失败了,所以在swizzleSel中调用originalSel,其实调用的还是自身,所以出现死循环
  • 解决方法是在originalMethod为nil时,替换后将swizzleSel复制一个不做任何事的空实现
if (!originalMethod)  {
          class_addMethod(aClass, originalSel, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
          method_setImplementation(swizzleMethod, imp_implementationWithBlock(^(id self, SEL _cmd){ }));        
}

Method_Swizzling的具体用途AOP(面向切面编程)

  1. 页面统计(AOP)、NSMutableArray的insert等插入nil,hook: 給全局图片名称添加前缀,分类中为已有的属性或者方法添加钩子(增加一段代码)
  2. 用于记录或者存储,比方说记录ViewController进入次数、Btn的点击事件、ViewController的停留时间等等。 可以通过Runtime获取到具体ViewController、Btn信息,然后传给服务器
  3. 添加需要而系统没提供的方法,比方说修改Statusbar颜色。用于轻量化、模块化处理
  4. 用Method Swizzle 动态给指定的方法添加代码,以解决Cross-cutting concern的编程方式叫做Aspect Oriented Programming,将逻辑处理和事件记录的代码解耦
  5. AOP可以把琐碎的事务从主逻辑中分离出来,作为单独的模块,它是对面向对象编程模式的一种补充
  6. 比较好的AOP库,封装了runtime,Method Swizzling这些黑科技,该库只有两个API
      + (id<AspectToken>)aspect_hookSelector:(SEL)selector
                                  withOptions:(AspectOptions)options
                                   usingBlock:(id)block
                                        error:(NSError **)error;
      - (id<AspectToken>)aspect_hookSelector:(SEL)selector
                                  withOptions:(AspectOptions)options
                                   usingBlock:(id)block
                                        error:(NSError **)error;

Objective-C类的继承、方法的重写和重载

  1. 继承:Objective-c中类的继承与C++类似,不同的是Objective-c不支持多重继承,一个类只能有一个父类,单继承使Objective-c的继承关系很简单,易于管理程序
  2. 重写:在Objective-c中,子类可继承父类中的方法,而不需要重新编写相同的方法,直接可以使用父类的方法。
    但有时我们不想使用使用父类方法,而是想作一定的修改,怎么办呢?只要将子类中书写一个与父类具有相同的方法名、返回类型和参数,就可以将将父类的方法覆盖重写
  • 如何只导入父类,执行子类的方法
OverRide *overRide = (OverRide*)[[NSClassFromString(@"SubOverRide") alloc] init];
[overRide superMethod];
  • 继承中方法调用的流程:首先到子类中去找,如果有该方法,就调用该方法,如果没有,就到父类中去找,父类没有就去父类的父类找,最后都没有找到,程序崩溃
  1. 重载:在Objective-c中,方法是不能重载的。也就是说我们不能在类中定义这样的两个方法:它们的名子相同,参数个数相同,参数类型不同,不同的返回值类型。否则Xcode会报错。

引用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容