Runtime 理解介绍的文章非常多,我只想讲讲Runtime 可以用在哪里,而我在项目里哪些地方用到了runtime。多以实际使用过程为主,来介绍runtime的使用。

* 那么runtime 怎么使用?可以用在哪些场景下呢?*

首先,使用runtime 相关API,要#import <objc/runtime.h>

运行时获取某个类的属性或函数

运行时动态获取某个类的属性或者函数等,可以用来做很多事情,如json 解析、数据库结果解析、判断某个类的子类等。

解析、转化为Model
// 获取属性列表
objc_property_t * class_copyPropertyList(Class cls, unsigned int *outCount)
// 获取属性名
const char *property_getName(objc_property_t property)
// 获取属性类型
const char *property_getAttributes(objc_property_t property)

以上方法可以用来:

* 解析json数据,转化为Model对象。

* 解析数据库查询结果,转化为Model 对象。

这里有动态获取类的属性的示例代码片段:

    unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
//获取属性名
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
//获取属性类型等参数
NSString *propertyType = [NSString stringWithCString: property_getAttributes(property) encoding:NSUTF8StringEncoding];
/*
各种符号对应类型,部分类型在新版SDK中有所变化,如long 和long long
c char C unsigned char
i int I unsigned int
l long L unsigned long
s short S unsigned short
d double D unsigned double
f float F unsigned float
q long long Q unsigned long long
B BOOL
@ 对象类型 //指针 对象类型 如NSString 是@“NSString”
propertyType,你可以打印出来,看看它是什么。
要判断某个属性的类型,只需要[propertyType hasPrefix:@"Ti"]
这代表它是int 类型。
*/
}
free(properties);
判断某个类的子类

有时候我们在程序中需要判断某个类是否是另一个类的子类。这个功能也可以利用runtime类实现,这里有示例代码:

    int numClasses;
Class *classes = NULL;
numClasses = objc_getClassList(NULL,0); if (numClasses >0 )
{
classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
if (class_getSuperclass(classes[i]) == [xxxxClass class]){
id class = classes[i];
// 执行某个方法 或者 做其他事情
[class performSelector:@selector(xxxxMethod) withObject:nil];
}
}
free(classes);
}

以上两段示例代码摘自我之前写的FMDB Model 封装:JKDBModel,你可以去看更详尽的解析和使用过程。

获取某个类的实例变量

如果你还需要获取某个类的实例变量做什么操作的话,可以使用如下这几个API:

// 获取实例变量数组
Ivar * class_copyIvarList(Class cls, unsigned int *outCount)
// 获取实例变量名称
const char * ivar_getName( Ivar ivar)
// 获取实例变量类型
const char * ivar_getTypeEncoding( Ivar ivar)

这面有获取实例变量的示例代码片段:

    unsigned int outCount, i;

    Ivar *ivaries = class_copyIvarList([Son class], &outCount);
for (i = 0; i < outCount; i++) {
Ivar ivar = ivaries[i];
NSString *ivarName = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding];
NSString *ivarType = [NSString stringWithCString:ivar_getTypeEncoding(ivar) encoding:NSUTF8StringEncoding];
NSLog(@"名称:%@---类型:%@",ivarName,ivarType);
/*
各种符号对应类型,部分类型在新版SDK中有所变化,如long 和long long
c char C unsigned char
i int I unsigned int
l long L unsigned long
s short S unsigned short
d double D unsigned double
f float F unsigned float
q long long Q unsigned long long
B BOOL
@ 对象类型 //指针 对象类型 如NSString 是@“NSString”
*/
}
free(ivaries);
获取某个类的方法

获取某个类的方法,会包含这个类的property 的set 和get 方法,但是不包括父类的property set 和get 方法,不包括父类的方法(如果在当前类覆写,就包括)。

主要API:

// 获取方法数组
Method * class_copyMethodList(Class cls, unsigned int *outCount)
// 获取方法的 SEL
SEL method_getName( Method method)
// 获取方法名
const char* sel_getName(SEL aSelector)

获取方法数组的示例代码片段:

    unsigned int outMethodCount, j;
Method *methods = class_copyMethodList([Son class], &outMethodCount);
for (j = 0; j < outMethodCount; j++) {
Method method = methods[j];
SEL selector = method_getName(method);
if (selector) {
NSString *methodName = [NSString stringWithCString:sel_getName(selector) encoding:NSUTF8StringEncoding];
NSLog(@"方法:%@",methodName);
}
}
free(methods);

