clang c语言多文件编译,如何看clang编译后的C语言代码

这是一篇比较轻松的文章,说一说clang编译后的代码怎么看。clang编译后的是C++的源代码,但其实也仅是用到了struct结构,其本质是C语言源代码,所以标题里就写“C语言代码”了。10多行的代码在编译之后能达到上万行代码,如果是第一次看,还是会有点懵的,所以记录一下要如何找到自己想要的核心代码,以便有小伙伴想要了解这块时,少走一点弯路。

准备OC代码,并编译

OC的代码写的越多,引用的Framework越多,编译后的代码量也会越多,就越影响自己阅读代码。我这里就想看一下编译后的OC类、实例对象、方法等等是怎样的关联,所以只引用了就够用了。完整的OC代码如下,一会将编译这份代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20// main.m

#import

@interface EOCObject : NSObject

@property (nonatomic, copy) NSString *name;

@end

@implementation EOCObject

- (void)work {

NSLog(@"I'm working!");

}

@end

int main(int argc, char * argv[]) {

@autoreleasepool {

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

[obj work];

return 0; // 为了简便,这里可以直接返回0。

}

}

现在打开终端,执行如下命令:

1clang -rewrite-objc main.m

警告可以忽略,没有报error就可以,然后在与main.m同一个目录下会生成一个main.cpp文件。

如果引用了其他Framework包,比如,可以指定使用模拟器中的SDK编译:

1xcrun -sdk iphonesimulator clang -rewrite-objc -fobjc-arc main.m

分析生成的.cpp文件

现在打开刚才生成的main.cpp文件,从上面往下看。上面定义了一堆基本类型结构,是编译任何一个.m文件都会有的。

首先声明了objc_selector,也就是SEL。还有objc_class,OC中类的struct结构。然后是超类结构和定义Protocol。

1

2

3

4

5

6

7

8

9

10

11

12

13#ifndef __OBJC2__

#define __OBJC2__

#endif

struct objc_selector; struct objc_class;

struct __rw_objc_super {

struct objc_object *object;

struct objc_object *superClass;

__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}

};

#ifndef _REWRITER_typedef_Protocol

typedef struct objc_object Protocol;

#define _REWRITER_typedef_Protocol

#endif

接着导入需要的基本方法。

1

2

3

4

5

6

7

8

9

10

11

12

13#define __OBJC_RW_DLLIMPORT extern

__OBJC_RW_DLLIMPORT void objc_msgSend(void);

__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);

__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);

__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);

__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);

__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass(const char *);

__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass(struct objc_class *);

__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass(const char *);

__OBJC_RW_DLLIMPORT void objc_exception_throw(struct objc_object *);

__OBJC_RW_DLLIMPORT int objc_sync_enter(struct objc_object *);

__OBJC_RW_DLLIMPORT int objc_sync_exit(struct objc_object *);

__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);

然后定义了NSUInteger的类型。

1

2

3

4

5#ifdef _WIN64

typedef unsigned long long _WIN_NSUInteger;

#else

typedef unsigned int _WIN_NSUInteger;

#endif

接着是对枚举的定义。

1

2

3

4

5

6

7

8

9

10#ifndef __FASTENUMERATIONSTATE

struct __objcFastEnumerationState {

unsigned long state;

void **itemsPtr;

unsigned long *mutationsPtr;

unsigned long extra[5];

};

__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);

#define __FASTENUMERATIONSTATE

#endif

然后是对常量字符串的定义,它的isa取值为__CFConstantStringClassReference,代码中出现的所有常量字符串类型都会转换成该类型,并且放在内存中的数据区域(.data区)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18#ifndef __NSCONSTANTSTRINGIMPL

struct __NSConstantStringImpl {

int *isa;

int flags;

char *str;

#if _WIN64

long long length;

#else

long length;

#endif

};

#ifdef CF_EXPORT_CONSTANT_STRING

extern "C" __declspec(dllexport) int __CFConstantStringClassReference[];

#else

__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];

#endif

#define __NSCONSTANTSTRINGIMPL

#endif

接着是对Block的定义,可以看到Block实际上是void *类型,它的取值这里只导入了两个_NSConcreteGlobalBlock和_NSConcreteStackBlock,其实还有一个_NSConcreteMallocBlock。_Block_object_assign是对一个Block进行copy时调用的,_Block_object_dispose就是销毁的时候了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23#ifndef BLOCK_IMPL

#define BLOCK_IMPL

struct __block_impl {

void *isa;

int Flags;

int Reserved;

void *FuncPtr;

};

// Runtime copy/destroy helper functions (from Block_private.h)

#ifdef __OBJC_EXPORT_BLOCKS

extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);

extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);

extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];

extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32];

#else

__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);

__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);

__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];

__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];

#endif

#endif

#define __block

#define __weak

下面是对OC中容器类型的定义。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15#include

