02-runtime初探-对象与方法的本质

167 阅读3分钟

1.在需要使用runtime的地方导入#import<objc/message.h>。
如demo, LGPersoon类,有run方法申明,但无无实现

@interface LGPersoon : NSObject
- (void)run;
@end

如下图,说明OC是动态语言

image.png

我们来看下,main文件通过runtime编译成c语言的代码

终端执行,将.m生成C
clang -rewrite-objc main.m -o main.cpp

就成生成。cpp文件
OC对象的本质就是结构体

LGPersoon *p = [[LGPersoon alloc] init];
[p run];

screenshot-20220217-135858.png

方法的本质 -  发送消息

/// (void *)objc_msgSend)((id)p   消息接收者
/// sel_registerName("run")    方法编号
/// IMP函数实现的指针 --sel ->找到 IMP
/// 下层通讯   方法 -- 对象 --类
/// 父类
LGStudent *s = [LGStudent new];
// [s run];
/// 方法的调用底层编译
/// 方法的本质 : 消息: 消息接收者 消息编号 。。。 参数(消息体) 方法调用
((**void** *(*)(**id**, **SEL**))objc_msgSend)(s, sel_registerName("run"));
NSLog(@"%p --- %p", sel_registerName("run"), **@selector**(run));

NSLog(@"%p --- %p", sel_registerName("run"), @selector(run)); 你会发现。打印的方法编号是一样的

runtime-初探[86068:943153] 0x7fff6dd7b8bb --- 0x7fff6dd7b8bb
这说明@selector(run) 与 sel_registerName("run") 是等同的

问?有一个类对象,怎么调用对象不存在的方法,且编译不会报错

答: 那当然是使用runtime了
LGPersoon *p = [[LGPersoon alloc] init];
方法1: [p performSelector:@selector(walk)];
方法2: ((void ()(id, SEL))objc_msgSend)(p, sel_registerName("walk"));
方法3:
void *pointB = &p;
[( __bridge id)pointA walk];

类方法的调用

((void*(*)(id, SEL))objc_msgSend)(objc_getClass("LGStudent"), sel_registerName("run"));

向父类发送消息(对象方法)

struct objc_super mySuper;
mySuper.receiver = s;
mySuper.super_class = class_getSuperclass([s class]);  /// 元类
((void*(*)(id, SEL))objc_msgSendSuper)((__bridge id)(&mySuper), @selector(run));

面试题

  1. 对象方法存在哪
    类里面
  2. 类方法存在哪
    元类里面
  3. 类方法存在 元类里面,哪方法是什么样的方式姿态存在
    实例方法 ///  对象 是 类 的一个实例对象  对象的方法是以 实例方法的形式存在类 ///  so  类对象 也是元类的 一个实例。  所以类方法 是以 实例方法的 姿态存在元类里面
//  main.m
//  runtime-初探
//#import <UIKit/UIKit.h>
//#import "AppDelegate.h"
#import "LGPersoon.h"
#import "LGStudent.h"
#import <objc/message.h>

// 代码--->编译连接---> 执行
// 对于C函数就是静态性,我编译如果不存在这个run函数,就会报错,但是OC不一样
**int** main(**int** argc, **char** * argv[]) {
//    NSString * appDelegateClassName;
    **@autoreleasepool** {
        // Setup code that might create autoreleased objects goes here.
//        appDelegateClassName = NSStringFromClass([AppDelegate class]);

        LGPersoon *p = [[LGPersoon alloc] init];
//        [p run];
        // clang --rewrite-objc main.m -o main.cpp
        /** 编译成c后的源码
         LGPersoon *p = ((LGPersoon *(*)(id, SEL))(void *)objc_msgSend)((id)((LGPersoon *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPersoon"), sel_registerName("alloc")), sel_registerName("init"));

         ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));
         */
        /// 方法的本质 -  发送消息
        /// (void *)objc_msgSend)((id)p   消息接收者
        /// sel_registerName("run")    方法编号
        /// IMP函数实现的指针 --sel ->找到 IMP
        /// 下层通讯   方法 -- 对象 --类
        /// 父类
        LGStudent *s = [LGStudent new];
//        [s run];
        /// 方法的调用底层编译
        /// 方法的本质 : 消息: 消息接收者 消息编号 。。。 参数(消息体) 方法调用
        ((void *(*)(id, SEL))objc_msgSend)(s, sel_registerName("run"));

        /// runtime
        NSLog(@"%p --- %p", sel_registerName("run"), @selector(run));
        
        /// 0x7fff6dd7b8bb --- 0x7fff6dd7b8bb  说明 sel_registerName("run") 等同于 @selector(run)
        /// 类方法编译底层
        id cls = [LGStudent class];
        void *pointA = &cls;
        void *pointB = &p;
        [( __bridge id)pointA walk];
        /// 使用&对象,然后 (__bridge id) 就可以调用方法了,编译不会报错。 (p 是不存在 walk 方法的);
//        [(__bridge id)pointB walk];
//        [p performSelector:@selector(walk)];
//        ((void *(*)(id, SEL))objc_msgSend)(p, sel_registerName("walk"));

        /// 类方法的调用
        ((void *(*)(id, SEL))objc_msgSend)(objc_getClass("LGStudent"), sel_registerName("run"));

        /// 向父类发送消息(类方法)
        struct objc_super mySuper;
        mySuper.receiver = s;
        mySuper.super_class = class_getSuperclass([s class]);  /// 元类
        ((void *(*)(id, SEL))objc_msgSendSuper)((__bridge id)(&mySuper), @selector(run));

        /// 面试题
        /// 对象方法存在哪。-  答。类里面
        /// 类方法存在哪。- 答。 元类

        /// 面试题
        /// 类方法存在 元类里面,哪方法是什么样的方式姿态存在 答: 实例方法
        ///  对象 是 类 的一个实例对象  对象的方法是以 实例方法的形式存在类
        ///  so  类对象 也是元类的 一个实例。  所以类方法 是以 实例方法的 姿态存在元类里面
    }
    return 0;
}