常见的拓扑结构有哪些&Clang Rewrite视角OC类结构分析与部分常见考点_@end

作者丨收納箱 来源丨掘金 0. 简介 本文利用clang -rewrite-objc探索了类、元类中存储的信息;类、元类、根源类的关系。 最后,通过 属性重排 展示了苹果对成员变量的布局

原标题:Clang Rewrite视角OC类结构分析与部分常见考点

作者丨收納箱

来源丨掘金

0. 简介

本文利用clang -rewrite-objc探索了类、元类中存储的信息;类、元类、根源类的关系。

最后,通过 属性重排 展示了苹果对成员变量的布局优化,再次重申 《WWDC20 iOS14 Runtime优化》 中苹果反复提到的观点:

在开发中,请使用运行时API,而不是指针偏移、mask等更底层的方式获取信息。

因为苹果可能对这底层逻辑进行优化,如果直接访问会有不稳定的问题,但对外提供的API是稳定的,所以推荐使用运行时API来解决遇到的问题。

1.类、元类、根源类 1.1 源码

main.m

#import <Foundation/Foundation.h>

@interfaceYCFather: NSObject

@property( nonatomic, copy) NSString*name;

+ ( void)clsSayHello1;

- ( void)insSayHello1;

@end

@implementationYCFather

+ ( void)clsSayHello1 {}

- ( void)insSayHello1 {}

@end

@interfaceYCSon: YCFather

@property( nonatomic, copy) NSString*midName;

+ ( void)clsSayHello2;

- ( void)insSayHello2;

@end

@implementationYCSon

+ ( void)clsSayHello2 {}

- ( void)insSayHello2 {}

@end

intmain( intargc, char* argv[]) {

return0;

}

1.2 Clang -rewrite-objc

执行下面的代码,会输出一个 main.cpp 文件。

xcrun -sdk iphoneos clang -arch arm64 -w -rewrite-objc -fobjc-arc -mios-version-min= 8.0.0-fobjc-runtime=ios- 8.0.0main.m

⚠️ 注意:此处使用 clang -rewrite-objc 得到的c++代码只能做参考,与真实的代码还是会有一定出入,但逻辑基本一致

1.2.1 类的基本结构

搜索 NSObject

#ifndef _REWRITER_typedef_NSObject

#define _REWRITER_typedef_NSObject

typedefstructobjc_object NSObject;

typedefstruct{} _objc_exc_ NSObject;

#endif

structNSObject_IMPL{

__unsafe_unretained Class isa;

};

我们可以看到 NSObject 主要成员变量就是 Class isa ,下面我们再看看 YCFather 和 YCSon 。

#ifndef _REWRITER_typedef_YCFather

#define _REWRITER_typedef_YCFather

typedefstructobjc_object Y CFather;

typedefstruct{} _objc_exc_Y CFather;

#endif

extern"C"unsignedlongOBJC_IVAR_$_Y CFather$_name;

structY CFather_IMPL{

structNSObject_IMPLNSObject_IVARS; // 继承 NSObject_IMPL 的成员变量 isa

NSString*__ strong_name; // 追加自己的 _name 成员变量

};

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

// + (void)clsSayHello1;

// - (void)insSayHello1;

/* @end */

// @implementation YCFather

// clsSayHello1 IMP

staticvoid_C_Y CFather_clsSayHello1(Class self, SEL _cmd) {}

// insSayHello1 IMP

staticvoid_I_Y CFather_insSayHello1(Y CFather* self, SEL _cmd) {}

// name 属性的 get 方法

staticNSString* _I_Y CFather_name(Y CFather* self, SEL _cmd) { return(*( NSString*__ strong*)(( char*) self+ OBJC_IVAR_$_Y CFather$_name)); }

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

// name 属性的 set 方法

staticvoid_I_Y CFather_setName_(Y CFather* self, SEL _cmd, NSString*name) { objc_setProperty ( self, _cmd, __OFFSETOFIVAR__( structY CFather, _name), ( id)name, 0, 1); }

// @end

#ifndef _REWRITER_typedef_YCSon

#define _REWRITER_typedef_YCSon

typedefstructobjc_object YCSon;

typedefstruct{} _objc_exc_YCSon;

#endif

extern"C"unsignedlongOBJC_IVAR_$_YCSon$_midName;

structYCSon_I MPL{

structY CFather_IMPLY CFather_IVARS; // 继承 YCFather_IMPL 的成员变量 isa 和 _name

NSString*__ strong_midName; // 追加自己的 _midName 成员变量

};

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

// + (void)clsSayHello2;

// - (void)insSayHello2;

/* @end */

// @implementation YCSon

// clsSayHello2 IMP

staticvoid_C_YCSon_clsSayHello2(Class self, SEL _cmd) {}

// insSayHello2 IMP

staticvoid_I_YCSon_insSayHello2(YCSon * self, SEL _cmd) {}

// midName 属性的 get 方法

staticNSString* _I_YCSon_midName(YCSon * self, SEL _cmd) { return(*( NSString*__ strong*)(( char*) self+ OBJC_IVAR_$_YCSon$_midName)); }

