ios runtime 二

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,636评论 0 9
  • 目录 Objective-C Runtime到底是什么 Objective-C的元素认知 Runtime详解 应用...
    Ryan___阅读 1,896评论 1 3
  • Runtime是什么 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我...
    SuAdrenine阅读 839评论 0 3
  • 参考链接: http://www.cnblogs.com/ioshe/p/5489086.html 简介 Runt...
    乐乐的简书阅读 2,097评论 0 9
  • Java环境搭建 工具 Windows764位 Jdkjdk-8u40-windows-x64 环境变量配置 将j...
    归来的良人阅读 123评论 0 1