WWDC – Class optimization

By introducing the objective-C low-level optimization process, we can understand the composition of class and DirtyMemory and CleanMemory in runtime. In essence, iOS does not have a swap method to manage DirtyMemory, so try to save unchanged class data to CleanMemory.

CleanMemory and DirtyMemory

Dirty memory is much more expensive than clean memory, and it must exist for as long as it runs. By separating out data that will not change, most of the class data can be stored in clean memory, which Is what Apple is pursuing.

Clean Memory

  1. clean memoryMemory that does not change after loading
  2. class_ro_tBelong toclean memoryBecause it’s read-only and it doesn’t align with memory
  3. clean memoryIt can be removed to save more memory if you need itclean memory, the system can be reloaded from disk

Dirty Memory

  1. dirty memory Memory that changes while the process is running
  2. The class structure becomes as soon as it is useddirty memoryBecause the runtime writes new data to it. Such as creating a new method cache and pointing to it from the class, initializing class-related subclasses and superclasses
  3. dirty memoryIs the main reason that class data is split into two parts

Runtime Memory changes and optimizations for the class

  1. In the beginning inappIs present in the binary codedirty memoryisMYClassUser definedclass, and generate the classclean memorytheclass_ro_t.
  2. When this class MYClass is first used, the runtime assigns it an extradirty memorytheclass_rw_tFor read-write data (The focus is on observing runtime status through this data result)
  3. But in this WWDC optimization process put thisclass_rw_tThe frequently moving parts are isolated from each otherclass_rw_ext_t, leaving only the point to the class.

Old structure drawing:

Optimized structure diagram:

Class_rw_t data structure:

1.First Subclass, Next Subling Class: Contains information that will only be generated at runtime.First Subclass, Next Subling Class, all classes will become a tree structure. This is done through the First Subclass and Next Subling Class Pointers, which allow the runtime to iterate over all classes currently in use

Class_rw_ext_t data structure:

Methods, Properties, protocols: these are included because they can be modified at runtime. When the category is loaded, it can add new Methods to the class or add them via the runtime API. This is a field only used by Swift, because the entire data structure OC is shared with Swift, but the Swift class itself does not need this field, it is used for someone to access the OC name of Swfit, which has low utilization.

classDiagram

MYClass --|> class_rw_t:DirtyMemory
class_rw_t --|> class_rw_ext_t:DirtyMemory
class_rw_ext_t --|> class_ro_t:CleanMemory

class MYClass{
Metaclass
SuperClass
Flags
Method cache
}
class class_rw_t{
Flags
First Subclass
Next Sibling Class
}
class class_rw_ext_t{
Methods
Properties
Protocols
Demangled Name
}
class class_ro_t{
Flags
Size
Name
Mthods
Protocols
Ivars
Properties
}

Class structure encoding

SELandIMPRelationship between

  1. SEL: Method Number
  2. IMP: address of function pointer
  3. SELEquivalent to a book’s catalog name
  4. IMPEquivalent to the pages of a book

Find method document

To get the Type encoding diagram, open xcode–> Command + Shift +0–> search ivar_getTypeEncoding–> Click Type Encodings

Official code table

Code Meaning
c A char
i An int
s A short
l A long, is treated as a 32-bit quantity on 64-bit programs.
q A long long
c An unsigned char
I An unsigned int
S An unsigned short
L An unsigned long
Q An unsigned long long
f A float
d A double
B A C++ bool or a C99 _Bool
v A void
* A character string (char *)
@ An object (whether statically typed or typed id)
# A class object (Class)
: A method selector (SEL)
[array type] An array
{name=type… } A structure
(name=type…) A union
bnum A bit field of num bits
^type A pointer to type
? An unknown type (among other things, this code is used for function pointers)

Verification code:

#import <Foundation/ foundation. h> #import <objc/runtime.h> #pragma mark - %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)); An 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))); } int main(int argc, const char * argv[]) { @autoreleasepool { // objc_object VS objc_class // person VS NSObject // id objc_object * // class objc_class * lgTypes(); } return 0; }Copy the code

LLVM print:

