-.runtime简介

  • runtime简称运行时,OC就是运行时机制,也就是运行时的一些机制,其中最主要的是消息机制;
  • 对于C语言,函数的调用在编辑的时候,会决定调用哪个函数;
  • 对于OC的函数,属于动态调用过程,在编译的时候,并不能决定真正调用哪个函数,只有真的运行的时候,才会根据函数的名称找到对应的函数来调用;
  • 事实证明:

    在编译阶段,OC可以调用任何函数,即使这事函数并未实现,只要声明过就不会报错;

    在编译阶段,C语言调用为实现的函数就会报错。

二、runtime作用

  1. 发送消息
  • 方法调用的本质,就是让对象发送消息
  • objc_msgSend, 只有对象才能发送消息,以objc开头
  • 使用消息机制前提,必须导入#import <objct/message.h>
  • 简单使用
  • clang

任何方法的调用本质,就是发送一个消息,用runtime发送消息。OC底层实现通过runtime实现。

解决:运行时方法中参数不提醒的问题

xcode6 之前,苹果运行使用objc_msgSend 是有参数提示的,之后就没有了。苹果不推荐我们使用runtime。如果方法,看:http://www.cnblogs.com/lyz0925/p/7365058.html。

简单代码如下:

    //----runtime消息机制
//id self : 类名
//SEL op
   //...:表示该方法需要,传入对应的参数。
//objc_msgSend(<#id self#>, <#SEL op, ...#>)
id object = objc_msgSend([NSObject class], @selector(alloc));
objc_msgSend(object, @selector(init));
NSLog(@"---%@----", object);

以上的代码,类似于[[NSObject alloc]   init] 的功能。

clang -rewrite-objc 文件名--使用编译器

runtime:方法都是有前缀的,谁的事情谁开头,例如-消息机制 用objc_msgSend 开头一样。

需要用到runtime的消息机制大致有:可以帮我们调用私有方法,另外就是装逼用,不要为了用runtime而用,而是再必要的时候才用。

如果消息机制调用了多个参数的话,如下代码:

//步骤:1.创建一个person类,2.声明一个run带NSInter类型的方法,3.实现该方法。之后再调用
Person *pers = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
objc_msgSend(pers, sel_registerName("init"));
objc_msgSend(pers, @selector(run:), 30); //30 就是runtime需要传入的参数

方法调用流程:--面试

例如:如果调用person类上的eat方法? 对象方法:类对象的方法列表, 类方法:元类中查找方法

1.通过isa去对应的类中寻找对应的类方法

2.把方法名转成方法编号(注册方法编号--用编号可以快速的找到它)

  3.根据方法编号去查找对应方法

  4.找到只是最终函数实现地址,根据地址去方法区调用应对函数

 内存5大区:栈 堆 静态区 常量区 方法区

栈:不需要手动管理内存,自动管理

2.交换方法(只要想修改系统方法实现时使用)

  来个需求吧:每次UIImage用系统方法加载图片时,告诉我是否图片加载成功。

常规的方法,就是:

    //判读图片是否赋值成功
UIImage *image = [UIImage imageNamed:@"111.png"];
if (image == nil) {
NSLog(@"---图片赋值失败------");
}else {
NSLog(@"---图片赋值成功------");
}

  弊端:每次都要这么判断,太麻烦;

  另外一种方法就是,自定义一个继承UIImage的类YZImage,然后重写imageNamed:的方法:

//重写方法:想给系统的方法添加额外功能
+ (UIImage *)imageNamed:(NSString *)name {
UIImage *image = [super imageNamed:name];
if (image) {
NSLog(@"success");
}else {
NSLog(@"fail");
}
return image;
}

  弊端:每次使用,都需要导入自定义的头文件;如果是突然修改,项目大了,不太好替换。

  因此,用运行时来实现这个需求是最好的选择!--给添加分类,用运行时交换方法

  注意:在分类中,最好不要重写系统方法,一旦重写,把系统方法实现给覆盖了。

  步骤:1.给系统的方法添加一个分类;

     2.自己实现一个带有扩展功能的方法(命名时,给系统方法,加个前缀,例如yz_imageName:);

      3.只交换一次。在load方法中,实现--load方法把内存加载进内存的时候调用,只会调用一次,另外需要在方法被调用之前,就替换系统的方法。 而initialize 会调用多次。

      4.获取iamgeNamed方法、获取要替换的方法、交换方法:runtime

