Objective-C Runtime的基本使用(iOS Runtime的初体验)
来源:互联网 发布:照片原图ps淘宝详情页 编辑:程序博客网 时间:2024/06/18 06:36
一、Runtime前言
最近研究Runtime,基础不够好,研究好久了,才了解一些些,知道个大概,这里做一个笔记。OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类的对象相应的方法。利用runtime机制让我们可以在程序运行时动态修改类,对象中的所有属性,方法,就算是私有方法以及私有属性都可以动态的修改。所以我所理解的就是 动态创建类,修改类,访问私有方法等一些基本特性,应该说理解runtime的基本用法吧!
二、Runtime简介
Runtime简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制,runtime是一套比较底层的纯C语言API,属于一个C语言库,我们平时写的OC代码中,程序在运行过程时,其实最终都是转成了runtime的C语言代码,runtime算是OC的幕后工作者。比如 OC: [[Person alloc] init]; runtime: objc_msgSend(objc_msgSden(“Peroson”, “allo”), “init”) runtime是开源库,在这里可以查看苹果官网的开源的源代码
三、Runtime用在什么场景
1.在程序运行过程中,动态创建一个类(比如KVO的底层实现)
2.在程序运行过程中,动态地为某个类添加属性\方法,修改属性值\方法
3.遍历一个类的所有成员变量(属性)\所有方法
4.交换方法实现
5.动态创建类
四、Runtime 术语
我们知道 [Person message]; 转换成 objc_msgSend(Persong, @selelct(message)); 它本身是这样的id obje_msgSend(id self, SEL op, …);1.id objc_msgSend第一个参数类型id,它是一个指向类实例的指针:typedef struct objc_object *id (objc_object是什么 查看第4点)2.SEL objc_msgSend函数的第二个参数类型SEL, 它是selector在Objc中的表示类型。selector是方法选择器,可以理解为区分方法的ID3. … 是参数4.struct objc_object {Class isa; };objc_object 结构体包含一个 isa 指针,根据isa指针就可以找到对象所属的类。 (可以查看源代码 )4.Class isa是指针是因为Class其实是一个指向objc_class结构体的指针
typedef struct objc_class *Class
struct objc_class { Class isa OBJC_ISA_AVAILABILITY;#if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;#endif} OBJC2_UNAVAILABLE;
这边包含了 超类指针, 类名, 成员变量, 方法,缓存, 还有附属的协议Method:是一种代表类中的某个方法的类型Ivar: 是一种代表类中实例变量的类型IMP:这个函数指针就是指向这个方法的实现Cache:缓存相关函数objc_msgSend : 给对象发送消息class_copyMethodList : 遍历某个类所有的方法class_copyIvarList : 遍历某个类所有的成员变量必备常识 1> Ivar : 成员变量 2> Method : 成员方法
五、Runtime 场景举例
每个按钮对应一个操作
首先创建一个Person类
Person.h文件
//// Person.h// RuntimeTestDemo//// Created by GongHui_YJ on 16/6/2.// Copyright © 2016年 YangJian. All rights reserved.//#import <Foundation/Foundation.h>@interface Person : NSObject@property (strong, nonatomic) NSString *name;@property (strong, nonatomic) NSString *address;- (void)eat;- (void)test1;- (void)test2;@end
Person.m文件
//// Person.m// RuntimeTestDemo//// Created by GongHui_YJ on 16/6/2.// Copyright © 2016年 YangJian. All rights reserved.//#import "Person.h"@implementation Person{ int age; NSString *sex;}- (instancetype)init{ self = [super init]; if (self) { _name = @"张三"; _address = @"浙江省杭州市"; age = 18; sex = @"男"; } return self;}- (void)eat{ NSLog(@"吃饭");}- (void)test1{ NSLog(@"我是test1方法");}- (void)test2{ NSLog(@"我是test2方法");}- (NSString *)description{ return [NSString stringWithFormat:@"name: %@ -- age: %i -- sex:%@ -- %@", _name, age, sex, _address];}@end
在ViewControll.m实现如下
1.获取属性\成员变量列表 使用 class_copyIvaeList函数
/** * 获取属性/成员 * * @param sender sender */- (IBAction)getProperty:(id)sender { Class classPerson = NSClassFromString(@"Person"); NSLog(@"--------------获取所有成员变量列表打印结果如下-----------------"); // 获取所有成员变量列表 使用 class_copyIvarList unsigned int count = 0; // Ivar *ivarList = class_copyIvarList(classPerson, &count); // 获取所有的成员变量列表 count 记录变量的数量 for (int i = 0; i < count; i++) { Ivar ivar = ivarList[i]; // 取出第i个位置的成员变量 const char *perosonName = ivar_getName(ivar); //获取变量名 const char *perosonType = ivar_getTypeEncoding(ivar); // 获取变量编码类型 NSLog(@"%s ---- %s\n", perosonName, perosonType); } NSLog(@"--------------分割线-----------------"); NSLog(@"--------------获取所有属性列表打印结果如下-----------------"); // 获取属性列表 使用 class_copyPropertyList unsigned int countProperty = 0; objc_property_t *propertyList = class_copyPropertyList(classPerson, &countProperty); for (int i = 0; i < countProperty; i++) { const char *cName = property_getName(propertyList[i]); const char *butes = property_getAttributes(propertyList[i]); NSLog(@"%s --- %s\n", cName, butes); } // 获取成员变量列表打印结果 (使用class_copyIvarList函数) /** 2016-06-02 19:26:16.522 RuntimeTestDemo[32706:3539390] --------------获取所有成员变量列表打印结果如下----------------- 2016-06-02 19:26:16.523 RuntimeTestDemo[32706:3539390] age ---- i 2016-06-02 19:26:16.523 RuntimeTestDemo[32706:3539390] sex ---- @"NSString" 2016-06-02 19:26:16.523 RuntimeTestDemo[32706:3539390] _name ---- @"NSString" 2016-06-02 19:26:16.523 RuntimeTestDemo[32706:3539390] _address ---- @"NSString" 2016-06-02 19:26:16.524 RuntimeTestDemo[32706:3539390] --------------分割线----------------- 2016-06-02 19:26:16.524 RuntimeTestDemo[32706:3539390] --------------获取所有属性列表打印结果如下----------------- 2016-06-02 19:26:16.524 RuntimeTestDemo[32706:3539390] name --- T@"NSString",&,N,V_name 2016-06-02 19:26:16.524 RuntimeTestDemo[32706:3539390] address --- T@"NSString",&,N,V_address */}
2.修改私有变量的值
/** * 修改私有变量的值 * * @param sender */- (IBAction)updatePrivateValue:(id)sender { Person *person = [[Person alloc] init]; NSLog(@"修改前数据: %@", [person description]); unsigned int count = 0; // Ivar *ivarList = class_copyIvarList([Person class], &count); for (int i = 0; i < count; i++) { Ivar var = ivarList[i]; if (i == 0) // 1 是表示私有变量 age 刚刚上面打印出来 第2个 { object_setIvar(person, var, @28); // 把私有变量age的值改成 28 } if (i == 1) // 私有变量 sex { object_setIvar(person, var, @"女"); } } NSLog(@"修改后数据---: %@", [person description]); /** 打印结果 发现 私有变量 age 和 sex 的值已经改变 2016-06-02 19:48:32.218 RuntimeTestDemo[33349:3554863] 修改前数据: name: 张三 -- age: 18 -- sex:男 -- 浙江省杭州市 2016-06-02 19:48:32.218 RuntimeTestDemo[33349:3554863] 修改后数据---: name: 张三 -- age: 450 -- sex:女 -- 浙江省杭州市 */}
3.获取类的所有方法
/** * 获取类的所有方法(包括私有) * * @param sender sender */ - (IBAction)getAllMethod:(id)sender { unsigned int count = 0; Method *memberFuncs = class_copyMethodList([Person class], &count); // 获取所有方法名 for (int i = 0; i < count; i++) { SEL name = method_getName(memberFuncs[i]); const char *nameMethod = sel_getName(name); // 获取方法名 NSLog(@"%s", nameMethod); } /** 获取所有.m 文件的所有方法 其中包括属性的get set方法.cxx_destruct 系统的 2016-06-02 19:55:26.411 RuntimeTestDemo[33544:3558588] eat 2016-06-02 19:55:26.412 RuntimeTestDemo[33544:3558588] address 2016-06-02 19:55:26.412 RuntimeTestDemo[33544:3558588] .cxx_destruct 2016-06-02 19:55:26.412 RuntimeTestDemo[33544:3558588] description 2016-06-02 19:55:26.412 RuntimeTestDemo[33544:3558588] name 2016-06-02 19:55:26.413 RuntimeTestDemo[33544:3558588] setName: 2016-06-02 19:55:26.413 RuntimeTestDemo[33544:3558588] init 2016-06-02 19:55:26.413 RuntimeTestDemo[33544:3558588] setAddress: */ }
4.动态添加方法
/** * 动态添加方法 * * @param sender sender */- (IBAction)addMethod:(id)sender {// class_addMethod函数参数的含义:// 第一个参数Class cls, 类型// 第二个参数SEL name, 被解析的方法// 第三个参数 IMP imp, 指定的实现 这里表示具体的实现方法 myTestMethod// 第四个参数const char *types,方法的类型(方法的参数) 0代表没有参数// const char *cs = getprogname(); class_addMethod([Person class], @selector(NewMethod::), (IMP)myTestMethod, "i@:i@"); // 这里会报警告 可以忽略 //调用方法 【如果使用[per method]方法!(在ARC下会报no visible @interface 错误)】 [person1 performSelector:@selector(NewMethod::)];}// 具体的实现, 即IMP所指向的方法int myTestMethod(id self, SEL _cmd, int var1, NSString *str){ NSLog(@"已经新增方法"); return var1;}/**打印结果 打印如下 说明已经添加成功 2016-06-03 11:00:58.040 RuntimeTestDemo[34732:3750980] 已经新增方法 *//** 此刻再打印获取类的所有方法 NewMethod:: 这个新增的方法已经添加进来 2016-06-03 11:00:59.185 RuntimeTestDemo[34732:3750980] NewMethod:: 2016-06-03 11:00:59.185 RuntimeTestDemo[34732:3750980] eat 2016-06-03 11:00:59.185 RuntimeTestDemo[34732:3750980] address 2016-06-03 11:00:59.185 RuntimeTestDemo[34732:3750980] .cxx_destruct 2016-06-03 11:00:59.185 RuntimeTestDemo[34732:3750980] description 2016-06-03 11:00:59.185 RuntimeTestDemo[34732:3750980] name 2016-06-03 11:00:59.186 RuntimeTestDemo[34732:3750980] setName: 2016-06-03 11:00:59.186 RuntimeTestDemo[34732:3750980] init 2016-06-03 11:00:59.186 RuntimeTestDemo[34732:3750980] setAddress: */
5.交换方法实现
/** * 方法交换 * * @param sender sender */- (IBAction)methodExchange:(id)sender { [person1 test1]; // 未交换前的输出结果 Method method1 = class_getInstanceMethod([person1 class], @selector(test1)); Method method2 = class_getInstanceMethod([person1 class], @selector(test2)); // 方法交换 method_exchangeImplementations(method1, method2); [person1 test1]; // 输出交换后的结果 /** 打印结果 交换完之后 test1的方法 打印test2的结果 2016-06-03 11:27:57.921 RuntimeTestDemo[35341:3767704] 我是test1方法 2016-06-03 11:27:57.922 RuntimeTestDemo[35341:3767704] 我是test2方法 */}
6.动态创建一个类
/** * 动态添加类 * * @param sender */- (IBAction)addClass:(id)sender { // 添加一个Student类 Class classStudent = objc_allocateClassPair([Person class], "Student", 0); // 添加一个NSStrig的变量 if (class_addIvar(classStudent, "schoolName", sizeof(NSString *), 0, "@")) { NSLog(@"添加成员变量 schollName成功"); } // 为Student类添加方法 if (class_addMethod(classStudent, @selector(printSchool), (IMP)printSchool, "v@:")) { NSLog(@"添加方法printSchool成功"); } // 注册这个类到runtime系统中 可以使用他 objc_registerClassPair(classStudent); // 返回void // 创建类 id student = [[classStudent alloc] init]; NSString *schoolName = @"福建师范大学"; // 给刚刚添加的变量赋值 [student setValue:schoolName forKey:@"schoolName"]; // 动态调用 [student performSelector:@selector(printSchool) withObject:nil]; /** 打印结果 2016-06-03 11:49:26.724 RuntimeTestDemo[35846:3781380] 添加成员变量 schollName成功 2016-06-03 11:49:26.725 RuntimeTestDemo[35846:3781380] 添加方法printSchool成功 2016-06-03 11:49:26.725 RuntimeTestDemo[35846:3781380] 我的学校是福建师范大学 */}//方法的实现void printSchool(id self, SEL _cmd){ NSLog(@"我的学校是%@", [self valueForKey:@"schoolName"]);}
六、Runtime使用心得
Runtime很强大,这里我只是初体验,对于很多的东西还不懂,不理解,算是一个初步的了解吧,应该算了解了runtime的基本用法,用来确实很爽。要理解透彻,完全的应用到实际项目中,还需努力。对新人了解应该有帮助,首先知道runtime是什么,runtime的基本使用,再慢慢挖掘,逐步吃透。博客中的demo我已经上传,需要的可以下载运行看下打印的日志。如果不足还望指出。
七、demo地址和参考博客,感言致谢
demo地址:http://download.csdn.net/detail/yj229201093/9540125 这里感谢两位大神的博客,受益良多。 主要参考链接(当然网络上还有很多好的博客都学习了O(∩_∩)O哈哈~) 1.http://www.bkjia.com/IOSjc/1012702.html2.http://yulingtianxia.com/blog/2014/11/05/objective-c-runtime/
- Objective-C Runtime的基本使用(iOS Runtime的初体验)
- ios学习--Objective-C runtime的使用
- Objective-C的Runtime
- Objective-C 的 Runtime
- Objective C的runtime
- Objective-C的runtime详解
- Objective-C的Runtime特性
- Objective-C Runtime的数据类型
- Objective-C的runtime原理
- iOS Runtime的使用
- ios的runtime使用
- iOS Runtime 初体验
- Objective-C 的 Runtime 与 Java 的 Runtime
- Objective-C runtime之运行时的基本特点(一)
- Objective-C runtime之运行时的基本特点(一)
- Objective-C runtime之运行时的基本特点(一)
- Objective-C runtime之运行时的基本特点(一)
- Objective-C runtime之运行时的基本特点(一)
- Nginx中502和504错误详解
- 我的算法学习之路
- C#指定下标position读取文本,并返回正确position;
- ABAP CL_DEMO_OUTPUT类
- 初学JavaWeb编程过程中遇到的一些问题记录
- Objective-C Runtime的基本使用(iOS Runtime的初体验)
- 定时任务
- iOS修改系统原声的返回按钮的点击事件
- mvc 从后台得到前台元素值的方法
- mybatis配置文件映射文件等基本元素
- 数据库空值陷阱
- Unity UGUI--Text组件
- PVP:手游进程的终点
- 合并果子(优先队列)