2021-07-01 01:03:52.622628+0800 001- Class attributes and variables [920333:3820537] char --> c 2021-07-01 01:03:52.623543+0800 [92033:3820537] int --> I 2021-07-01 01:03:52.623621+0800 001 -- short --> s 2021-07-01 01:03:52.623666+0800 001- Class Attributes and variables [92033:3820537] Long --> Q 2021-07-01 01:03:52.623698+0800 001- Class attributes and variables [92033:3820537] long --> Q 2021-07-01 01:03:52.623698+0800 001- Class attributes and variables [92033:3820537] Long long --> q 2021-07-01 01:03:52.623817+0800 001 -- Class attributes and variables [92033:3820537] unsigned char --> C 2021-07-01 [92021-07-01] unsigned int --> I 2021-07-01 01:03:52.623939+0800 - Class attributes and variables [92033:3820537] unsigned int --> I 2021-07-01 01:03:52.623939+0800 Unsigned short --> S 2021-07-01 01:03:52.623995+0800 001- Unsigned long [92033:3820537] unsigned short --> S 2021-07-01 01:03:52.623995+0800 001- Unsigned long [92033:3820537] unsigned short --> S 2021-07-01 01:03:52.623995+0800 001 --> Q 2021-07-01 01:03:52.624076+0800 001- class attributes and variables [92033:3820537] float --> f 2021-07-01 01:03:52.624146+0800 [92033:3820537] bool --> B 2021-07-01 01:03:52.624191+0800 001 -- void --> V 2021-07-01 Char * --> * 2021-07-01 01:03:52.631074+0800 001- Class char * --> * 2021-07-01 01:03:52.631074+0800 001- Class char * --> * 2021-07-01 01:03:52.631074+0800 001- class char * --> * 2021-07-01 01:03:3820537 Id --> @2021-07-01 01:03:52.631115+0800 001 -- Class attributes and variables [92033:3820537] Class --> # 2021-07-01 01:03:52.631153+0800 001- Class properties and variables [92033:3820537] SEL --> 2021-07-01 01:03:52.631189+0800 001- Class attributes and variables [92033:3820537] int[] --> [3I] 2021-07-01 01:03:52.631226+0800 Struct -> {person=* I} 2021-07-01 01:03:52.631262+0800 001- struct -> {person=* I} 2021-07-01 01:03:52.631262+0800 001- [92033:3820537] union -> (union_type=* I) 2021-07-01 01:03:52.631301+0800 001- Class attributes and variables [92033:3820537] int[] --> ^ ICopy the code

Method code symbol

Define a LGPerson class with custom attributes and member variables, In the terminal, use xcrun – SDK iphonesimulator clang -arch arm64-rewrite-objc main.m -o main-arm64. CPP to generate.

The source code:

#import <Foundation/Foundation.h> #import <objc/runtime.h> @interface LGPerson : NSObject { // STRING int double float char bool NSString *hobby; // string int a; NSObject *objc; } @property (nonatomic, copy) NSString *nickName; @property (atomic, copy) NSString *acnickName; @property (nonatomic) NSString *nnickName; @property (atomic) NSString *anickName; @property (nonatomic, strong) NSString *name; @property (atomic, strong) NSString *aname; @end @implementation LGPerson @end int main(int argc, const char * argv[]) { @autoreleasepool { LGPerson *person = [LGPerson alloc]; person.name = @"KC"; // setName -> objc_setPropety NSLog(@"Hello, World!" ); } return 0; }Copy the code

