zoukankan      html  css  js  c++  java
  • 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, "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/

     

     

     

     

     

     

     

  • 相关阅读:
    动态规划(1)
    dockerfile构架镜像(8)
    redis(1)
    递归算法(1)
    docker commit理解构建镜像(7)
    Node fs 创建多层文件夹
    SUSE系列---修改IP和hosts文件
    本地oracle可以通过localhost连接,无法通过ip地址连接解决方法,oracle远程连接配置
    PLSQL报错: ORA-12514:TNS:监听程序当前无法识别连接描述符中请求的服务
    StringUtils字符串工具类左侧补齐(leftPad)、右侧补齐(rightPad)、左右两侧补齐(center)工具方法
  • 原文地址:https://www.cnblogs.com/windsSunShine/p/5136413.html
Copyright © 2011-2022 走看看