Runtime详解

turboksiOS73

The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language; it’s what makes the language work. 

Objective-C语言将尽可能多的决策从编译时和链接时间延迟到运行时。只要有可能,它都是动态的。这意味着该语言不仅需要编译器,还需要运行时系统来执行编译后的代码。运行时系统作为Objective-C语言的一种操作系统;它使语言工作。

来自百度翻译!!?

先来看一下什么是OC的消息机制?

OC中的方法调用其实都是转成了objc_msgSend函数的调用,给方法调用者发送了一条消息(selector方法名)。

例如:

KSPerson * kk = [[KSPerson alloc] init];

[kk print] 转成C函数就等价于 objc_msgSend(kk,@selector(print));

objc_msgSend底层的3大流程:消息发送、动态方法解析、消息转发

接下来看什么是Runtime?

说runtime之前、先聊一下C语言。

C语言是一门面向过程、抽象化的通用程序设计语言、广泛用于底层开发。C语言的函数调用、是在编译时期就决定好需要调用哪个函数。

而OC是一门动态性比较强的编程语言、允许很多操作推迟到程序运行时在进行。OC的动态性就是Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数。包括我们平时编写的一些OC代码、底层都是转换成了RUntime API进行调用。

Runtime的具体应用?

一、利用关联对象(AssociatedObject)给父类添加属性

    static char objKey;

    NSObject * obj = [[NSObject alloc] init];

    //设置关联对象

    objc_setAssociatedObject(obj, &objKey, @"asdf", OBJC_ASSOCIATION_RETAIN_NONATOMIC); //获取关联对象

    NSString *string = objc_getAssociatedObject(obj, &objKey);

    NSLog(@"AssociatedObject = %@", string);


二、遍历类的所有成员变量、修改UITextField的placeholder文字颜色?