Generate main. CPP part of the source code:

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[24];
} _OBJC_$_INSTANCE_METHODS_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	24,
	{{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_LGPerson_nickName},
	{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},
	{(struct objc_selector *)"acnickName", "@16@0:8", (void *)_I_LGPerson_acnickName},
	{(struct objc_selector *)"setAcnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAcnickName_},
	{(struct objc_selector *)"nnickName", "@16@0:8", (void *)_I_LGPerson_nnickName},
	{(struct objc_selector *)"setNnickName:", "v24@0:8@16", (void *)_I_LGPerson_setNnickName_},
	{(struct objc_selector *)"anickName", "@16@0:8", (void *)_I_LGPerson_anickName},
	{(struct objc_selector *)"setAnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAnickName_},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_},
	{(struct objc_selector *)"aname", "@16@0:8", (void *)_I_LGPerson_aname},
	{(struct objc_selector *)"setAname:", "v24@0:8@16", (void *)_I_LGPerson_setAname_},
	{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_LGPerson_nickName},
	{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},
	{(struct objc_selector *)"acnickName", "@16@0:8", (void *)_I_LGPerson_acnickName},
	{(struct objc_selector *)"setAcnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAcnickName_},
	{(struct objc_selector *)"nnickName", "@16@0:8", (void *)_I_LGPerson_nnickName},
	{(struct objc_selector *)"setNnickName:", "v24@0:8@16", (void *)_I_LGPerson_setNnickName_},
	{(struct objc_selector *)"anickName", "@16@0:8", (void *)_I_LGPerson_anickName},
	{(struct objc_selector *)"setAnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAnickName_},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_},
	{(struct objc_selector *)"aname", "@16@0:8", (void *)_I_LGPerson_aname},
	{(struct objc_selector *)"setAname:", "v24@0:8@16", (void *)_I_LGPerson_setAname_}}
};
Copy the code

findnickName:"@16@0:8"Do explain

@ @ 0:8 16

  1. @ : id
  2. 16: Occupies memory
  3. @ : id
  4. 0: Starts from position 0
  5. : : SEL
  6. 8: Start from position 8

Member and instance variables

Member variable: int double float char bool Base type

Instance variables: structure, object

Graph TD setName --> objc_setPropet --> Memory assignment

SEL: pointer to a class member method, but different from the function pointer in C, the function pointer directly stores the address of the method, but SEL is only the method number.

IMP: a function pointer that holds the address of the method

Ivar = = member variable

Code:

@interface LGPerson : NSObject { // STRING int double float char bool NSString *hobby; // string int a; NSObject *objc; } @property (nonatomic, copy) NSString *nickName; @property (atomic, copy) NSString *acnickName; @property (nonatomic) NSString *nnickName; @property (atomic) NSString *anickName; @property (nonatomic, strong) NSString *name; @property (atomic, strong) NSString *aname; @end @implementation LGPerson @end @interface LGTeacher : NSObject @end @implementation LGTeacher @end int main(int argc, const char * argv[]) { @autoreleasepool { // objc_object VS objc_class // person VS NSObject // id objc_object * // class objc_class * LGPerson *person = [LGPerson alloc]; person.name = @"KC"; // setName -> objc_setPropety LGTeacher *teacher = [LGTeacher alloc]; // Class tClass = object_getClass(teacher); Class pClass = object_getClass(person); lgTypes(); lgObjc_copyIvar_copyProperies(pClass); // ivar_getTypeEncoding() NSLog(@"Hello, World!" ); } return 0; }Copy the code

c++:

static NSString * _I_LGPerson_nickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nickName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_LGPerson_setNickName_(LGPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _nickName), (id)nickName, 0, 1); }

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

static NSString * _I_LGPerson_acnickName(LGPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _acnickName), 1); }
static void _I_LGPerson_setAcnickName_(LGPerson * self, SEL _cmd, NSString *acnickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _acnickName), (id)acnickName, 1, 1); }

static NSString * _I_LGPerson_nnickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nnickName)); }
static void _I_LGPerson_setNnickName_(LGPerson * self, SEL _cmd, NSString *nnickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nnickName)) = nnickName; }

static NSString * _I_LGPerson_anickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_anickName)); }
static void _I_LGPerson_setAnickName_(LGPerson * self, SEL _cmd, NSString *anickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_anickName)) = anickName; }

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; }

static NSString * _I_LGPerson_aname(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_aname)); }
static void _I_LGPerson_setAname_(LGPerson * self, SEL _cmd, NSString *aname) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_aname)) = aname; }
Copy the code

Copy focuses on memory translation or objc_getPropety

