美文网首页
ios runtime 二

ios runtime 二

作者: piao152 | 来源:发表于2016-12-30 20:02 被阅读23次

    (本文部分内容参考:http://www.jianshu.com/p/927c8384855a,文/兴宇(简书作者))

    OC的动态特性表现为了三个方面:

    -动态类型
    -动态关联
    -动态加载

    一、动态类型

    简单来说就是在编译时不确定具体对象类型,在运行时动态匹配,这个跟继承、多态不同,简单来说就是id类型的定义使用,id类型用于隐藏对象类型的类名部分,相当于C语言中的“void *”,这个概念容易理解,不多做记录。

    动态类型识别常用方法

    - (BOOL)isKindOfClass:classObj  //是否是classObj类或其子类**
    - (BOOL)isMemberOfClass:classObj  //是否是classObj的实例**
    - (BOOL)respondsTosSelector:selector  //类中是否有这个方法**
    - (BOOL)conformsToProtocol:(Protocol *)aProtocol;  //是否遵守协议Protocol
    - (BOOL)isProxy;  //是否代理
    

    二、动态关联

    1、动态关联方法

    借助IOS的方法调用机制(发消息),OC可以先跳过编译,到运行的时候才动态地添加函数调用,在运行时才决定要调用什么方法,需要传什么参数进去。这就是动态绑定,要实现他就必须用SEL变量绑定一个方法。最终形成的这个SEL变量就代表一个方法的引用。SEL并不是C里面的函数指针,SEL变量只是一个整数,他是该方法的ID

    形式1---从外部隐式调用一个不存在的方法:
    //隐式调用方法
    [target performSelector:@selector(resolveAdd:) withObject:@"test"];
    
    形式2---对象内部动态添加方法:
    //方法定义
    void runAddMethod(id self, SEL _cmd, NSString *string){
        NSLog(@"add C IMP ", string);
    }
    //调用:
    //给本类动态添加一个方法
    SEL sel;
    class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
    

    其中class_addMethod的四个参数分别是:
    1、Class cls 给哪个类添加方法,本例中是self
    2、SEL name 添加的方法名称
    3、IMP imp 方法的实现,C方法的方法实现可以直接获得。如果是OC方法,可以用+ (IMP)instanceMethodForSelector:(SEL)aSelector;获得方法的实现
    4、"v@:*"方法的签名,代表有一个参数的方法

    另外附上:替换方法定义、交换两个方法实现、设置一个方法的实现摘自简书:http://www.jianshu.com/p/927c8384855a,文/兴宇,谨慎使用
    #import "UIViewController+swizzling.h"
    #import <objc/runtime.h>
    
    @implementation UIViewController (swizzling)
    
    //load方法会在类第一次加载的时候被调用
    //调用的时间比较靠前,适合在这个方法里做方法交换
    + (void)load{
        //方法交换应该被保证,在程序中只会执行一次
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
    
            //获得viewController的生命周期方法的selector
            SEL systemSel = @selector(viewWillAppear:);
            //自己实现的将要被交换的方法的selector
            SEL swizzSel = @selector(swiz_viewWillAppear:);
            //两个方法的Method
            Method systemMethod = class_getInstanceMethod([self class], systemSel);
            Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
    
            //首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败
            BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
            if (isAdd) {
                //如果成功,说明类中不存在这个方法的实现
                //将被交换方法的实现替换到这个并不存在的实现
                class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
            }else{
                //否则,交换两个方法的实现
                method_exchangeImplementations(systemMethod, swizzMethod);
            }
    
        });
    }
    
    - (void)swiz_viewWillAppear:(BOOL)animated{
        //这时候调用自己,看起来像是死循环
        //但是其实自己的实现已经被替换了
        [self swiz_viewWillAppear:animated];
        NSLog(@"swizzle");
    }
    
    @end
    
    PS:当调用一个不存在的方法时(含未实现),系统会在崩溃之前调用系统预设的拦截方法,也就是说,采用动态绑定方法时可以重写NSObject的拦截方法,防止此类崩溃。
    + (BOOL)resolveClassMethod:(SEL)sel;    //当你调用一个不存在的类方法的时候,会调用这个方法,默认返回NO,你可以加上自己的处理然后返回YES。
    + (BOOL)resolveInstanceMethod:(SEL)sel;    //同上,实例方法的处理
    - (id)forwardingTargetForSelector:(SEL)aSelector;    //将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。
    - (void)forwardInvocation:(NSInvocation *)anInvocation;    //将你调用的不存在的方法打包成`NSInvocation`传给你。做完你自己的处理后,调用`invokeWithTarget:`方法让某个target触发这个方法
    

    2、动态关联对象

    //首先定义一个全局变量,用它的地址作为关联对象的key
    static char associatedObjectKey;
    //设置关联对象
    objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串属性",OBJC_ASSOCIATION_RETAIN_NONATOMIC);
     //获取关联对象
    NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);
    NSLog(@"AssociatedObject = %@", string);
    
    使用方式举例
    const char JDPaySingleObjectDictionary;
    
    - (void)sendObject:(id)object
    {
        [self sendObject:object withIdentifier:@"JDPaySingleObjectDictionary"];
    }
    
    - (void)sendObject:(id)object withIdentifier:(NSString *)identifier
    {
        NSAssert(identifier != nil, @"identifier can't be nil.");
        
        NSMutableDictionary *eventHandlerDictionary = objc_getAssociatedObject(self,&JDPaySingleObjectDictionary);
        if (eventHandlerDictionary == nil) {
            return;
        }
         
        void(^block)(id object) = [eventHandlerDictionary objectForKey:identifier];
        if (block!=NULL)
        {
            block(object);
            [eventHandlerDictionary removeObjectForKey:identifier];
            return;
        }
    }
    
    - (void)receiveObject:(void(^)(id object))sendObject
    {
        [self receiveObject:sendObject withIdentifier:@"JDPaySingleObjectDictionary"];
    }
    
    - (void)receiveObject:(void(^)(id object))sendObject withIdentifier:(NSString *)identifier
    {
        NSAssert(identifier != nil, @"identifier can't be nil.");
        
        NSMutableDictionary *eventHandlerDictionary = objc_getAssociatedObject(self,&JDPaySingleObjectDictionary);
        if (eventHandlerDictionary == nil) {
            eventHandlerDictionary = [[NSMutableDictionary alloc] init];
            objc_setAssociatedObject(self, &JDPaySingleObjectDictionary, eventHandlerDictionary, OBJC_ASSOCIATION_RETAIN);
        }
        
        [eventHandlerDictionary setObject:sendObject forKey:identifier];
    }
    
    - (void)receiveObjectSuccess:(void (^)(id successObj))sendObject withSuccessId:(NSString *)successId andFailed:(void (^)(id errorObj))errorObject WithFailedId:(NSString *)failedId
    {
        NSAssert(successId != nil, @"successId can't be nil.");
        NSAssert(failedId != nil, @"failedId can't be nil.");
        
        [self receiveObject:sendObject withIdentifier:successId];
        [self receiveObject:errorObject withIdentifier:failedId];
    }
    

    三、动态加载(动态创建新类)
    使用OC提供的与runtime相关的函数,动态的创建一个新的类
    举例说明:

    -(void)viewDidLoad
    {
        [super viewDidLoad];
        //创建一个名为CustomView的类,它是UIView的子类
        Class newClass = objc_allocateClassPair([UIView class], "CustomView", 0);
        //为该类增加一个名为 report的方法,具体实现是reportFunction方法
        class_addMethod(newClass, @selector(report), (IMP)reportFunction, "v@:");
        //注册该类
        objc_registerClassPair(newClass);
    }
    void reportFunction(id self, SEL _cmd)
    {
        NSLog(@"This object is %p, and class is %@ and super class is %@", self, [self class], [self superclass]);
    }
    

    相关文章

      网友评论

          本文标题:ios runtime 二

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