Runtime -----那些被忽略的技能
有人说现在的程序员都被惯坏了,尤其使用一些面向对象的语言开发的时候,只是简单的调用一些系统封装好的接口或者是调用一些“便利的”第三方,对于一个程序的真正实现有了解吗???又有多少了解呢???就单单的拿Objective-c 来说,确实感觉它无所不能,神马都可以做!大到整个工程文件的整合,内存的自动管理,小到图层动画的应用,界面的切换,使用几个函数就能完成,然而它的内部实现,具体如何管理的就不是那么清晰明了了。所以有时候感觉自己能用动画实现很炫酷的效果,然而主要的还是对函数的使用,对对象的操作。这也是面向对象的优势,但对程序原来讲,了解一个程序内部的真正实现还是挺有意思的比如说OC中的代理,OC中没有多继承,那通过代理实现多继承,它是怎么实现的呢?在OC 中就有这么一个特性 Runtime,它有时候像空气一样无时无刻不在,但要懂得如何运用了就可以在这个充满雾霾的空气中呼吸新鲜的空气了~但你也要运用得当,否则会氧中毒的~~
Runtime在OC中是一种运行时库在Objective-C 1.0的时候主要是使用C和汇编写的库,在Objective-C 2.0的时候使用的是C++对其进行的实现一般我们在实现的时候用到Runtime最多的也就是通过遍历类中的所有的实例变量,实现自动的归档---反归档好了,直接上代码:
Ivar *ivars = class_copyIvarList([self class], &count); // 反归档时调用
-(instancetype)initWithCoder:(NSCoder *)aDecoder { // self = [super init]; self = [self init]; if (self) {
// 若直接采用一般的方法 :----- 属性赋值
//
// self.name = [aDecoder decodeObjectForKey:@"name"];
// self.gender = [aDecoder decodeObjectForKey:@"gender"];
// self.age = [aDecoder decodeIntegerForKey:@"age"];
// self.hobby = [aDecoder decodeObjectForKey:@"hobby"];
// self.nickName = [aDecoder decodeObjectForKey:@"nickName"]; // 使用 Runtime 进行反归档处理
unsigned int ivarCount = 0; Ivar * ivarArray = class_copyIvarList([self class], &ivarCount); for (int i = 0; i < ivarCount; i++) {
NSString * varName =[NSString stringWithUTF8String:ivar_getName(ivarArray[i])]; id value = [aDecoder decodeObjectForKey:varName];
[self setValue:value forKey:varName]; }
free(ivarArray); }
return self; } // 归档时调用
-(void)encodeWithCoder:(NSCoder *)aCoder
{ // [aCoder encodeObject:self.name forKey:@"name"];
//
// [aCoder encodeObject:self.gender forKey:@"gender"];
//
// [aCoder encodeInteger:self.age forKey:@"age"];
//
// [aCoder encodeObject:self.hobby forKey:@"hobby"];
//
// [aCoder encodeObject:self.nickName forKey:@"nickName"]; unsigned int ivarCount = 0; Ivar * ivarArray = class_copyIvarList([self class], &ivarCount); // 使用KVC进行取值在归档encode for (int i = 0; i < ivarCount; i++) {
// C 语言的字符串转换成NSString NSString * varName = [NSString stringWithUTF8String:ivar_getName(ivarArray[i])]; // 使用KVC 取值 再编码 id value =[self valueForKey:varName];
// 归档
[aCoder encodeObject:value forKey:varName]; } } // 对P2进行归档
NSData * p2Data = [NSKeyedArchiver archivedDataWithRootObject:p2]; // NSLog(@"%@",p2Data); // 进行反归档 转换成对象 Person * p3 = [NSKeyedUnarchiver unarchiveObjectWithData:p2Data]; // NSLog(@"%@",p3.name);
RunTime 的归档反归档适用于归档对象属性较多的情况,可以减少代码量,逻辑更清晰。这种方式一般来说用的比较多,除此之外一些基本的使用方法也是比较常用的,比如说:
//使用运行时需要导入头文件 #import <objc/objc-runtime.h>
//或者直接引入: #include <objc/runtime.h> #include <objc/message.h> Method methodA = class_getInstanceMethod([self class], @selector(testA)); Method methodB = class_getInstanceMethod([self class], @selector(testB)); // 交换AB的方法实现 method_exchangeImplementations(methodA, methodB);
当然在Xcode在默认是不使用Runtime的需要调整一个地方:如下图:
这样就可以,还有获取某个类中的所有实例变量链表:
// 实例变量的个数 unsigned int ivarCount = 0; Ivar * ivaArray = class_copyIvarList([类名 class], &ivarCount); for (int i = 0; i < ivarCount; i++) { Ivar var = ivaArray[i]; // 输出实例变量的名称和类型 NSLog(@"%s,%s",ivar_getName(var),ivar_getTypeEncoding(var)); }// 释放指针
free(ivaArray);
获取某个类中的所有的实例方法:
unsigned int methodCount = 0; Method * methodArray = class_copyMethodList([Person class], &methodCount); for (int i = 0; i < methodCount; i++) { Method m = methodArray [i]; // 输出方法的名字和方法的类型编码
NSLog(@"%s,%s",sel_getName(method_getName(m)),method_getTypeEncoding(m)); }
其实Runtime的消息驱动机制是和常见的,除此之外还有动态绑定,具体的动态绑定有两个重要的方法,代码写的很详细:
// 动态绑定 +(BOOL)resolveClassMethod:(SEL)sel { // 输出 方法名 和所在的行数
NSLog(@"%s,%d",__FUNCTION__,__LINE__); // 定义block添加类方法 void (^resolveClassBlock)(id,SEL) = ^(id receiver,SEL objc_cmd){ NSLog(@"%s,类的方法未实现,执行此段",sel_getName(objc_cmd)); }; if (sel == @selector(heiheihei)) { // 调用block IMP blockIMP = imp_implementationWithBlock(resolveClassBlock); // 添加一个类方法
class_addMethod(object_getClass([self class]), sel, blockIMP, "v@:");
} return [super resolveClassMethod:sel]; } // 动态绑定
+(BOOL)resolveInstanceMethod:(SEL)sel { NSLog(@"%s,%d",__FUNCTION__,__LINE__); // 定义block添加实例方法 // 接受者 选标
void(^resolveBlock)(id , SEL) = ^(id receiver, SEL objc_cmd) { NSLog(@"%s,实例方法未实现,会执行此处代码。",sel_getName(objc_cmd)); }; if (sel == @selector(aaacccddd)) {
// 获取实现的bolck的地址
IMP blockIMP = imp_implementationWithBlock(resolveBlock); // 第四个参数函数的类型
class_addMethod([self class], sel, blockIMP, "v@:"); } return [super resolveInstanceMethod:sel]; }
那么到了最后的重头戏了, 在Objective-C程序中模拟多重继承就是通过消息转发机制实现的,使得一个对象通过转发来响应消息,看起来就象该对象从别的类那借来了或者”继承“了方法实现一样:具体的需要实现这两种方法进行消息的转发:
-methodSignatureForSelector:
-forwardInvocation: 具体代码如下:
//得到方法的签名
-(NSMethodSignature * )methodSignatureForSelector:(SEL)aSelector { NSMethodSignature * sig = [super methodSignatureForSelector:aSelector]; if (!sig) { WS * ws = [[WS alloc]init]; sig = [ws methodSignatureForSelector:aSelector];
}
return sig; } -(void)forwardInvocation:(NSInvocation *)anInvocation { // 方法自动执行 对消息进行转发 // 如果 本类调用 study 时 则要对方法进行转发
if (anInvocation.selector == @selector(study)) { [anInvocation invokeWithTarget:[WS new]]; } } [p2 performSelector:@selector(study)];
其实这就是Objective-C中实现代理的原理,通过这些简单的实现可以在进行相应的优化,可以开辟子线程对消息进行转发等优化方法;
最后在分享一个好玩的小技巧:
更改系统的状态栏如下:
只需在Appdelegate中作如下处理:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UIApplication *thisApp = [UIApplication sharedApplication];
Ivar statusVar = class_getInstanceVariable([thisApp class], "_statusBar");
id statusBar = object_getIvar(thisApp, statusVar);
Ivar statusBackViewVar = class_getInstanceVariable([statusBar class], "_foregroundView");
id statusBackView = object_getIvar(statusBar, statusBackViewVar);
NSArray *viewArray = [statusBackView subviews]; UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 3, 43, 15)];
imageView.backgroundColor = [UIColor whiteColor];
imageView.image = [UIImage imageNamed:@"替换成自己喜欢的图片"];
[viewArray[0] addSubview:imageView];
return YES;
}
这样自己的个性化状态栏就有了~~~~
欢迎转载,请注明转载和原文出处:http://www.cnblogs.com/windsSunShine/
Runtime -----那些被忽略的技能的更多相关文章
- 你get了无数技能,为什么一事无成
前几日看到阮一峰老师的发的一句话,颇有感慨,「你只是坐在电脑前,往网上发表了一段文字或者一张图片,随便什么,就能够接触到多少陌生的灵魂.这就是我热爱互联网的原因」.我打心底认为这是一个最好的时代, ...
- 你 get 了无数技能,为什么一事无成
前 几日看到阮一峰老师的发的一句话,颇有感慨,「你只是坐在电脑前,往网上发表了一段文字或者一张图片,随便什么,就能够接触到多少陌生的灵魂.这就是我热 爱互联网的原因」.我打心底认为这是一个最好的时代, ...
- 专访笨叔叔:2019年可能是Linux年?(转)
链接:https://zhuanlan.zhihu.com/p/57815479 2017年9月<奔跑吧 Linux内核>一书出版后得到了广大Linux从业人员和爱好者(特别是从事Linu ...
- 基于swoole+Redis的消息实时推送通知
swoole+Redis将实时数据的推送 一 实现功能 设计师订单如果设计师未抢单,超时(5分钟)设计订单时时给设计师派送, 设计师公众号中收到派单信息 设计发布者收到派单成功信息 环境 centos ...
- 深入理解xLua基于IL代码注入的热更新原理
目前大部分手游都会采用热更新来解决应用商店审核周期长,无法满足快节奏迭代的问题.另外热更新能够有效降低版本升级所需的资源大小,节省玩家的时间和流量,这也使其成为移动游戏的主流更新方式之一. 热更新可以 ...
- iOS 开发-- Runtime 1小时入门教程
1小时让你知道什么是Objective-C Runtime,并对它有一定的基本了解,可以在开发过程中运用自如. 三.Objective-C Runtime到底是什么东西? 简而言之,Objective ...
- 企业必会技能 tomcat
企业必会技能 tomcat tomcat 一.什么是Tomcat? Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项 ...
- Runtime应用防止按钮连续点击 (转)
好久之前就看到过使用Runtime解决按钮的连续点击的问题,一直觉得没啥好记录的.刚好今天旁边同时碰到这个问题,看他们好捉急而且好像很难处理,于是我先自己看看… 前面自己也学习了很多Runtime的东 ...
- 自定义注解之运行时注解(RetentionPolicy.RUNTIME)
对注解概念不了解的可以先看这个:Java注解基础概念总结 前面有提到注解按生命周期来划分可分为3类: 1.RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成clas ...
随机推荐
- 在Linux 应用层 基于i2c-dev.h 实现i2c读写
/* i2c-dev.h - i2c-bus driver, char device interface Copyright (C) 1995-97 Simon G. Vogl Copyright ( ...
- Oracle数据库入门——如何根据物化视图日志快速刷新物化视图
Oracle物化视图的快速刷新机制是通过物化视图日志完成的.Oracle如何通过一个物化视图日志就可以支持多个物化视图的快速刷新呢,本文简单的描述一下刷新的原理. 首先,看一下物化视图的结构:SQL& ...
- POJ 3519 Minimal Backgammon
Minimal Backgammon Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 1195 Accepted: 700 ...
- Redis配置以及通过C#访问小试
首先安装一个Ubuntu14.04的虚拟机用来安装Redis.Ubuntu的Unity在虚拟机里面卡爆了,可以通过如下方法安装传统的Gnome界面: sudo aptitude install gno ...
- 简单设置 navgationbar(导航栏) 的 title 字体跟颜色
NSDictionary *navbarTitleTextAttributes = [NSDictionary dictionaryWithObjectsAndKeys: [UIColor white ...
- sql order by 排序多个字段
order by 多个字段,每个字段后面都有排序方式,默认ASC 例如:select table a order by a.time1 ,a.time2 desc,a.time3 asc
- Robot Framework自动化测试(五)--- 开发系统关键字
最近一直在用robot framework 做自动化测试项目,老实说对于习惯直接使用python的情况下,被框在这个工具里各种不爽,当然,使用工具的好处也很多,降低了使用成本与难度:当然,在享受工具带 ...
- 15套漂亮的 PSD 格式的图标,不一样的视觉效果
在 Web 设计领域,图标发挥非常重要的作用,因为美丽的和创造性的图标集可以很容易地使网页设计更有吸引力.网页设计设计师专注于每一个领域的设计,包括颜色选择.图标.创造力.混色等.正确的选择图标可以使 ...
- LeetCode——LRU Cache
Description: Design and implement a data structure for Least Recently Used (LRU) cache. It should su ...
- 配置内存中OLTP文件组提高性能
在今天的文章里,我想谈下使用内存中OLTP的内存优化文件组来获得持久性,还有如何配置它来获得高性能.在进入正题前,我想简单介绍下使用你数据库里这个特定文件组,内存OLTP是如何获得持久性的. 内存中O ...