The source code:

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { bool copy = (shouldCopy && shouldCopy ! = MUTABLE_COPY); bool mutableCopy = (shouldCopy == MUTABLE_COPY); reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy); } void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) { reallySetProperty(self, _cmd, newValue, offset, true, false, false); } void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) { reallySetProperty(self, _cmd, newValue, offset, false, false, false); } void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) { reallySetProperty(self, _cmd, newValue, offset, true, true, false); } void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) { reallySetProperty(self, _cmd, newValue, offset, false, true, false); }Copy the code
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) { if (offset == 0) { object_setClass(self, newValue); return; } id oldValue; id *slot = (id*) ((char*)self + offset); if (copy) { newValue = [newValue copyWithZone:nil]; } else if (mutableCopy) { newValue = [newValue mutableCopyWithZone:nil]; } else { if (*slot == newValue) return; newValue = objc_retain(newValue); } if (! atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); } objc_release(oldValue); }Copy the code

 LGPerson *p1 = [[LGPerson alloc] init];
        NSLog(@"%@", p1);
Copy the code
P / 2021-06-18 20:37:27. 200345 + 0800 KCObjcBuild [37943-460748] < LGPerson: 0x100780eb0> KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available. (lldb) p/x LGPerson.class (Class) $0 = 0x0000000100008380 LGPerson (lldb) p (class_data_bits_t *)0x00000001000083a0 (class_data_bits_t *) $1 = 0x00000001000083a0 (lldb) p $1->data() (class_rw_t *)  $2 = 0x0000000100781510 (lldb) p *$2 (class_rw_t) $3 = { flags = 2148007936 witness = 1 ro_or_rw_ext = { std::__1::atomic<unsigned long> = { Value = 4295000344 } } firstSubclass = nil nextSiblingClass = NSUUID } (lldb) px LGTeacher.class error: 'px' is not a valid command. (lldb) p/x LGTeacher.class (Class) $4 = 0x0000000100008330 LGTeacher (lldb) p $1->data() (class_rw_t *) $5 = 0x0000000100781510 (lldb) p *$5 (class_rw_t) $6 = { flags = 2148007936 witness = 1 ro_or_rw_ext = { std::__1::atomic<unsigned long> = { Value = 4295000344 } } firstSubclass = LGTeacher nextSiblingClass = NSUUID } (lldb)Copy the code

There are no class methods at the bottom, only object methods; Class methods are in metaclasses

Attribute low-levelsetterwithgetterMethod implementation mode

Verify whether the underlying property is a method (objc_setProperty, objc_getProperty) or a memory offset

Objc_setProperty is a mid-tier code. In order not to create a separate implementation method underneath for each property when using setter methods, when a copy property is detected on a property object, This object’s setter methods are redirected to objc_setProperty(), and the property object’s setter methods can be implemented simply by implementing setProperty methods underneath.

Define a LGPerson class, add custom attributes and member variables for various modifiers (weak, strong, weak, nonatomic, atomic), Xcrun-sdk iphonesimulator clang -arch arm64-rewrite-objc main.m -o main-arm64. CPP generates the main. CPP file.

Objc code:

#import <Foundation/Foundation.h> #import <objc/runtime.h> @interface LGPerson : NSObject { // STRING int double float char bool NSString *hobby; // string int a; NSObject *objc; } @property (nonatomic, copy) NSString *nickName; @property (atomic, copy) NSString *acnickName; @property (nonatomic) NSString *nnickName; @property (atomic) NSString *anickName; @property (nonatomic, strong) NSString *name; @property (atomic, strong) NSString *aname; @property (nonatomic,assign) NSInteger height; @property (atomic,assign) NSInteger weight; @end @implementation LGPerson @end @interface LGTeacher : NSObject @end @implementation LGTeacher @end int main(int argc, const char * argv[]) { @autoreleasepool { // objc_object VS objc_class // person VS NSObject // id objc_object * // class objc_class * LGPerson *person = [LGPerson alloc]; person.name = @"KC"; // setName -> objc_setPropety LGTeacher *teacher = [LGTeacher alloc]; // Class tClass = object_getClass(teacher); Class pClass = object_getClass(person); NSLog(@"Hello, World!" ); } return 0; }Copy the code

Generate some c++ code:

static NSString * _I_LGPerson_nickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nickName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_LGPerson_setNickName_(LGPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _nickName), (id)nickName, 0, 1); }

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