//    UITextField

    unsigned int numIvars;//成员变量个数

    Ivar *vars = class_copyIvarList(NSClassFromString(@"UITextField"), &numIvars);

    NSString*key = nil;

    for(int i =0; i < numIvars; i++) {

        Ivar thisIvar = vars[i];

        key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];//成员变量的名字

        NSLog(@"name :%@", key);

        key = [NSString stringWithUTF8String:ivar_getTypeEncoding(thisIvar)];//成员变量数据类型

        NSLog(@"type :%@", key);

    }

    free(vars);

    通过获取到了UITextField的成员变量、可以获取到显示placeholder的是一个叫_placeholderLabel的变量、所以我们可以对他进行的相关属性进行修改。

    UITextField * KSField = [[UITextField alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];

    KSField.placeholder = @"测试";

    [self.view addSubview:KSField];

    Ivar ivar = class_getInstanceVariable([KSField class], "_placeholderLabel");

    id placeholderLabel = object_getIvar(KSField, ivar);

    [placeholderLabel performSelector:@selector(setTextColor:) withObject:UIColor.redColor];

三、交换方法实现

#import <Foundation/Foundation.h>

@interface NSURL (KSUrl)

+ (instancetype)KS_URLWithString:(NSString *)URLString;

@end

#import "NSURL+KSUrl.h"

#import <objc/runtime.h>

@implementation NSURL (KSUrl)

+ (void)load{

    Method m1 = class_getClassMethod([NSURL class], @selector(URLWithString:));

    Method m2 = class_getClassMethod([NSURL class], @selector(KS_URLWithString:));

    //方法交换

    method_exchangeImplementations(m1, m2);

}

+ (instancetype)KS_URLWithString:(NSString *)URLString{

    NSURL * url = [NSURL KS_URLWithString:URLString];

    if (!url) {

        NSLog(@"传入的url为空");

    }else{

        NSLog(@"传入的url正常");

    }

    return url;

}

@end

调用方法

- (void)viewDidLoad {

    [super viewDidLoad];

    NSURL * url = [NSURL URLWithString:@"111"];

}

打印:

2020-07-16 16:57:28.586612+0800 基础练习[2650:139632] 传入的url正常

四、方法找不到的异常处理

对于这种情况、我们有3次机会来处理。

objc_msgSend底层的3大流程:

消息发送、动态方法解析、消息转发

1.消息发送----编写代码的正确性、保证相关方法存在实现。

.h

@interface KSPerson : NSObject

- (void)print;

@end

.m

- (void)print{

    NSLog(@"%s",__func__);

}

KSPerson * kk = [[KSPerson alloc] init];

[kk print];

这一步就属于调用了objc_msgSend(kk,@selector(print));

也就是所谓的消息发送机制。

2.动态方法解析----

- (void)print{

    NSLog(@"%s",__func__);

}

当没有实现上边对应的方法的时候、则会进入动态方法解析阶段

+ (BOOL)resolveInstanceMethod:(SEL)sel{

    if (sel == @selector(print)) {

        Method method = class_getInstanceMethod(self, @selector(test));

        class_addMethod(self,

                        sel,

                        method_getImplementation(method),

                        method_getTypeEncoding(method));

        return NO;

    }

    return [super resolveInstanceMethod:sel];

}

-(void)test{

    NSLog(@"%s",__func__);

}



2361104-665495c73cd14dea.png

打印:

2020-07-16 17:12:13.445550+0800 基础练习[2812:147911] -[KSPerson test]

3.消息转发----

如果上边还没有处理该异常的话、就会进入到消息转发的阶段。

- (id)forwardingTargetForSelector:(SEL)aSelector{

    if (aSelector == @selector(print)) {

        return [KSStudent new];  //让KSStudent对象来处理print方法

    }

    return [super forwardingTargetForSelector:aSelector];

//    return nil;//继续下一步

}

打印:

2020-07-16 17:16:17.794979+0800 基础练习[2882:151262] -[KSStudent print]

如果上边没有进行处理、而是返回nil的话,则进入

- (id)forwardingTargetForSelector:(SEL)aSelector{

//    if (aSelector == @selector(print)) {

//        return [KSStudent new];

//    }

//    return [super forwardingTargetForSelector:aSelector];

    return nil;//继续下一步

}

@encode指令、可以将具体的类型表示成字符串编码

v: void

@: id

:: SEL

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{

    if ([NSStringFromSelector(aSelector) isEqualToString:@"print"]) {

        return [NSMethodSignature signatureWithObjCTypes:"v@:"];

    }

    return [super methodSignatureForSelector:aSelector];

}

- (void)forwardInvocation:(NSInvocation *)anInvocation{

    NSLog(@"到我这就结束了");

}

打印:

2020-07-16 17:18:03.157596+0800 基础练习[2909:152716] 到我这就结束了



相关文章

iOS一行代码搞定自定义AlertView

iOS一行代码搞定自定义AlertView

效果图走一走:使用方式: pod  'KSAlertView'举例使用,单按钮Alert: [[[KSAlertView alloc] initWithT...

isKindOfClass和isMemberOfClass的区别

isKindOfClass和isMemberOfClass的区别

+ (BOOL)isMemberOfClass:(Class)cls {//    类的元类 与 传入类 对比    return object_getClas...

iOS的KVO、KVC

iOS的KVO、KVC

一:KVO  在实际的开发过程中,我们可能会遇到这种需求,就是需要根据一个对象某个属性值的改变,来作出不同的处理。但是实现起来比较麻烦,需要在很多地方进行判断处理,为了方便我们的开...

百度地图自定义marker

百度地图自定义marker

#import "HomeViewController.h"#import "PaopaoView.h"@interface HomeViewControlle...

Runtime动态创建类

Runtime动态创建类

- (void)viewDidLoad {    [super viewDidLoad];    Class KSObj = objc_allocateClas...

iOS程序的内存布局

iOS程序的内存布局

    代码段:编译之后的代码    数据段:字符串常量:比如NSString * str = @“123”已初始化数据:已初始化的全局变量...