OC runtime

1.程序是怎样运行起来的?(以C语言以及VC运行环境为例)
  1. 编辑
  • (把程序代码输入,交给计算机)。
  1. 编译(成目标程序文件.obj)
  • 编译就是把高级语言变成计算机可以识别的2进制语言,计算机只认识1和0,编译程序把人们熟悉的语言换成2进制的。
  • 编译程序把一个源程序翻译成目标程序的工作过程分为五个阶段:词法分析;语法分析;语义检查和中间代码生成;代码优化;目标代码生成。主要是进行词法分析和语法分析,又称为源程序分析,分析过程中发现有语法错误,给出提示信息。
  1. 链接(成可执行程序文件.exe)。
  • 链接是将编译产生的.obj文件和系统库连接装配成一个可以执行的程序。
  • 为何要将源程序翻译成可执行文件的过程分为编译和链接两个独立的步骤,不是多此一举吗?之所以这样做,主要是因为:在一个较大的复杂项目中,有很多人共同完成一个项目(每个人可能承担其中一部分模块),其中有的模块可能是用汇编语言写的,有的模块可能是用VC写的,有的模块可能是用VB写的,有的模块可能是购买(不是源程序模块而是目标代码)或已有的标准库模块,因此,各类源程序都需要先各自编译成目标程序文件(2进行机器指令代码),再通过链接程序将这些目标程序文件连接装配成可执行文件。
  1. 运行(可执行程序文件)。
  • 上述四个步骤中,其中第一步的编辑工作是最繁杂而又必须细致地由人工在计算机上来完成,其余几个步骤则相对简单,基本上由计算机来自动完成。
2. 动态语言和静态语言的在程序不同阶段是怎样处理的?

编程语言有静态和动态之分。

静态语言就是,在程序运行前决定了所有的类型判断,类的所有成员,方法在编译阶段就确定好了内存地址,也就意味着所有的类对象只能访问属于自己的成员变量和方法,否则编译器直接报错。如:java,C++,C

动态语言,类型的判断、类的成员变量、方法的内存地址都是在程序的运行阶段才最终确定,并且还能动态的添加成员变量和方法。也就意味着你调用一个不存在的方法时,编译也能通过,甚至一个对象它是什么类型并不是表面我们所看到的那样,只有运行之后才能决定其真正的类型。相比于静态语言,动态语言具有较高的灵活性和可订阅性。而OC,正是一门动态语言。

3. runtime
运行时:

就是程序在运行时做的一些事。

runtime :

runtime 是OC底层的一套C语言的API,它是一个C和汇编写的动态库,将OC和C紧密关联。编译器最终都会将OC代码转化为运行时代码。

runtime这个系统主要做两件事 :

1、封装C语言的结构体和函数,让开发者在运行时创建、检查或者修改类、对象和方法等等。
2、传递消息,找出方法的最终执行代码。

在OC中,如果想要某个对象传递消息,那么就会使用动态绑定机制来决定需要调用的方法。在底层,所有的方法都是普通的C语言函数,然而对象收到消息之后,究竟该调用哪个方法则完全取决于运行期决定,甚至在运行时改变,这些特性使得OC成为一门真正的动态的语言。

OC中的重要工作都由“运行期组件”而不是编译器来完成,使用OC面向对象特性所需的全部数据结构及函数都在运行期组件里面。例如:运行期组件中含有全部的内存管理的方法。

在提高性能的方面:只需要更新运行期组件,即可提升应用程序的性能。而在那种许多的工作都在编译期完成的语言,若想要获得类似的性能提升,则要重新编译应用程序代码。

4.要了解运行时,我们得先了解OC的消息机制
4.1 消息发送(Messaging):是 Runtime 通过 selector(选择子) 快速查找 IMP 的过程,有了函数指针就可以执行对应的方法实现;

id returnValue = [someObject messageName: parameter];

调用的runtime函数:
原型:void objc_msgSend(id self, SEL cmd, . . . )
id returnValue = objc_msgSend(someObject,@ selector(messageName:), parameter);

解释:objc_msgSend函数会根据接受者和选择子的类型来调用适当的方法。为了接收完成这个操作,该方法需要在接收者(someObject)所属的类中搜寻其“方法列表”,如果能找到就跳转到实现代码,若不能,就沿着继承体系向上查找,找到后在跳转,如果最终都找不到,就执行“消息转发”。