代码实现如下:

这里的代码是在UIImage 的自定义分类中实现的:

+ (void)load {
Method imageName = class_getClassMethod(self, @selector(imageNamed:));
Method yz_imageName = class_getClassMethod(self, @selector(yz_imageName:));
//交换方法:
method_exchangeImplementations(imageName, yz_imageName);
//虽然走的imageName 但实际上是调用的yz_imageName
//虽然走的yz_imageName 但实际上是调用的imageName
} + (UIImage *)yz_imageName:(NSString *)name {
// UIImage *image = [UIImage imageNamed:name]; --如果这里调用imageName就会有死循环的问题
UIImage *image = [UIImage yz_imageName:name];
if (image) {
NSLog(@"success");
}else {
NSLog(@"fail");
}
return image;
}

3.动态添加方法

  在动态添加方法的时候,performSelector就是一个典型的例子。

  为什么要动态添加方法?OC都是懒加载机制,只要一个方法实现了,就会马上添加到方法列表中。

  注意:只要一个对象调用了一个未实现的方法就会调用resolveInstanceMethod:这个方法,进行处理。该方法的作用时,动态添加方法,处理未实现。

  代码实现如下:

在子类的分类中实现:

#import "printName.h"
#import <objc/message.h> @implementation printName //定义C函数
void eat(id self, SEL sel, printName *print) {
NSLog(@"-----%@ %@ name=%@-----", self, NSStringFromSelector(sel), print.name);
} + (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(eat)) { /** Class cls
cls 参数表示需要添加新方法的类。 SEL name
name 参数表示 selector 的方法名称,可以根据喜好自己进行命名。 IMP imp
imp 即 implementation ,表示由编译器生成的、指向实现方法的指针。也就是说,这个指针指向的方法就是我们要添加的方法。 const char *types
最后一个参数 *types 表示我们要添加的方法的返回值和参数。
*/ class_addMethod(self, @selector(eat), (IMP)eat, "v@:@");
}
return [super resolveInstanceMethod:sel];
} @end

代码调用如下:

    //动态添加方法--比较难理解
printName *print = [[printName alloc] init];
print.name = @"nameValue";
[print performSelector:@selector(eat) withObject:print];

 4.给分类动态添加属性(成员变量)

  步骤:1.创建一个NSObject的分类:Property;

     2..h中声明属性 name;

        3..m中用runtime实现