运行时替换方法(Method Swizzling)

Method Swizzling 的使用需要谨慎,因为一不小心可能就会导致无法排查的Bug,毕竟它替换的是官方的API,有些API内部做了什么事情,很难完全把握。

使用场景,需要监控用户经常打开的界面,以及在某界面停留的时长。

我们可以怎么做呢?写一个UIViewController 的Category,然后在类别中,添加自定义的方法:如-xxxviewDidAppear:和-xxxviewDidDisappear:方法,然后在-load 方法中,用自定义的方法替换原来的方法。

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self); SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)); if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
} #pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"xxx_viewWillAppear: %@", self);
// 在这里,我们可以发送一个消息到服务器,或者做其他事情等。
}

以上示例代码摘自:Objective-C Runtime 运行时之四:Method Swizzling

关于Method Swizzling,他是把两个方法的实现部分互换了。

比如上面我们调用-xxx_viewWillAppear:,因为-xxx_viewWillAppear: 和-viewWillAppear:的实现部分互换后,其实执行的时候,并不会执行上面的这个实现,而是调用-viewWillAppear:的内部实现。所以上面的代码,完全不会产生循环调用。

还是写段代码说明吧:

- (void)viewWillAppear:(BOOL)animated {
NSLog(@"这是原来的方法");
} - (void)xxx_viewWillAppear:(BOOL)animated {
NSLog(@"xxx_viewWillAppear: %@", self);
// 在这里,我们可以发送一个消息到服务器,或者做其他事情等。
}

假如上面这俩方法用method swizzling 替换后,我们调用-xxx_viewWillAppear:会打印这是原来的方法;而调用-viewWillAppear:会打印xxx_viewWillAppear:。这里需要细细体会一下。

关于Method Swizzling更多的注意点请看原文Method Swizzling

对象关联(Associated Objects)

对象关联(或称为关联引用)本来是Objective-C 运行时的一个重要特性,它能让开发者对已经存在的类在扩展中添加自定义的属性

需要用的以下三个函数:

void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)

id objc_getAssociatedObject(id object, void *key)

void objc_removeAssociatedObjects(id object)

众所周知,OC 中的Category 中不能添加新的属性,但是我们通过Associated Objects可以间接的实现往类上添加自定义的属性。

不能添加属性的根本原因是不会帮我们自动添加对象的实例变量,也不会帮我们生成set 和get方法,虽然set /get 方法可以自己实现,但是没有实例变量来存储数据。



很容易看懂官方文档对参数的描述,但是key 需要注意一下:

通常推荐的做法是添加的属性最好是 static char类型的,当然更推荐是指针型的。通常来说该属性应该是常量、唯一的、在适用范围内用getter和setter访问到,所以通常我们这样写:

static char kAssociatedObjectKey;

