ios runtime 二

2016-12-30  本文已影响23人  piao152

(本文部分内容参考: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]);
}
上一篇下一篇

猜你喜欢

热点阅读