通过Clang 看OC对象的本质

219 阅读3分钟

1. Clang编译

创建一个MacOS工程,我们编译里面的main.m文件。 编译命令:clang -rewrite-objc main.m -o main.cpp,通过该命令可以将main.m编译成一个cpp文件。

其他命令

// macos命令行项目:
clang -rewrite-objc main.m -o main.cpp  

clang -rewrite-objc 

// 引用UIKit库
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.5.sdk main.m

// xcrun xcode 命令:
xcrun -sdk iphonesimulator clang -arch -arm64 -rewrite-objc main.m -o main.m-arm64.cpp // 模拟器
xcrun -sdk iphoneos clang -arch -arm64 -rewrite-objc main.m -o main.m-arm64.cpp // 真机

2.编译结果

main.m内容如下:

#import <Foundation/Foundation.h>

@interface Person : NSObject {
    NSString *nickname;
}
@property (weak, nonatomic) NSString * name;

@end

@implementation Person


@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
    }
    return 0;
}

我们搜索Person 有124个结果

搜索结果

通过对比这些结果分析我们知道LGPerson编译后的结果如下cpp文件我们可以看到:

  • 我们的Person类被编译为一个的objc_object结构体:
  • 成员变量nickname还是nickname
  • 属性name被编译成了_name
  • 同时生成了name属性的getter和setter方法
#ifndef _REWRITER_typedef_Person
#define _REWRITER_typedef_Person
typedef struct objc_object Person;
typedef struct {} _objc_exc_Person;
#endif

extern "C" unsigned long OBJC_IVAR_$_Person$_name;
struct Person_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *nickname;
	NSString *_name;
};

// @property (copy, nonatomic) NSString * name;

/* @end */


// @implementation Person



static NSString * _I_Person_name(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); }
// @end

我们再次进行全局 搜索 typedef struct objc_object得到843个结果,可见这个结构体一定定义了很多很多的东西。

那么objc_object在底层到底是什么东西呢,我们可以通过搜索objc_object {去寻找它的定义。通过搜索我们找到了objc_object的定义。再其上下还发现了Class和id以及SEL。

typedef struct objc_class *Class;


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


typedef struct objc_object *id;



typedef struct objc_selector *SEL;

由以上代码我们可以得到如下结论:

  • objc_object在底层是一个结构体
  • id 是一个objc_object类型的结构体指针
  • SEL是一个objc_selector类型的结构体指针
  • Class是一个objc_class类型的结构体指针

那么objc_selectorobjc_class是怎么实现的呢,我们发现再次进行全局搜索相关字符后就找不到相关结果了。下面先来介绍一个通过clang编译后的Person相关的属性和方法签名相关知识。objc_class可以参考我的另一篇文章:iOS Objective-C 类原理objc_selector可以参考objc_selector

3.方法列表和签名

注 : Xcode 11.5 (11E608c)

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[4];
} _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	4,
	{{(struct objc_selector *)"name", "@16@0:8", (void *)_I_Person_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_Person_setName_},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_Person_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_Person_setName_}}
};

"@16@0:8"为方法签名:

  • @ 为返回值类型
  • 16 是所有参数所占内存长度
  • @ 为第一个参数类型(id self)
  • 0 是第一个参数的起始位置 (id 占8字节)
  • :为第二个参数类型 (SEL cmd)
  • 8 为第二个参数起始位置(如果参数多,可以以此类推)(SEL 占8字节)

各种类型对应符号:

  • 打印:
void types(){
    NSLog(@"char --> %s",@encode(char));
    NSLog(@"int --> %s",@encode(int));
    NSLog(@"short --> %s",@encode(short));
    NSLog(@"long --> %s",@encode(long));
    NSLog(@"long long --> %s",@encode(long long));
    NSLog(@"unsigned char --> %s",@encode(unsigned char));
    NSLog(@"unsigned int --> %s",@encode(unsigned int));
    NSLog(@"unsigned short --> %s",@encode(unsigned short));
    NSLog(@"unsigned long --> %s",@encode(unsigned long long));
    NSLog(@"float --> %s",@encode(float));
    NSLog(@"bool --> %s",@encode(bool));
    NSLog(@"void --> %s",@encode(void));
    NSLog(@"char * --> %s",@encode(char *));
    NSLog(@"id --> %s",@encode(id));
    NSLog(@"Class --> %s",@encode(Class));
    NSLog(@"SEL --> %s",@encode(SEL));
    int array[] = {1,2,3};
    NSLog(@"int[] --> %s",@encode(typeof(array)));
    typedef struct person{
        char *name;
        int age;
    }Person;
    NSLog(@"struct --> %s",@encode(Person));
    
    typedef union union_type{
        char *name;
        int a;
    }Union;
    NSLog(@"union --> %s",@encode(Union));

    int a = 2;
    int *b = {&a};
    NSLog(@"int[] --> %s",@encode(typeof(b)));
}

查找

类型

占用内存大小