static NSString * _I_LGPerson_acnickName(LGPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _acnickName), 1); }
static void _I_LGPerson_setAcnickName_(LGPerson * self, SEL _cmd, NSString *acnickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _acnickName), (id)acnickName, 1, 1); }

static NSString * _I_LGPerson_nnickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nnickName)); }
static void _I_LGPerson_setNnickName_(LGPerson * self, SEL _cmd, NSString *nnickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nnickName)) = nnickName; }

static NSString * _I_LGPerson_anickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_anickName)); }
static void _I_LGPerson_setAnickName_(LGPerson * self, SEL _cmd, NSString *anickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_anickName)) = anickName; }

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; }

static NSString * _I_LGPerson_aname(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_aname)); }
static void _I_LGPerson_setAname_(LGPerson * self, SEL _cmd, NSString *aname) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_aname)) = aname; }

static NSInteger _I_LGPerson_height(LGPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_height)); }
static void _I_LGPerson_setHeight_(LGPerson * self, SEL _cmd, NSInteger height) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_height)) = height; }

static NSInteger _I_LGPerson_weight(LGPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_weight)); }
static void _I_LGPerson_setWeight_(LGPerson * self, SEL _cmd, NSInteger weight) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_weight)) = weight; }
Copy the code

C + + image:

Conclusion:

  1. Properties of thesetterCan be implemented in two ways:objc_setPropertyandMemory migration
  2. Properties of thegetterMethods can also be implemented in two ways:objc_getPropertyandMemory migration
  3. Property object existscopyProperty, will place this objectsetter,getterMethod redirects toobjc_setProperty(),objc_getProperty()
  4. If the declared object does not set properties other than how initial atomicity, what is the default propertystrong, will not triggerobjc_setProperty,objc_getProperty

Analysis of theobjc_setProperty,objc_getPropertyThe bottom of the

Objc4.8 various types of objc_setProperty source code:

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { bool copy = (shouldCopy && shouldCopy ! = MUTABLE_COPY); bool mutableCopy = (shouldCopy == MUTABLE_COPY); reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy); } void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) { reallySetProperty(self, _cmd, newValue, offset, true, false, false); } void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) { reallySetProperty(self, _cmd, newValue, offset, false, false, false); } void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) { reallySetProperty(self, _cmd, newValue, offset, true, true, false); } void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) { reallySetProperty(self, _cmd, newValue, offset, false, true, false); }Copy the code

Objc4.8 objc_getProperty reallySetProperty source code:

id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { if (offset == 0) { return object_getClass(self); } // Retain release world id *slot = (id*) ((char*)self + offset); if (! atomic) return *slot; // Atomic retain release world spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); id value = objc_retain(*slot); slotlock.unlock(); // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock. return objc_autoreleaseReturnValue(value); } static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) { if (offset == 0) { object_setClass(self, newValue); return; } id oldValue; id *slot = (id*) ((char*)self + offset); if (copy) { newValue = [newValue copyWithZone:nil]; } else if (mutableCopy) { newValue = [newValue mutableCopyWithZone:nil]; } else { if (*slot == newValue) return; newValue = objc_retain(newValue); } if (! atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); } objc_release(oldValue); }Copy the code

Conclusion:

  1. Whatever modifier you usesettermethodsobjc_setProperty,objc_setProperty_atomicWait a minute. It all ends up being calledreallySetProperty
  2. objc_getPropertyThere is only one type method

LLVM validates setter and getter methods when the object property is copy

LLVM source verification process:

  1. objc_setProperty -> getSetPropertyFnGetPropertySetFunction -> PropertyImplStrategy -> IsCopy (judgment)
  2. objc_getProperty -> getGetPropertyFnGetPropertyGetFunction -> PropertyImplStrategy -> IsCopy (judgment)

Objc_setProperty and objc_getProperty:

getSetPropertyFnwithgetGetPropertyFnGraph:

GetPropertySetFunction and GetPropertyGetFunction diagram:

GetPropertySetFunctionCalled diagram:

GetPropertyGetFunctionCalled diagram:

PropertyImplStrategyGraph:

supplement

isKindOfClassandisMemberOfClass

  1. Check the official documentation to find outisKindOfClassReturns YES if the method caller is an instance object of the passed class, or if the caller is an instance object of a class in the successor chain of the passed class.
  2. isMemberOfClassThe method caller must be an instance object of the passed class to return YES.

Explore isKindOfClass and isMemberOfClass through an example

Code:

#import <Foundation/Foundation.h>

@interface LGPerson : NSObject

@end

@implementation LGPerson

@end

void lgKindofDemo(void){
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];     
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];   
    BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];     
    BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];   
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];     
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];   
    BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];     
    BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];   
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}

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