objc_setAssociatedObject(self, &kAssociatedObjectKey, object, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(self, &kAssociatedObjectKey);

当然,对于key 还有更好的做法,那就是selector。用selector 的示例在下面。

下面用代码演示如何在Category中添加一个新的属性。

这是Son+AssociatedObject.h

#import "Son.h"

@interface Son (AssociatedObject)

/** 家庭住址 */
@property (copy, nonatomic) NSString *address;
/** 身高 */
@property (assign, nonatomic) int height; @end

这是Son+AssociatedObject.m

#import "Son+AssociatedObject.h"
#import <objc/runtime.h> @implementation Son (AssociatedObject) - (void)setAddress:(NSString *)address
{
objc_setAssociatedObject(self, @selector(address), address, OBJC_ASSOCIATION_COPY_NONATOMIC);
} - (NSString *)address
{
return objc_getAssociatedObject(self, @selector(addObject:));
} - (void)setHeight:(int)height
{
NSNumber *heighNum = [NSNumber numberWithInt:height];
objc_setAssociatedObject(self, @selector(height), heighNum, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} - (int)height
{
NSNumber *heightNum = objc_getAssociatedObject(self, @selector(addObject:));
return heightNum.intValue;
} @end

虽然上面有提到void objc_removeAssociatedObjects(id object),但是不要轻易使用这个函数,因为它会移除所有的关联对象。我们一般要移除某个关联对象,只需要用objc_setAssociatedObject传入nil即可。

补充一个关联对象的使用场景:

你在使用AlertView 或者ActionSheet的时候,有没有很苦恼不能在点击的代理方法中方便的获取到Model对象呢?

除了在控制器中添加一个property 这种方式外;

我们也可以为AlertView 或者ActionSheet 添加一个关联对象,这样就可以在代理方法中方便的获取到Model 对象啦。

这里如果我们为AlertView 或者ActionSheet 添加Category来实现的话,代码跟上面为Son 添加类别基本一样,对象类型改为id 类型即可。

或者我们在控制器中调用的时候,添加关联对象也可以。这时候就用这种方式:

static char kAssociatedObjectKey;
objc_setAssociatedObject(self, &kAssociatedObjectKey, object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(self, &kAssociatedObjectKey);

UIAlertController 也跟上面一样。

关于Associated Objects的使用,有连个为Category扩展功能,使得Category中也能方便的添加属性以及相应的getter 和setter 的例子。

OC 自动生成分类属性方法

一个库–DProperty

运行时动态创建一个类

我再某控制器中测试写了这么一个方法,来创建一个MyClass 类。项目中并不存在叫MyClass 的类文件。

- (void)createClass
{
Class MyClass = objc_allocateClassPair([NSObject class], "MyClass", 0);
// 1.添加一个叫name 类型为NSString的实例变量,第四个参数是对其方式,第五个参数是参数类型
if (class_addIvar(MyClass, "name", sizeof(NSString *), 0, "@")) {
NSLog(@"add name ivar success");
}
// 2.添加一个property
// 这里需要注意,添加property之前需要先添加一个与之对应的实例变量
if (class_addIvar(MyClass, "_address", sizeof(NSString *), 0, "@")) {
NSLog(@"add _address ivar success");
} objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t backingivar = { "V", "_address"};
objc_property_attribute_t attrs[] = {type, ownership, backingivar};
class_addProperty(MyClass, "address", attrs,2); // 3.添加函数, myclasstest是已经实现的函数,"v@:"这种写法见参数类型连接
class_addMethod(MyClass, @selector(myclasstest:), (IMP)myclasstest, "v@:");
// 4.注册这个类到runtime系统中就可以使用他了
objc_registerClassPair(MyClass);
// 5.生成了一个实例化对象
id myobj = [[MyClass alloc] init];
NSString *str = @"名字";
// 6.给刚刚添加的变量赋值
// object_setInstanceVariable(myobj, "itest", (void *)&str);在ARC下不允许使用
[myobj setValue:str forKey:@"name"];
[myobj setValue:@"这是地址" forKey:@"address"];
// 7.调用myclasstest方法,也就是给myobj这个接受者发送myclasstest这个消息
[myobj myclasstest:10];
} //这个方法实际上没有被调用,但是必须实现否则不会调用下面的方法
- (void)myclasstest:(int)a
{
NSLog(@"啊啊啊啊啊");
}
//调用的是这个方法
static void myclasstest(id self, SEL _cmd, int a) //self和_cmd是必须的,在之后可以添加其他参数
{
Ivar v = class_getInstanceVariable([self class], "name");
//返回名为name的ivar的变量的值
id o = object_getIvar(self, v);
//成功打印出结果
NSLog(@"name is %@", o);
NSLog(@"参数 a is %d", a); objc_property_t property = class_getProperty([self class], "address");
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
id value = [self valueForKey:propertyName];
NSLog(@"address is %@", value);
}

关于运行时创建一个新类,上面的注释已经写的很详细了。

Have Fun!

Runtime系列(二)--Runtime的使用场景的更多相关文章

  1. Zookeeper系列(二)特征及应用场景

    zookeeper类似一个分布式的文件系统,每个节点可以有和它自身或它的子节点相关联的数据,此外指向节点的路劲必须使用绝对路径(不能使用相对路劲):   Znode 对应目录树中的的一个节点,并拥有一 ...

  2. Runtime简介以及常见的使用场景(此内容非原创,为转载内容)

    Runtime简称运行时,是一套比较底层的纯C语言的API, 作为OC的核心,运行时是一种面向对象的编程语言的运行环境,其中最主要的是消息机制,Objective-C 就是基于运行时的. 所谓运行时, ...

  3. Runtime常用的几个应用场景

    Runtime常见的几个应用场景. Runtime常见应用场景 具体应用拦截系统自带的方法调用(Method Swizzling黑魔法) 实现给分类增加属性 实现字典的模型和自动转换 JSPatch替 ...

  4. 微信公众号开发C#系列-11、生成带参数二维码应用场景

    1.概述 我们在微信公众号开发C#系列-7.消息管理-接收事件推送章节有对扫描带参数二维码事件的处理做了讲解.本篇主要讲解通过微信公众号开发平台提供的接口生成带参数的二维码及应用场景. 微信公众号平台 ...

  5. (转发)IOS高级开发~Runtime(二)

    一些公用类: @interface ClassCustomClass :NSObject{ NSString *varTest1; NSString *varTest2; NSString *varT ...

  6. CUDA运行时 Runtime(二)

    CUDA运行时 Runtime(二) 一. 概述 下面的代码示例是利用共享内存的矩阵乘法的实现.在这个实现中,每个线程块负责计算C的一个方子矩阵C sub,块内的每个线程负责计算Csub的一个元素.如 ...

  7. Docker系列之AspNetCore Runtime VS .NetCore Runtime VS .NET Core SDK(四)

    前言 接下来我们就要慢慢步入在.NET Core中使用Docker的殿堂了,在开始之前如题,我们需要搞清楚一些概念,要不然看到官方提供如下一系列镜像,我们会一脸懵逼,不知道到底要使用哪一个. AspN ...

  8. [知识库分享系列] 二、.NET(ASP.NET)

    最近时间又有了新的想法,当我用新的眼光在整理一些很老的知识库时,发现很多东西都已经过时,或者是很基础很零碎的知识点.如果分享出去大家不看倒好,更担心的是会误人子弟,但为了保证此系列的完整,还是选择分享 ...

  9. Newtonsoft.Json C# Json序列化和反序列化工具的使用、类型方法大全 C# 算法题系列(二) 各位相加、整数反转、回文数、罗马数字转整数 C# 算法题系列(一) 两数之和、无重复字符的最长子串 DateTime Tips c#发送邮件,可发送多个附件 MVC图片上传详解

    Newtonsoft.Json C# Json序列化和反序列化工具的使用.类型方法大全   Newtonsoft.Json Newtonsoft.Json 是.Net平台操作Json的工具,他的介绍就 ...

  10. 探索ASP.Net Core 3.0系列二:聊聊ASP.Net Core 3.0 中的Startup.cs

    原文:探索ASP.Net Core 3.0系列二:聊聊ASP.Net Core 3.0 中的Startup.cs 前言:.NET Core 3.0 SDK包含比以前版本更多的现成模板. 在本文中,我将 ...

随机推荐

  1. bzoj1127[POI2008]KUP 悬线法

    Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 485  Solved: 174[Submit][Status][D ...

  2. bzoj1433

    1433: [ZJOI2009]假期的宿舍 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3371  Solved: 1425[Submit][Sta ...

  3. rasa_core:基于机器学习的对话引擎

    用机器学习管理你的对话,让它提升每一个对话.Rasa Core引导对话,考虑对话的历史和外部环境. 而不是成千上万的规则,Rasa 从真正的对话中挑选模式. 现在是扔掉你的状态机的时候了! Manag ...

  4. Weekly Contest 75题解

    Q1. Rotate String(796) We are given two strings, A and B. A shift on A consists of taking string A a ...

  5. SQL之排序

    1.按多个列排序 经常需要按不止一个列进行数据排序.例如,如果要显示雇员名单,可能希望按姓和名排序(首先按姓排序,然后在每个姓中再按名排序).如果多个雇员有相同的姓,这样做很有用. 要按多个列排序,简 ...

  6. 89. Gray Code(中等,了解啥是 gray code)

    知道啥是 gray code 就是收获了. 下面介绍了 gray code 发明的 motivation, 了解动机后就知道啥是 gray code 了. https://zh.wikipedia.o ...

  7. JavaScript正则表达式模式匹配(2)——分组模式匹配

    var pattern=/google{4,8}$/; // {4,8}$表示匹配结尾4-8次 var str='googleeeeeeeee'; // 表示e的4-8次 alert(pattern. ...

  8. C++框架_之Qt的开始部分_概述_安装_创建项目_快捷键等一系列注意细节

    C++框架_之Qt的开始部分_概述_安装_创建项目_快捷键等一系列注意细节 1.Qt概述 1.1 什么是Qt Qt是一个跨平台的C++图形用户界面应用程序框架.它为应用程序开发者提供建立艺术级图形界面 ...

  9. ubuntu15.10 opencv3.1 安装配置codeblocks

    安装codeblocks: sudo add-apt-repository ppa:damien-moore/codeblocks-stable // 添加codeblocks的ppa sudo ap ...

  10. ACM Find them, Catch them

    The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TW ...