Introduction of 0.
This paper uses clang-rewrite-objc to explore the information stored in classes and metaclasses. Class, metaclass, root class relationship.
Finally, through the attribute rearrangement shows apple’s layout optimization of member variables, again reiterates the point repeatedly mentioned by Apple in WWDC20 iOS14 Runtime Optimization:
In development, use runtime apis instead of lower-level methods like pointer offsets, masks, and so on to get information.
Because Apple may optimize this underlying logic, it is recommended that runtime apis be used to resolve problems that may be unstable if accessed directly, but the API provided externally is stable.
1. Class, metaclass, root class
1.1 the source code
main.m
#import <Foundation/Foundation.h>
@interface YCFather : NSObject
@property (nonatomic, copy) NSString *name;
+ (void)clsSayHello1;
- (void)insSayHello1;
@end
@implementation YCFather
+ (void)clsSayHello1 {}
- (void)insSayHello1 {}
@end
@interface YCSon : YCFather
@property (nonatomic, copy) NSString *midName;
+ (void)clsSayHello2;
- (void)insSayHello2;
@end
@implementation YCSon
+ (void)clsSayHello2 {}
- (void)insSayHello2 {}
@end
int main(int argc, char * argv[]) {
return 0;
}
Copy the code
1.2 Clang – rewrite – objc
Executing the following code outputs a main.cpp file.
Xcrun-sdk iphoneOS clang-arch arm64-w-rewrite-objc-fobjc-arc-mios-version-min =8.0.0 -fobjc-Runtime =ios-8.0.0 main.mCopy the code
⚠️ note: the c++ code using clang-rewrite-objc here is for reference only. There are some differences with the real code, but the logic is basically the same
1.2.1 Basic structure of classes
Search NSObject
#ifndef _REWRITER_typedef_NSObject
#define _REWRITER_typedef_NSObject
typedef struct objc_object NSObject;
typedef struct {} _objc_exc_NSObject;
#endif
struct NSObject_IMPL{__unsafe_unretained Class isa;
};
Copy the code
We can see that NSObject’s main member variable is Class ISA, and now let’s look at YCFather and YCSon.
#ifndef _REWRITER_typedef_YCFather
#define _REWRITER_typedef_YCFather
typedef struct objc_object YCFather;
typedef struct {} _objc_exc_YCFather;
#endif
extern "C" unsigned long OBJC_IVAR_$_YCFather$_name;
struct YCFather_IMPL {
struct NSObject_IMPL NSObject_IVARS; // inherits the NSObject_IMPL member variable isa
NSString* __strong _name; // Append your own _name member variable
};
// @property (nonatomic, copy) NSString *name;
// + (void)clsSayHello1;
// - (void)insSayHello1;
/* @end */
// @implementation YCFather
// clsSayHello1 IMP
static void _C_YCFather_clsSayHello1(Class self, SEL _cmd) {}
// insSayHello1 IMP
static void _I_YCFather_insSayHello1(YCFather * self, SEL _cmd) {}
// Get method for the name attribute
static NSString * _I_YCFather_name(YCFather * self, SEL _cmd) { return(* (NSString* __strong((*)char *)self + OBJC_IVAR_$_YCFather$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long.id.bool.bool);
// The set method of the name attribute
static void _I_YCFather_setName_(YCFather * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct YCFather, _name), (id)name, 0.1); }
// @end
#ifndef _REWRITER_typedef_YCSon
#define _REWRITER_typedef_YCSon
typedef struct objc_object YCSon;
typedef struct {} _objc_exc_YCSon;
#endif
extern "C" unsigned long OBJC_IVAR_$_YCSon$_midName;
struct YCSon_IMPL {
struct YCFather_IMPL YCFather_IVARS; // Inherit the member variables isa and _name of YCFather_IMPL
NSString* __strong _midName; // Append your _midName member variable
};
// @property (nonatomic, copy) NSString *midName;
// + (void)clsSayHello2;
// - (void)insSayHello2;
/* @end */
// @implementation YCSon
// clsSayHello2 IMP
static void _C_YCSon_clsSayHello2(Class self, SEL _cmd) {}
// insSayHello2 IMP
static void _I_YCSon_insSayHello2(YCSon * self, SEL _cmd) {}
// The get method of the midName attribute
static NSString * _I_YCSon_midName(YCSon * self, SEL _cmd) { return(* (NSString* __strong((*)char *)self + OBJC_IVAR_$_YCSon$_midName)); }
// Set method of the midName attribute
static void _I_YCSon_setMidName_(YCSon * self, SEL _cmd, NSString *midName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct YCSon, _midName), (id)midName, 0.1); }
// @end
Copy the code
1.2.2 Information in classes
The analysis of YCFather is similar to that of YCSon. Here, YCFather is taken as an example for analysis.
- List of member variables _OBJC_$_INSTANCE_VARIABLES_YCFather
static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[1];
} _OBJC_$_INSTANCE_VARIABLES_YCFather __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
1.// There is only one member variable
{{(unsigned long int *)&OBJC_IVAR_$_YCFather$_name, "_name"."@\"NSString\"".3.8}} // member variable _name
};
Copy the code
- List of instance methods _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.// There are 5 instance methods: SEL, Encode, IMP
{{(struct objc_selector *)"insSayHello1"."v16@0:8", (void *)_I_YCFather_insSayHello1},
{(struct objc_selector *)"name"."@ @ 0:8 16", (void *)_I_YCFather_name},
{(struct objc_selector *)"setName:"."v24@0:8@16", (void *)_I_YCFather_setName_},
{(struct objc_selector *)"name"."@ @ 0:8 16", (void *)_I_YCFather_name},
{(struct objc_selector *)"setName:"."v24@0:8@16", (void *)_I_YCFather_setName_}}
};
Copy the code
- Class method list _OBJC_$_CLASS_METHODS_YCFather
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_YCFather __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1.// there are 1 class methods, SEL, Encode, IMP
{{(struct objc_selector *)"clsSayHello1"."v16@0:8", (void *)_C_YCFather_clsSayHello1}}
};
Copy the code
- Property method list _OBJC_$_PROP_LIST_YCFather
static 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_YCFather __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1.// Have 1 attribute
{{"name"."T@\"NSString\",C,N,V_name"}}};Copy the code
- Initialization of the metaclass object RO _OBJC_METACLASS_RO_$_YCFather
⚠️ : Class methods are stored in a metaclass.
static struct _class_ro_t _OBJC_METACLASS_RO_$_YCFather __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1.sizeof(struct _class_t), sizeof(struct _class_t),
0."YCFather",
(const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_YCFather, // YCFather's class methods are stored in the metaclass
0.0.0.0};Copy the code
- Initializer of class object RO _OBJC_CLASS_RO_$_YCFather
⚠️ : Instance methods are stored in classes.
static struct _class_ro_t _OBJC_CLASS_RO_$_YCFather __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0, __OFFSETOFIVAR__(struct YCFather, _name), sizeof(struct YCFather_IMPL),
0."YCFather",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_YCFather, // List of methods
0,
(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_YCFather, // List of member variables
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_YCFather, // Attribute list
};
Copy the code
- Initializations of metaclass_ _YCFather∗∗, classes ∗∗OBJC_CLASS_\_YCFather**, classes **OBJC\_CLASS\__YCFather∗∗, and classes ∗OBJC_CLASS__YCFather
extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
// Initialization of the metaclass
extern "C" __declspec(dllexport) struct _class_t OBJC_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_t OBJC_CLASS_$_NSObject;
// Class initialization
extern "C" __declspec(dllexport) struct _class_t OBJC_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,
};
Copy the code
- The relational binding of metaclasses to classes
⚠️ 关 键 词 : class, metaclass, root class
static void OBJC_CLASS_SETUP_$_YCFather(void ) {
OBJC_METACLASS_$_YCFather.isa = &OBJC_METACLASS_$_NSObject; // the isa of the YCFather metaclass is the NSObject metaclass
OBJC_METACLASS_$_YCFather.superclass = &OBJC_METACLASS_$_NSObject; The superclass of the YCFather metaclass is the NSObject metaclass
OBJC_METACLASS_$_YCFather.cache = &_objc_empty_cache;
OBJC_CLASS_$_YCFather.isa = &OBJC_METACLASS_$_YCFather; // isa of the YCFather class is the YCFather metaclass
OBJC_CLASS_$_YCFather.superclass = &OBJC_CLASS_$_NSObject; // The superclass of YCFather is NSObject
OBJC_CLASS_$_YCFather.cache = &_objc_empty_cache;
}
// add the YCSon binding
static void OBJC_CLASS_SETUP_$_YCSon(void ) {
OBJC_METACLASS_$_YCSon.isa = &OBJC_METACLASS_$_NSObject; // the isa of the YCSon metaclass is the NSObject metaclass.
OBJC_METACLASS_$_YCSon.superclass = &OBJC_METACLASS_$_YCFather; // The superclass of the YCSon metaclass is the YCSon metaclass
OBJC_METACLASS_$_YCSon.cache = &_objc_empty_cache;
OBJC_CLASS_$_YCSon.isa = &OBJC_METACLASS_$_YCSon; Isa of the YCSon class is the YCSon metaclass
OBJC_CLASS_$_YCSon.superclass = &OBJC_CLASS_$_YCFather; // The superclass of YCSon is YCFather
OBJC_CLASS_$_YCSon.cache = &_objc_empty_cache;
}
Copy the code
Unfortunately, we can’t see the structure in OBJC_METACLASS_$_NSObject visually, but we can use runtime or source code analysis to get the classic picture below. Based on the code analysis above, we can also intuitively verify most of the relationships shown below.
2. Rearrange the member variables of the property
2.1 the source code
main.m
#import <Foundation/Foundation.h>
@interface YCFather : NSObject
@property (nonatomic.copy) NSString *name;
@property (nonatomic.copy) NSString *nickName;
@property (nonatomic.assign) int age;
@property (nonatomic.assign) long height;
@property (nonatomic) char c1;
@property (nonatomic) char c2;
@end
@implementation YCFather
@end
int main(int argc, char * argv[]) {
return 0;
}
Copy the code
2.2 Clang – rewrite – objc
Executing the following code outputs a main.cpp file.
Xcrun-sdk iphoneOS clang-arch arm64-w-rewrite-objc-fobjc-arc-mios-version-min =8.0.0 -fobjc-Runtime =ios-8.0.0 main.mCopy the code
⚠️ note: the c++ code using clang-rewrite-objc here is for reference only. There are some differences with the real code, but the logic is basically the same
We can see that the order of the member variables is different from the order in which we declare the attributes.
struct YCFather_IMPL {
struct NSObject_IMPL NSObject_IVARS;
char _c1;
char _c2;
int _age;
NSString* __strong _name;
NSString* __strong _nickName;
long _height;
};
Copy the code
This is due to Apple’s memory optimization, which we can compare:
#import <objc/runtime.h>
struct NSObject_IMPL{__unsafe_unretained Class isa;
};
struct YCFather {
struct NSObject_IMPL NSObject_IVARS;
NSString* __strong _name;
NSString* __strong _nickName;
int _age;
long _height;
char _c1;
char _c2;
};
struct YCFather_IMPL {
struct NSObject_IMPL NSObject_IVARS;
char _c1;
char _c2;
int _age;
NSString* __strong _name;
NSString* __strong _nickName;
long _height;
};
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
printf("sizeof(struct YCFather): %lu\nsizeof(struct YCFather_IMPL): %lu".sizeof(struct YCFather), sizeof(struct YCFather_IMPL));
}
@end
/ / output
sizeof(struct YCFather): 48
sizeof(struct YCFather_IMPL): 40
Copy the code
You can see that the size of the rearranged structure is optimized by 48−40=848-40=848−40=8 bytes compared to the size of the unrearranged structure.
Of course, we can also verify that the actual memory layout is consistent with our analysis by debugging output:
If you found this article helpful, give me a thumbs up ~ 👍🏻