Copy the code

LLVM print:

2021-07-03 00:11:28.157280+0800 KCObjcBuild[11864:4389325] RE1:1 RE2:0 RE3:0 RE4:0 2021-07-03 00:11:28.157470+0800 KCObjcBuild[11864:4389325] re5 :1 re6 :1 re7 :1 re8 :1Copy the code

Assembly debugging method diagram:

Conclusion:

Objc_opt_isKindOfClass is called by the isKindOfClass method. Hook isMemberOfClass method to call objC_opt_class. The next step is to analyze the objc_opt_isKindOfClass, isKindOfClass, objC_Opt_class, and isMemberOfClass methods.

objc_opt_isKindOfClassThe underlying principle

Objc-internal. h supports masOS > 10.15 and iOS > 13.0

OBJC_EXPORT BOOL
objc_opt_isKindOfClass(id _Nullable obj, Class _Nullable cls)
	OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);

Copy the code

Concrete implementation:

// Calls [obj isKindOfClass] BOOL objc_opt_isKindOfClass(id obj, Class otherClass) {#if __OBJC2__ // now use OBJC2 version // slowPath (! Obj) if obj is empty, the low probability event will not occur. obj)) return NO; Class CLS = obj->getIsa(); //fastpath(! CLS ->hasCustomCore()) (class or parent has no default isKindOfClass method) if (fastPath (! cls->hasCustomCore())) { for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) { if (tcls == otherClass) return YES; } return NO; Return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass); }Copy the code

Conclusion:

Obj ->getIsa() : obj->getIsa() : obj->getIsa() : obj->getIsa() : obj->getIsa() : obj->getIsa() : obj->getIsa();

isKindOfClassThe underlying principle

Concrete implementation:

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

Copy the code

Conclusion:

  1. + isKindOfClassProcess.Class of metaclass vs cls(classes that need to be compared), different continue to compare.The parent of a metaclass vs clsContinue to compare until foundA metaclass.A metaclass vs cls, different continue to compare.Root class (NSObject) vs cls, if not the sameRoot class (NSObject)The parent classnilAnd returns out of the loopNO
  2. - isKindOfClassProcess. Gets the class of the current object,class vs cls, different continue to compare.The parent of the class vs clsContinue to compare until foundRoot class (NSObject).Root class (NSObject) vs cls, if not the sameThe root class, NSObjectThe parent class of) isnilAnd returns out of the loopNO

objc_opt_classThe underlying principle

Objc-internal. h supports masOS > 10.15 and iOS > 13.0

OBJC_EXPORT Class _Nullable objC_opt_class (ID _Nullable obj) OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);Copy the code

Concrete implementation:

// Calls [obj class] Class objc_opt_class(id obj) { #if __OBJC2__ if (slowpath(! obj)) return nil; Class CLS = obj->getIsa(); // if (fastPath (! CLS ->hasCustomCore())) {CLS ->hasCustomCore())) {CLS ->isMetaClass(); obj : cls; } #endif return ((Class(*)(id, SEL))objc_msgSend)(obj, @selector(class)); }Copy the code

Conclusion:

The implementation of objc_opt_class is to get the class and return it if the argument is an object or a class

isMemberOfClassThe underlying principle

Concrete implementation:

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

Copy the code

Conclusion:

  1. + isMemberOfClassProcess.Class of metaclass vs cls(classes to be compared), return if they are the sameYESOtherwise returnNO
  2. - isMemberOfClassProcess.class vs cls(classes to be compared), return if they are the sameYESOtherwise returnNO