美文网首页iOS DeveloperiOS Developer - RuntimeiOS 开发
iOS开发--Runtime黑魔法,替换系统类方法及属性

iOS开发--Runtime黑魔法,替换系统类方法及属性

作者: 断风刀 | 来源:发表于2016-09-20 09:48 被阅读3049次

    大多数情况下,系统类为我们提供的方法已经能够满足开发的需求,但有时候需求中的功能使用原生方法很难实现,或者系统类提供的原生方法存在bug.
    比如:我们要在所有的viewcontroll 的viewwillappear:方法之前通过NSLog输出方法的信息。
    实现的方法有多种,你可以这么做:

    1. 建一个uiviewcontroll 父类,重写viewwillappear方法,调用super viewwillappear 方法之前加上NSlog
    2. 所有新建的UIViewcontroller 继承第一步生成的
      上面方式可以完成该功能,但你做了那么多的修改,基本每个uiviewcontroller都去修改了父类,这种方法太过于消耗性能。

    但其实有更简单和高效的方法。
    OC中的类调用方法是通过三种方式来进行调用。

    1. 方法,代表类定义中一个方法类型(typedef struct objc_method *Method)
    2. SEL 选择器(typedef struct objc_selector *SEL),一个方法在运行时的名字,常见的有 [self performSelector:@selector(somemethod:) withObject:nil afterDelay:0.5]; @selector(somemethod:)作为方法的入口
    3. 方法的实现入口IMP及SEL
      这三种方法确定了具体调用哪一个函数
      下面是实现代码
    #import"UIViewController+Tracking.h"
    #import<objc/runtime.h>
    
    @implementationUIViewController(Tracking)
    
    +(void)load{
    NSString*className=NSStringFromClass(self.class);
    NSLog(@"classname%@",className);
    staticdispatch_once_tonceToken;
    dispatch_once(&onceToken,^{
    Classclass=[selfclass];
    
    //Whenswizzlingaclassmethod,usethefollowing:
    //Classclass=object_getClass((id)self);
    
    SELoriginalSelector=@selector(viewWillAppear:);
    SELswizzledSelector=@selector(xxx_viewWillAppear:);
    
    MethodoriginalMethod=class_getInstanceMethod(class,originalSelector);
    MethodswizzledMethod=class_getInstanceMethod(class,swizzledSelector);
    
    BOOLdidAddMethod=
    class_addMethod(class,
    originalSelector,
    method_getImplementation(swizzledMethod),
    method_getTypeEncoding(swizzledMethod));
    
    if(didAddMethod){
    class_replaceMethod(class,
    swizzledSelector,
    method_getImplementation(originalMethod),
    method_getTypeEncoding(originalMethod));
    }else{
    method_exchangeImplementations(originalMethod,swizzledMethod);
    }
    });
    }
    

    我们想要重写NSObject的 load 方法,oc给我们提供了objc/runtime.h类让我们获取这些东西,同时还提供了对类方法操作的函数
    我们想要实现的是,直接用一个方法替换掉系统的方法,然后把一些自定义的动作加到方法中。我们只想运行一次就够了,所以使用了 dispatch_once(&onceToken, ^{ …… }
    接下来给类添加了新方法
    把新方法和系统方法替换

    #pragmamark-Runtime
    
    -(void)bb_viewWillAppear:(BOOL)animated{
    NSLog(@"viewWillAppear:%@",self);
    [self bb_viewWillAppear:animated];
    }
    

    这样,新方法在实现的时候,调用的是 [self bb_viewwillAppear:animated]; 因为在上面已经用bb_viewwillAppear 和 viewwillAppear 互换了。所以实际上执行的是系统的viewwillAppear
    这个时候可能你又有疑问了,为什么实现是- (void)bb_viewWillAppear:(BOOL)animated{} 这样的
    这是因为 SEL swizzledSelector = @selector(bb_viewWillAppear:); 拿的就是我们新写的方法。

    相关文章

      网友评论

      • 梁森的简书:多人开发时如何判断某个系统方法被替换过呢
        上北以北:这个就是methord swizzing的弊端, 这么用只能把文档写好,不然别人很难知道哪些方法被替换过

      本文标题:iOS开发--Runtime黑魔法,替换系统类方法及属性

      本文链接:https://www.haomeiwen.com/subject/zuigettx.html