// midName 属性的 set 方法

staticvoid_I_YCSon_setMidName_(YCSon * self, SEL _cmd, NSString*midName) { objc_setProperty ( self, _cmd, __OFFSETOFIVAR__( structYCSon, _midName), ( id)midName, 0, 1); }

// @end

1.2.2 类中的信息

YCFather 和 YCSon 的分析类似,这里以 YCFather 为例进行分析。

  • 成员变量列表 OBJC $_INSTANCE_VARIABLES_YCFather
static struct /*_ivar_list_t*/{

unsignedintentsize; // sizeof(struct _prop_t)

unsignedintcount;

struct_ivar_tivar_list[ 1];

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

sizeof( _ivar_t),

1, // 只有一个成员变量

{{( unsignedlongint*)&OBJC_IVAR_$_YCFather$_name, "_name", "@"NSString"", 3, 8}} // 成员变量_name

};

  • 实例方法列表 OBJC $_INSTANCE_METHODS_YCFather
static struct /*_method_list_t*/ {

unsigned int entsize; // sizeof(struct _objc_method)

unsigned int method_count;

struct _objc_method method_list[5];

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

sizeof(_objc_method),

5, // 有5个实例方法,SEL、Encode、IMP

{{( structobjc_ selector*) "insSayHello1", "v16@0:8", ( void*)_ I_ YCFather_ insSayHello1},

{( structobjc_ selector*) "name", "@16@0:8", ( void*)_ I_ YCFather_ name},

{( structobjc_ selector*) "setName:", "v24@0:8@16", ( void*)_ I_ YCFather_ setName_},

{( structobjc_ selector*) "name", "@16@0:8", ( void*)_ I_ YCFather_ name},

{( structobjc_ selector*) "setName:", "v24@0:8@16", ( void*)_ I_ YCFather_ setName_}}

};

  • 类方法列表 OBJC $_CLASS_METHODS_YCFather
staticstruct/*_method_list_t*/{

unsignedintentsize; // sizeof(struct _objc_method)

unsignedintmethod_count;

struct_objc_method method_list[ 1];

} _OBJC_$_CLASS_METHODS_Y CFather__attribute__ ((used, section ( "__DATA,__objc_const"))) = {

sizeof(_objc_method),

1, // 有1个类方法,SEL、Encode、IMP

{{( structobjc_selector *) "clsSayHello1", "v16@0:8", ( void*)_C_Y CFather_clsSayHello1}}

};

  • 属性方法列表 OBJC $_PROP_LIST_YCFather
static struct/* _prop_list_t*/ {

unsigned int entsize; //sizeof( struct_prop_t)

unsigned int count_of_properties;

struct_prop_tprop_list[1];

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

sizeof(_prop_t),

1, //1个属性

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

};

  • 元类对象 RO 的初始化 OBJC METACLASS_RO_$_YCFather

⚠️ 常见考点1:类方法存储在元类 (注意static,这些信息只存在一份)

staticstruct_class_ro_t_OBJC_METACLASS_RO_$_YCFather __attribute__ ((used, section ( "__DATA,__objc_const"))) = {

1, sizeof( struct_class_t), sizeof( struct_class_t),

0,

"YCFather",

( conststruct_method_list_t*)&_OBJC_$_CLASS_METHODS_YCFather, // YCFather 的类方法存储在元类

0,

0,

0,

0,

};

  • 类对象 RO 的初始化 OBJC CLASS_RO_$_YCFather

⚠️ 常见考点2:实例方法存储在类 (注意static,这些信息只存在一份)

staticstruct_class_ro_t_OBJC_CLASS_RO_$_YCFather __attribute__ ((used, section ( "__DATA,__objc_const"))) = {

0, __OFFSETOFIVAR__( structYCFather, _name), sizeof( structYCFather_IMPL),

0,

"YCFather",

( conststruct_method_list_t*)&_OBJC_$_INSTANCE_METHODS_YCFather, // 方法列表

0,

( conststruct_ivar_list_t*)&_OBJC_$_INSTANCE_VARIABLES_YCFather, // 成员变量列表

0,

( conststruct_prop_list_t*)&_OBJC_$_PROP_LIST_YCFather, // 属性列表

};

  • 元类 OBJC_METACLASS__YCFather∗∗、类∗∗OBJC_CLASS__YCFather**、类 **OBJC_CLASS__YCFather∗∗、类∗∗OBJC_CLASS__YCFather 的初始化
extern"C"__declspec(dllimport) struct_class_tOBJC_METACLASS_$_NSObject;

// 元类的初始化

extern"C"__declspec(dllexport) struct_class_tOBJC_METACLASS_$_YCFather __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_$_YCFather,

};

extern"C"__declspec(dllimport) struct_class_tOBJC_CLASS_$_NSObject;

// 类的初始化

