iOS底层 - 对象的本质,方法的本质

  • 对象的本质是结构体
  • 方法的本质是消息发送

对象的本质

首先有一个SPPerson对象

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        SPPerson *p = [SPPerson new];
    }
    return NSApplicationMain(argc, argv);
}

我们知道OC底层是C和C++的汇编,我们用命令可以编译为C++代码

clang -rewrite-objc main.m -o main.cpp
// 更加优秀的编译是根据平台 和架构来

/**
xcrun                      : xcode  run
 -sdk  iphoneos      : iphoneos操作系统
 -arch  arm64         : arm64架构
*/
xcrun  -sdk  iphoneos  clang  -arch  arm64  -rewrite-objc  OC源文件  -o  输出的CPP文件

这里把main.m文件编译为main.cpp文件(十多万行代码,上面都是环境啊配置相关的内容,我们不关心,直接拖到最下面)

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        SPPerson *p = ((SPPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("SPPerson"), sel_registerName("new"));
    }
    return NSApplicationMain(argc, argv);
}

全局查找SPPerson

#ifndef _REWRITER_typedef_SPPerson
#define _REWRITER_typedef_SPPerson
typedef struct objc_object SPPerson;
typedef struct {} _objc_exc_SPPerson;
#endif

struct SPPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
};

很明显的一句话typedef struct objc_object SPPerson; 那么我们的SPPerson 就是objc_object类型的结构体~~~那么我们所说的对象预计都是objc_object类型的结构体!我们带着好奇心继续查看objc_object,(搜索struct objc_object {)

struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};

非常熟悉的身形isa这个时候我就想到了另外一个东西NSObject

#ifndef _REWRITER_typedef_NSObject
#define _REWRITER_typedef_NSObject
typedef struct objc_object NSObject;
typedef struct {} _objc_exc_NSObject;
#endif

struct NSObject_IMPL {
    Class isa;
};

我们的NSObjectisaobjc_object结构体中的属性在我们实现NSObject的时候只是对isa的重写~~~isa出镜率很高,比如我们编译block也能看到isa,代表这个匿名函数也是对象,也重复符合万物皆对象(别说你还缺对象)的说法!!!

方法的本质

继续来看方法的本质

SPPerson *p = [SPPerson new];

//clang -rewrite-objc main.m -o main.cpp
SPPerson *p = ((SPPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("SPPerson"), sel_registerName("new"));

调用SPPerson的new方法进行初始化,通过编译的代码我们发现底层是通过一个函数objc_msgSend()就是消息发送方法,

objc_msgSend(id self, SEL _cmd);

  • 一个是消息接受者
  • 一个是消息编号

我们在代码中直接调用objc_msgSend()方法时,系统提示默认参数个数是0个,考虑到编译参数问题,可以关闭严格核查,这样就不会报错了。

关闭严格核查

我通过SEL能找到函数实现,底层是依赖一个IMP的函数指针
就会找我们具体的函数实现
下面我模拟是不是也可不断发送消息,模拟四种消息发送

/*
场景:
1.两个类SPPerson和SPStudent,SPStudent继承自SPPerson;
2.两个类.h文件都声明对象方法run和类方法walk
3..m文件中实现方法
- (void)run {
    NSLog(@"%s", __func__);
}
+ (void)walk {
    NSLog(@"%s", __func__);
}
*/

SPStudent *s = [[SPStudent alloc] init];
[s run];
// 方法调用底层编译
// 方法的本质: 消息 : 消息接受者 消息编号 ....参数 (消息体)
objc_msgSend(s, sel_registerName("run"));

// 类方法编译底层
[SPStudent walk];
objc_msgSend(objc_getClass("SPStudent"), sel_registerName("walk"));

// 向父类发送消息(对象方法)
struct objc_super mySuper;
mySuper.receiver = s;
mySuper.super_class = class_getSuperclass([s class]);
objc_msgSendSuper(&mySuper, @selector(run));

// 向父类发送消息(类方法)
struct objc_super myClassSuper;
myClassSuper.receiver = s;
myClassSuper.super_class = class_getSuperclass(object_getClass([s class]));
objc_msgSendSuper(&myClassSuper, sel_registerName("walk"));

打印结果

2019-11-05 22:41:34.628826+0800 atys[21534:2150951] -[SPStudent run]
2019-11-05 22:41:34.628964+0800 atys[21534:2150951] -[SPStudent run]
2019-11-05 22:41:34.629047+0800 atys[21534:2150951] +[SPStudent walk]
2019-11-05 22:41:34.629128+0800 atys[21534:2150951] +[SPStudent walk]
2019-11-05 22:41:34.629214+0800 atys[21534:2150951] -[SPPerson run]
2019-11-05 22:41:34.629292+0800 atys[21534:2150951] +[SPPerson walk]

你可能感兴趣的:(iOS底层 - 对象的本质,方法的本质)