4.2 消息转发(Message Forwarding):是在查找 IMP 失败后执行一系列转发流程的慢速通道,如果不作转发处理,则会打日志和抛出异常。

当对象接受到无法解读的消息的时候,就会启动“消息转发”机制,程序员可以经由此过程告诉对象该如何处理这个消息。

消息转发分为2大阶段:

  • 第一阶段先征询接受者,所属的类,看是否能动态的添加方法,以处理当前这个“未知的选择子”,这叫做动态方法解析。
  • 第二阶段涉及“完整的消息转发机制”,当运行期系统已经将第一阶段执行完了,那么系统会请求接受者以其他的手段来处理与消息相关的方法调用。1.请接收者看看有没有备援的接受者可以处理这个消息,若有,消息转发结束,若还是没有就启动完整的消息转发机制,运行期系统会把与消息有关的全部信息封装到NSInvocation对象中去,在给接受者最后一次机会,令其设法解决当前未处理的这条信息。


    屏幕快照 2017-12-12 15.39.00.png
5.runtime的主要应用

(1)替换系统方法

#import "UIImage+ImageLoad.h"
#import 
@implementation UIImage (ImageLoad)

//自定义方法中最后一定要再调用一下系统的方法,让其有加载图片的功能,但是由于方法交换,系统的方法名已经变成了我们自定义的方法名,这就实现了系统方法的拦截!
+(UIImage *)xh_imageNamed:(NSString *)name {
    double version = [[UIDevice currentDevice].systemVersion doubleValue];
    if (version >= 7.0) {
        name = [name stringByAppendingString:@"_ios7"];
    }
    return [UIImage xh_imageNamed:name];
    
}


//分类中重写UIImage的load方法,实现方法的交换,
+(void)load {
    //获取2个类的方法
    Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
    Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
    
    //交换
    method_exchangeImplementations(m1, m2);
}

@end

(2)实现分类关联属性

#import "UIButton+BtnImageView.h"
#import 

@implementation UIButton (BtnImageView)

-(UIImageView *)btnImageView
{
    // return objc_getAssociatedObject(self, @selector(isClicked));
    return objc_getAssociatedObject(self, &kbtnImageView);
}

//set
static char kbtnImageView;
-(void)setBtnImageView:(UIImageView *)btnImageView
{
    //如果是指针类型
    return objc_setAssociatedObject(self, &kbtnImageView, btnImageView, OBJC_ASSOCIATION_RETAIN);
    
    //1 源对象self
    //2 关键字 唯一静态变量key isClicked
    //3 关联的对象
    //4 关键策略  OBJC_ASSOCIATION_RETAIN_NONATOMIC
    //objc_setAssociatedObject(self, @selector(isClicked), @(isClicked), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}
@end

(3)字典转model

(4)归档

(5) 万能控制器跳转

- (void)push:(NSDictionary *)params
{
    // 类名
    NSString *class =[NSString stringWithFormat:@"%@", params[@"class"]];
    const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding];
    // 从一个字串返回一个类
    Class newClass = objc_getClass(className);
    if (!newClass)
    {
        // 创建一个类
        Class superClass = [NSObject class];
        newClass = objc_allocateClassPair(superClass, className, 0);
        // 注册你创建的这个类
        objc_registerClassPair(newClass);
    }
    // 创建对象
    id instance = [[newClass alloc] init];
    // 对该对象赋值属性
    NSDictionary * propertys = params[@"property"];
    [propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        // 检测这个对象是否存在该属性
        if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) {
            // 利用kvc赋值
            [instance setValue:obj forKey:key];
        }
    }];
    // 获取导航控制器
    UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController;
    UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex];
    // 跳转到对应的控制器
    [pushClassStance pushViewController:instance animated:YES];
}

//检测是否存在这个属性

- (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName
{
    unsigned int outCount, i;
    // 获取对象里的属性列表
    objc_property_t * properties = class_copyPropertyList([instance
                                                           class], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property =properties[i];
        //  属性名转成字符串
        NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        // 判断该属性是否存在
        if ([propertyName isEqualToString:verifyPropertyName]) {
            free(properties);
            return YES;
        }
    }
    free(properties);
    return NO;
}

(7)插件开发

你可能感兴趣的:(OC runtime)