extern"C"__declspec(dllexport) struct_class_tOBJC_CLASS_$_YCFather __attribute__ ((used, section ( "__DATA,__objc_data"))) = {

0, // &OBJC_METACLASS_$_YCFather,

0, // &OBJC_CLASS_$_NSObject,

0, // (void *)&_objc_empty_cache,

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

&_OBJC_CLASS_RO_$_YCFather,

};

  • 元类与类的关系绑定

⚠️ 常见考点3:类、元类、根源类的关系

static voidOBJC_CLASS_SETUP_ $_YCFather( void) {

OBJC_METACLASS_ $_YCFather.isa= &OBJC_METACLASS_ $_NSObject; // YCFather元类的 isa 为 NSObject元类(根元类)

OBJC_METACLASS_ $_YCFather.superclass= &OBJC_METACLASS_ $_NSObject; // YCFather元类的 superclass 为 NSObject元类

OBJC_METACLASS_ $_YCFather.cache= &_objc_empty_cache;

OBJC_CLASS_ $_YCFather.isa= &OBJC_METACLASS_ $_YCFather; // YCFather类的 isa 为 YCFather元类

OBJC_CLASS_ $_YCFather.superclass= &OBJC_CLASS_ $_NSObject; // YCFather类的 superclass 为 NSObject类

OBJC_CLASS_ $_YCFather.cache= &_objc_empty_cache;

}

// 这里特意补上 YCSon 的关系绑定

static voidOBJC_CLASS_SETUP_ $_YCSon( void) {

OBJC_METACLASS_ $_YCSon.isa= &OBJC_METACLASS_ $_NSObject; // YCSon元类的 isa 为 NSObject元类(根元类)

OBJC_METACLASS_ $_YCSon.superclass= &OBJC_METACLASS_ $_YCFather; // YCSon元类的 superclass 为 YCSon元类的

OBJC_METACLASS_ $_YCSon.cache= &_objc_empty_cache;

OBJC_CLASS_ $_YCSon.isa= &OBJC_METACLASS_ $_YCSon; // YCSon类的 isa 为 YCSon元类

OBJC_CLASS_ $_YCSon.superclass= &OBJC_CLASS_ $_YCFather; // YCSon类的 superclass 为 YCFather类

OBJC_CLASS_ $_YCSon.cache= &_objc_empty_cache;

}

这里比较可惜的是,我们无法直观地看到 OBJC_METACLASS_$_NSObject 中的结构,但我们可以通过运行时或源码分析,已经得到了下面这张经典的图片了。根据上面的代码分析,我们也可以直观的验证下图所示的大部分关系。

2. 属性的成员变量重排 2.1 源码

main.m

#import <Foundation/Foundation.h>

@interfaceYCFather: NSObject

@property( nonatomic, copy) NSString*name;

@property( nonatomic, copy) NSString*nickName;

@property( nonatomic, assign) intage;

@property( nonatomic, assign) longheight;

@property( nonatomic) charc1;

@property( nonatomic) charc2;

@end

@implementationYCFather

@end

intmain( intargc, char* argv[]) {

return0;

}

2.2 Clang -rewrite-objc

执行下面的代码,会输出一个 main.cpp 文件。

xcrun -sdk iphoneos clang -arch arm64 -w -rewrite-objc -fobjc-arc -mios-version-min= 8.0.0-fobjc-runtime=ios- 8.0.0main.m

⚠️ 注意:此处使用 clang -rewrite-objc 得到的c++代码只能做参考,与真实的代码还是会有一定出入,但逻辑基本一致

我们可以看到:成员变量的顺序和我们声明属性的顺序不同。

structY CFather_IMPL{

structNSObject_IMPLNSObject_IVARS;

char_c1;

char_c2;

int_age;

NSString*__ strong_name;

NSString*__ strong_nickName;

long_height;

};

这是因为苹果进行了内存优化,我们可以对比一下:

#import <objc/runtime.h>

structNSObject_IMPL{

__unsafe_unretained Class isa;

};

structY CFather{

structNSObject_IMPLNSObject_IVARS;

NSString*__ strong_name;

NSString*__ strong_nickName;

int_age;

long_height;

char_c1;

char_c2;

};

structY CFather_IMPL{

structNSObject_IMPLNSObject_IVARS;

char_c1;

char_c2;

int_age;

NSString*__ strong_name;

NSString*__ strong_nickName;

long_height;

};

@implementationViewController

- ( void)viewDidLoad {

[ superviewDidLoad];

printf( "sizeof(struct YCFather): %lunsizeof(struct YCFather_IMPL): %lu", sizeof( structY CFather), sizeof( structY CFather_IMPL));

}

@end

// 输出

sizeof( structY CFather): 48

sizeof( structY CFather_IMPL): 40

可以看到经过重排之后的结构体大小比没有重排的结构体大小优化了48−40=848-40=848−40=8字节。

当然,我们同样可以通过调试输出的方式来验证真实的内存布局与我们的分析是一致的:

本文来自投稿,不代表长河网立场,转载请注明出处: http://www.changhe99.com/a/oV6MyXPO6g.html

(0)

相关推荐