一、isa 的走位分析
在开始下面的知识之前,先给出一个问题,我们都知道OC 里面的对象可以创建多个,那类可以创建的多个吗?
答案是类只能创建一个,接下来就在工程中验证一下


现在就出现了3个名词:对象,类,元类。
对象:根据类实例化出来的。
类:内存里只有一份,系统创建的。
元类:系统编译过程中发现有这么一个类,系统也同时创建的,也就是编译阶段产生的。
isa 的走向:
对象isa -> 类isa -> 元类isa -> ?根元类
问题:元类指向的又是什么呢?

并且还知道了 NSObject 是根元类,根元类指向的还是根元类。看下图,isa的流程图(来自官网)

细节说明:
1)supperclass 来自继承关系;
2)上图还有标识 Root class -> Root class -> nil(Root class 不存在时),这时通常就会报错;
3)NSObject 父类是 nil ;
4)根元类的父类是 NSObject。
接下看另一验证

二、对象的本质
从这开始就开始分析对象的本质,要从哪下手呢,我们可以从编译的C++文件中分析,通过 clang -rewrite-objc [文件名].m -o [文件名].cpp 这条指令可以将.m 文件生成 .cpp 文件,一起来实践一下 首先准备一下要生成的文件main.m,贴出代码
#import <Foundation/Foundation.h>
@interface LGPerson : NSObject
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
}
return 0;
}
然后开始将 main.m 编译一份 main.cpp
编译过程请参考链接:www.jianshu.com/p/7ef9d6605…



struct NSObject_IMPL {
Class isa;
};
根据上面的截图和NSObject_IMPL的结构体内容,也说明了,对象默认就是有个isa属性。由此可知,对象在底层编译时会得到结构体并且还支持一些的继承关系,比如父类的属性也会被继承过来,那但是如果是我们添加的一些属性呢? 接下就添加一个类型NSString ,名为 name 的属性,再 clang 一次,然后再打开cpp文件

static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) {
return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name));
}
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) {
(*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)) = name;
}
上面两个方法便是LGPerson属性的 Get 方法函数实现和LGPerson属性的 Set 方法函数实现。注意一个小细节,在传递的参数里面,会增加两个默认的参数,LGPerson * self 和 SEL _cmd ----->这就是为什么能直接拿到 self 与打印 cmd 的原因。
除了属性列表当然还有方法列表,再找找方法列表相关信息(如下图)
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_INSTANCE_METHODS_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_}}
};
其中有个编译生成的 set 与 get 方法

"@16@0:8":
@ :返回值类型
16:总共的长度
@ (第二个):参数一类型 --- id 0-7
::参数二类型 ---- sel 8-15
"v24@0:8@16":
v: 返回值 void
24 :总的长度
@(第一个):参数一类型 --- id 0-7
::参数二类型 ---- sel 8-15
@(第三个):参数三类型 --- id 16-23
以上符号代表的类型都怎么知道的呢,通过 @encode(),验证一下


拓展1:将每种数据类型所占空间表也贴出来

拓展2:在将 .m 文件用 clang 语句的过程中的可能会遇到的状况与解决办法 1)正常情况下 :clang -rewrite-objc [文件名].m -o [文件名].cpp
2)另一种方式:
xcrun -sdk iphonesimulator clang -rewrite-objc main.m -o main4.cpp (iphonesimulator使用模拟器库资源)
xcrun -sdk iphones clang -rewrite-objc main.m -o main4.cpp (iphones使用真机库资源)
3)遇到UIKit系统及其他动态库引用问题时:clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ViewController.m -o ViewController.cpp
其中(iPhoneSimulator.platform为模拟器资源,iphones为真机资源)
- 如遇下图错误
解决方案:新建一个终端,输入xcode-select --install 弹出一个框,然后点击”安装“。安装完成后,重新输入clang -rewrite-objc xxx.m即可
拓展3 : 类和元类是在什么创建的呢?--->编译期
验证:先编译一下项目此时还并未跑起来,然后将要验证的 Target 拖到 MachOView