struct __NSContainer_literal {

void * *arr;

__NSContainer_literal (unsigned int count, ...) {

va_list marker;

va_start(marker, count);

arr = new void *[count];

for (unsigned i = 0; i < count; i++)

arr[i] = va_arg(marker, void *);

va_end( marker );

};

~__NSContainer_literal() {

delete[] arr;

}

};

接着是与autoreleasePool相关的方法导入和定义。

1

2

3

4

5

6

7

8extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);

extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);

struct __AtAutoreleasePool {

__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}

~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}

void * atautoreleasepoolobj;

};

然后是定义一个宏,来计算struct结构中变量的偏移量。

1#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)

到此对OC中的一些基本结构的定义就结束了。下面一段是对我们自己写的代码中的字符串的定义。通过section ("__DATA, __cfstring")可以看到,常量字符串放置在了内存中的数据区域。

1static __NSConstantStringImpl __NSConstantStringImpl__var_folders_nx_dxppf5lj151119zfqd6lrc_m0000gn_T_main_obj_80f1ba_mi_0 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"I'm working!",12};

接下来有很长一段的空行,然后应该是我们OC中导入的头文件的转换。可以直接跳过,这一部分对于分析我们自己的代码作用不是很大。全局搜索#pragma clang assume_nonnull end,最后一次出现的地方的下方就是对应我们上面写的OC代码的转换了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14#ifndef _REWRITER_typedef_EOCObject

#define _REWRITER_typedef_EOCObject

typedef struct objc_object EOCObject;

typedef struct {} _objc_exc_EOCObject;

#endif

extern "C" unsigned long OBJC_IVAR_$_EOCObject$_name;

struct EOCObject_IMPL {

struct NSObject_IMPL NSObject_IVARS;

NSString *__strong _name;

};

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

/* @end */

声明一个struct objc_object类型的变量EOCObject,已经定义它的内部变量_name。

1

2

3

4

5

6

7

8

9

10

11// @implementation EOCObject

static void _I_EOCObject_work(EOCObject * self, SEL _cmd) {

NSLog((NSString *)&__NSConstantStringImpl__var_folders_nx_dxppf5lj151119zfqd6lrc_m0000gn_T_main_obj_f18233_mi_0);

}

static NSString * _I_EOCObject_name(EOCObject * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_EOCObject$_name)); }

extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_EOCObject_setName_(EOCObject * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct EOCObject, _name), (id)name, 0, 1); }

// @end

EOCObject中的3个函数实现,后面会用到此处的函数指针。

1

2

3

4

5

6

7

8int main(int argc, char * argv[]) {

/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

EOCObject *obj = ((EOCObject *(*)(id, SEL))(void *)objc_msgSend)((id)((EOCObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("EOCObject"), sel_registerName("alloc")), sel_registerName("init"));

((void (*)(id, SEL))(void *)objc_msgSend)((id)obj, sel_registerName("work"));

return 0;

}

}

对应函数main中代码的转换,方法调用都转成了用objc_msgSend调用。因为objc_msgSend返回的是void *,所以在使用时需要对其强制转换,所以看起起来啰嗦了一些。把((void (*)(id, SEL))(void *)objc_msgSend)看成一个整体就好。

再接下来,又是一堆结构体的定义,包括_prop_t、_objc_method、_protocol_t、_ivar_t、_class_ro_t、_class_t、_category_t,这一段代码比较简单,不贴代码出来了,太长了…

然后就是对我们在OC中定义的对象的处理过程,给各种struct赋值的一个过程。仔细阅读可以对我们平时在写的OC代码有一个更清晰的认识。下面依次说一下这段里的每一部分。

1extern "C" unsigned long int OBJC_IVAR_$_EOCObject$_name __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct EOCObject, _name);

利用上面的宏OFFSETOFIVAR计算_name的偏移量,赋值给变量OBJC_IVAR_$_EOCObject$_name,然后存在内存中的数据区域。

1

2

3

4

5

6

7

8

9static struct /*_ivar_list_t*/ {

unsigned int entsize; // sizeof(struct _prop_t)

unsigned int count;

struct _ivar_t ivar_list[1];

} _OBJC_$_INSTANCE_VARIABLES_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {

sizeof(_ivar_t),

1,

{{(unsigned long int *)&OBJC_IVAR_$_EOCObject$_name, "_name", "@\"NSString\"", 3, 8}}

};

拿到EOCObject的内部成员变量信息,赋值给变量_OBJC_$_INSTANCE_VARIABLES_EOCObject,每个成员变量的信息存在了_ivar_t类型的数组ivar_list中。

1

2

3

4

5

6

7

8

9

10

11static struct /*_method_list_t*/ {

unsigned int entsize; // sizeof(struct _objc_method)

unsigned int method_count;

struct _objc_method method_list[3];

} _OBJC_$_INSTANCE_METHODS_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {

sizeof(_objc_method),

3,

{{(struct objc_selector *)"work", "v16@0:8", (void *)_I_EOCObject_work},

{(struct objc_selector *)"name", "@16@0:8", (void *)_I_EOCObject_name},

{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_EOCObject_setName_}}

};

