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 的归档反归档适用于归档对象属性较多的情况,可以减少代码量,逻辑更清晰。这种方式一般来说用的比较多,除此之外一些基本的使用方法也是比较常用的,比如说:

1、 动态的为某个类添加属性\方法, 修改属性值\方法

2 、遍历一个类的所有成员变量(属性)\所有方法  那么就这两种方式给出具体的代码实现 :

首先是   动态的为某个类添加属性\方法, 修改属性值\方法

//使用运行时需要导入头文件  #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, "[email protected]:");
    }

    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, "[email protected]:");

    }

    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/

 

时间: 2024-08-15 18:25:26

Runtime -----那些被忽略的技能的相关文章

你get了无数技能,为什么一事无成

前几日看到阮一峰老师的发的一句话,颇有感慨,「你只是坐在电脑前,往网上发表了一段文字或者一张图片,随便什么,就能够接触到多少陌生的灵魂.这就是我热爱互联网的原因」.我打心底认为这是一个最好的时代,这个时代,我们能接触的信息比历史上任何时候都多,我们通过互联网能够轻易的分享自己的喜悦,传播自己的思想,正如我此刻正在敲的这些文字. 今天并不是想吹嘘信息时代有多好,毕竟我们都还生活在墙里头呢.今天只是谈谈我的一些思考,处于信息大潮下的我们,该以怎样的视角来审视这个时代,在信息时代的物竞天择中,我们如何

理解 Objective-C Runtime

当人们初学 Cocoa/Objective-C 时,Objective-C Runtime 是被忽略的特性之一.原因是 Objective-C(这门语言)很容易在几小时内就熟悉,新学 Cocoa 的人花费他们大部分的时间学习 Cocoa 框架和适应它是如何工作的.然而每个人至少应该知道一些 runtime 的工作细节,需要比知道编译器会把 [target doMethodWith:var1];  转换为 objc_msgSend(target,@selector(doMethodWith:),v

工具乃思维的奴隶

争论乃思维碰撞,正面碰撞,可产生最大的力量促使思维扩展.而埋头乱撞,只会越撞越糊涂.事物之间的争议无非是人与人之间世界观的争议.你有你理,我有我理,同一理解层次的碰撞或许还能正面碰撞,不同层次的,永远只有徒劳而返,伤精又伤力(可参考被忽略的技能).争议也分理性派和偏激派,同派且同一水平面之间才能产生璀璨的火花.那怕偏激对偏激,斗个你死我活的,“知音”相遇,不失乐趣,未必坏事. 工具乃思维奴隶,对工具的理解范围乃人思维的边界.矛说无所不破,盾说无所不挡.绝对论之间的对决,需找个公平量化比较点,“答

如何在游戏客户端和服务器之间精确同步玩家的状态?

欢迎来到unity学习.unity培训.unity企业培训教育专区,这里有很多Unity3D资源.Unity3D培训视频.Unity3D教程.Unity3D常见问题.Unity3D项目源码,[狗刨学习网]unity极致学院,致力于打造业内unity3d培训.学习第一品牌. 假定技能有前摇,攻击,后摇3个阶段. 前摇阶段可以理解为发招前的酝酿或者念几句咒语,攻击阶段可以理解为开始挥刀砍直至砍中目标身上这个时间段,后摇阶段可以理解为收刀恢复攻击姿势的阶段. 我们的技能的连招系统允许在某些技能(称为技

基于swoole+Redis的消息实时推送通知

swoole+Redis将实时数据的推送 一 实现功能 设计师订单如果设计师未抢单,超时(5分钟)设计订单时时给设计师派送, 设计师公众号中收到派单信息 设计发布者收到派单成功信息 环境 centos6.10 redis-4.0.2 swoole-src-4.4.12 php-7.1.5 MYsyql5.7 在centos6默认是gcc-4.7,安装swoole的时候需要升级到gcc-4.8 二 实现流程 1.开启swoole server端监听 2.开启swoole client连接执行定时执

iOS 开发-- Runtime 1小时入门教程

1小时让你知道什么是Objective-C Runtime,并对它有一定的基本了解,可以在开发过程中运用自如. 三.Objective-C Runtime到底是什么东西? 简而言之,Objective-C Runtime是一个将C语言转化为面向对象语言的扩展. 我们将C++和Objective进行对比,虽然C++和Objective-C都是在C的基础上加入面向对象的特性扩充而成的程序设计语言,但二者实现 的机制差异很大.C++是基于静态类型,而Objective-C是基于动态运行时类型.也就是说

linux下SVN忽略文件/文件夹的方法

linux下SVN忽略文件/文件夹的方法 假设想忽略文件temp 1. cd到temp所在的目录下: 2. svn propedit svn:ignore . 注意:请别漏掉最后的点(.表示当前目录),如果报错请看下面 3. 打开的文件就是忽略列表文件了(默认是空的),每一行是一项,在该文件中输入temp,保存退出 4. svn st查看状态,temp的?状态已经消除了 如果在svn propedit svn:ignore .时报错:svn: None of the environment va

Photoshop技能167个经典的Photoshop技巧大全

Photoshop技能167个经典的Photoshop技巧大全 学PS基础:Photoshop 技能167个­ 经典的Photoshop技巧大全,如果你是初级阶段的水平,熟读此文并掌握,马上进阶为中级水平.绝对不是广告噢. ­ 1. 快速打开文件­ 双击Photoshop的背景空白处(默认为灰色显示区域)即可打开选择文件的浏览窗口.­ 2. 随意更换画布颜色­ 选择油漆桶工具并按住Shift点击画布边缘,即可设置画布底色为当前选择的前景色.如果要还原到默认的颜色,设置前景色为25%灰度 (R19

C run-time函数总览

Argument Access(参数访问):变长参数列表.这个模块提供了三个宏:va_arg.va_end和va_start,用来实现变长参数列表的访问. Buffer Manipulation(内存操作):按字节处理内存区域.主要函数:memcpy.memmove.memset等. Byte classification(字节分类):用来测试在多字节字符中满足一定条件的特殊字节.例如:isleadbyte.感觉用途不是很广,暂且忽略. Character classification(字符分类