#import "NSObject+Property.h"
#import <objc/message.h> static const char *key = "name"; @implementation NSObject (Property) - (NSString *)name {
return objc_getAssociatedObject(self, key);
} - (void)setName:(NSString *)name {
objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

  代码调用如下:

    //给分类动态添加属性(成员变量)
NSObject *objc = [[NSObject alloc] init];
objc.name = @"珠珠测试";
NSLog(@"-----%@-----", objc.name); //给对象动态添加关联对象
objc_setAssociatedObject(objc, @"keyOne", @"keyValueOne", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(objc, @"keyTwo", @"keyValueTwo", OBJC_ASSOCIATION_RETAIN_NONATOMIC); NSLog(@"-----object keyOne = %@-----", objc_getAssociatedObject(objc, @"keyOne"));
NSLog(@"-----object keyTwo = %@-----", objc_getAssociatedObject(objc, @"keyTwo"));

    

 

OC-runtime 的温习的更多相关文章

  1. OC - runtime 之关联对象

    header{font-size:1em;padding-top:1.5em;padding-bottom:1.5em} .markdown-body{overflow:hidden} .markdo ...

  2. OC Runtime

    OC 是面向运行时的语言.Runtime就是系统在运行的时候的一些机制,其中最主要的是消息发送机制.OC语言与其他语言(如C语言)在函数(方法)的调用有很大的不同.C语言,函数的调用在编译的时候就已经 ...

  3. iOS - OC RunTime 运行时

    1.运行时的使用 向分类中添加属性 // 包含运行时头文件 #import <objc/runtime.h> /* void objc_setAssociatedObject(id obj ...

  4. oc - runtime运行机制

      Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时做的事放到了运行时来处理.同时OC也是一门简单的语言,很大一部分是C的内容,只是在语言层面上加了关键字和语法,真正让OC强大 ...

  5. 18 (OC)* RunTime

    目录: 一.怎么理解OC是动态语言,Runtime又是什么?二.理解消息机制的基本原理三.与Runtime交互的三种方式四.分析Runtime中的数据结构五.深入理解Rutime消息发送原理六.多继承 ...

  6. OC - runtime - 1

  7. OC 相关

    1.OC runtime的理解[转载] http://www.csdn.net/article/2015-07-06/2825133-objective-c-runtime/1

  8. iOS RunTime运行时(1):类与对象

    Objective-C语言是一门动态语言,他将很多静态语言在编译和链接期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码更具有灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一下 ...

  9. iOS运行时Runtime浅析

    运行时是iOS中一个很重要的概念,iOS运行过程中都会被转化为runtime的C代码执行.例如[target doSomething];会被转化成objc)msgSend(target,@select ...

  10. Objective-C的对象模型和runtime机制

    内容列表 对象模型(结构定义,类对象.元类和实例对象的关系) 消息传递和转发机制 runtime系统功能理解 对象模型 结构定义 对象(Object): OC中基本构造单元 (building blo ...

随机推荐

  1. (转)Spring管理的Bean的生命周期

    http://blog.csdn.net/yerenyuan_pku/article/details/52834011 bean的初始化时机 前面讲解了Spring容器管理的bean的作用域.接着我们 ...

  2. 解决Homestead yarn , npm run dev, 命令报错问题!

    解决Homestead yarn , npm run dev, 命令报错问题! 2018年06月01日 11:50:51 偶尔发发颠 阅读数:1654    版权声明:本文为博主原创,未经博主同意,不 ...

  3. 初次改app

    没有学过安卓,突然需要改app里的一个bug,一个没搞过安卓的人要怎么入手去改这个安卓的代码呢?下面看看我入手的步骤 首先,页面上有几个文字“曲线分析”,那么在项目里找到这个文字,就可以找到这个页面的 ...

  4. SQL分组聚合查询练习(SQL Server和Oracle相似)20190514

    先建表 CREATE TABLE [dbo].[orderdt_jimmy]( ,) NOT NULL, [order_nid] [int] NOT NULL, ) NOT NULL, [qty] [ ...

  5. 搜索 || DFS || UOJ 146 信息传递

    DFS+回溯 找最小环 每个人知道自己的生日,每次把自己知道的生日告诉固定的一个人,问最少多少次之后能从别人口中听到自己的生日 找一个最小环 #include <iostream> #in ...

  6. log4j.xml 精选的log4j.xml文档,比较详细,网上的版本很多,这个版本相对而言比较完整

    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE log4j:configuration PUB ...

  7. 数据库课程设计 PHP web实现

    纪念一下自己写的东西.. 都说很垃圾就是了 直接用XAMPP做的 菜鸟网上学的PHP和HTML <!DOCTYPE html> <html> <head> < ...

  8. 弹跳加载动画特效Bouncing loader

    一款非常常用的css 加载动画,这款CSS3 Loading动画主要由几个小球通过规律的上下跳动,渐隐渐显而成,效果十分生动.流畅.兼容IE8以上,尤其适合在移动端中使用,基本代替了图片实现加载的效果 ...

  9. EPT和VPID简介

    EPT(Extended Page Tables,扩展页表),属于Intel的第二代硬件虚拟化技术,它是针对内存管理单元(MMU)的虚拟化扩展.EPT降低了内存虚拟化的难度(与影子页表相比),也提升了 ...

  10. nginx 配置虚拟主机访问PHP文件 502错误的解决方法

    最近配置Nginx 服务器虚拟主机 访问目录发现报502错误 百度了很多方法 都不管用  我擦 各种抓狂----- 原本Nginx配置如下: 网上找了很多方法: 查看日志   借助nginx的错误日志 ...