拿到EOCObject的方法信息,赋值给变量_OBJC_$_INSTANCE_METHODS_EOCObject,每个方法的信息存在_objc_method类型的数组method_list中。_objc_method由方法的名字,如(struct objc_selector *)"work",和方法类型,如v16@0:8(冒号前面表示返回值,后面表示参数),还有指向实际去执行的函数的指针,如(void *)_I_EOCObject_work,_I_EOCObject_work在main的上面定义过。

1

2

3

4

5

6

7

8

9static struct /*_prop_list_t*/ {

unsigned int entsize; // sizeof(struct _prop_t)

unsigned int count_of_properties;

struct _prop_t prop_list[1];

} _OBJC_$_PROP_LIST_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {

sizeof(_prop_t),

1,

{{"name","T@\"NSString\",C,N,V_name"}}

};

拿到EOCObject定义在property中的属性信息,赋值给_OBJC_$_PROP_LIST_EOCObject,每个属性的信息存在_prop_t类型的数组prop_list中。

1

2

3

4

5

6

7

8

9

10

11static struct _class_ro_t _OBJC_METACLASS_RO_$_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {

1, sizeof(struct _class_t), sizeof(struct _class_t),

(unsigned int)0,

0,

"EOCObject",

0,

0,

0,

0,

0,

};

拿到EOCObject的元类信息,赋值给_OBJC_METACLASS_RO_$_EOCObject,可以看到EOCObject的元类也是EOCObject,但是方法列表等都是空的,因为我们没有在OC中给EOCObject定义类方法等。

1

2

3

4

5

6

7

8

9

10

11static struct _class_ro_t _OBJC_CLASS_RO_$_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {

0, __OFFSETOFIVAR__(struct EOCObject, _name), sizeof(struct EOCObject_IMPL),

(unsigned int)0,

0,

"EOCObject",

(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_EOCObject,

0,

(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_EOCObject,

0,

(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_EOCObject,

};

那到EOCObject类本身的一些信息,赋值给_OBJC_CLASS_RO_$_EOCObject,这里就把刚刚准备好的_OBJC_$_INSTANCE_METHODS_EOCObject、_OBJC_$_INSTANCE_VARIABLES_EOCObject、_OBJC_$_PROP_LIST_EOCObject关联了起来。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;

extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_EOCObject __attribute__ ((used, section ("__DATA,__objc_data"))) = {

0, // &OBJC_METACLASS_$_NSObject,

0, // &OBJC_METACLASS_$_NSObject,

0, // (void *)&_objc_empty_cache,

0, // unused, was (void *)&_objc_empty_vtable,

&_OBJC_METACLASS_RO_$_EOCObject,

};

extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_EOCObject __attribute__ ((used, section ("__DATA,__objc_data"))) = {

0, // &OBJC_METACLASS_$_EOCObject,

0, // &OBJC_CLASS_$_NSObject,

0, // (void *)&_objc_empty_cache,

0, // unused, was (void *)&_objc_empty_vtable,

&_OBJC_CLASS_RO_$_EOCObject,

};

_class_ro_t只是存了类的基本信息,但是没有存它的isa指向,和它的父类,于是结构体_class_t又封装了一次,并且加上缓存。所以上面这段得到的就是元类NSObject、元类EOCObject、类NSObject、类EOCObject。

1

2

3

4

5

6

7

8static void OBJC_CLASS_SETUP_$_EOCObject(void ) {

OBJC_METACLASS_$_EOCObject.isa = &OBJC_METACLASS_$_NSObject;

OBJC_METACLASS_$_EOCObject.superclass = &OBJC_METACLASS_$_NSObject;

OBJC_METACLASS_$_EOCObject.cache = &_objc_empty_cache;

OBJC_CLASS_$_EOCObject.isa = &OBJC_METACLASS_$_EOCObject;

OBJC_CLASS_$_EOCObject.superclass = &OBJC_CLASS_$_NSObject;

OBJC_CLASS_$_EOCObject.cache = &_objc_empty_cache;

}

该方法就把上面的各种类之间的关系串起来了,元类EOCObject的isa和父类都指向元类NSObject,类EOCObject的isa指向元类EOCObject,它的父类指向类NSObject。

1

2

3

4#pragma section(".objc_inithooks$B", long, read, write)

__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CLASS_SETUP[] = {

(void *)&OBJC_CLASS_SETUP_$_EOCObject,

};

此处应该是一个钩子,在初始化时执行上面的OBJC_CLASS_SETUP_$_EOCObject函数,对类进行设置。

1

2

3static struct _class_t *L_OBJC_LABEL_CLASS_$ [1] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= {

&OBJC_CLASS_$_EOCObject,

};

拿到当前的文件中所有的自定义类,暂时还没有看到变量L_OBJC_LABEL_CLASS_$的用处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值