OC class principle exploration series articles
- OC class principle exploration: Class structure analysis
- OC class principle exploration: Class structure analysis supplement
- OC class principles exploration: the underlying principles of attributes
Member variables and attributes
Create SSLPerson class in main.m:
@interface SSLPerson : NSObject {
NSString *_hobby;
NSObject *_obj;
int _age;
}
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;
@property (atomic, copy) NSString *career;
@end
Copy the code
Member variables and instance variables
- Member variables: like
_hobby
,_obj
,_age
These inside curly braces are called member variables; - Instance variables: like
_hobby
,_obj
These belong toNSObject
Class or inherited fromNSObject
Classes are also called instance variables;
attribute
- like
nickName
,name
,career
These are@property
Those decorated are called attributes.
Gets the properties and member variables of the class
The following method prints all the member variables and attributes of a class:
#ifdef DEBUG #define SSLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]); #else #define SSLog(format, ...) ; #endif void logObjc_copyIvar_copyProperies(Class pClass){ unsigned int count = 0; Ivar *ivars = class_copyIvarList(pClass, &count); for (unsigned int i=0; i < count; i++) { Ivar const ivar = ivars[i]; Const char*cName = ivar_getName(ivar); // Const char*cName = ivar_getName(ivar); NSString *ivarName = [NSString stringWithUTF8String:cName]; SSLog(@"class_copyIvarList:%@",ivarName); } free(ivars); unsigned int pCount = 0; objc_property_t *properties = class_copyPropertyList(pClass, &pCount); for (unsigned int i=0; i < pCount; i++) { objc_property_t const property = properties[i]; / / get nsstrings * propertyName attribute name = [nsstrings stringWithUTF8String: property_getName (property)]; SSLog(@"class_copyProperiesList:%@",propertyName); } free(properties); }Copy the code
The difference between member variables and attributes
Clang -rewrite-objc main.m -o main. CPP compiles main.m to obtain main. CPP:
extern "C" unsigned long OBJC_IVAR_$_SSLPerson$_nickName;
extern "C" unsigned long OBJC_IVAR_$_SSLPerson$_name;
struct SSLPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_hobby;
NSObject *_obj;
int _age;
NSString *_nickName;
NSString *_name;
NSString *_career;
};
// @property (nonatomic, copy) NSString *nickName;
// @property (nonatomic, strong) NSString *name;
// @property (atomic, copy) NSString *career;
static NSString * _I_SSLPerson_nickName(SSLPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_nickName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_SSLPerson_setNickName_(SSLPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _nickName), (id)nickName, 0, 1); }
static NSString * _I_SSLPerson_name(SSLPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_name)); }
static void _I_SSLPerson_setName_(SSLPerson * self, SEL _cmd, NSString *name) { (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_name)) = name; }
extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long, bool);
static NSString * _I_SSLPerson_career(SSLPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _career), 1); }
static void _I_SSLPerson_setCareer_(SSLPerson * self, SEL _cmd, NSString *career) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _career), (id)career, 1, 1); }
Copy the code
- Member variables
_hobby
,_obj
,_age
It’s just pure at the bottomMember variables
, no other; - attribute
nickName
,name
,career
Is compiled to tape at the bottom level_
Member variables and the correspondinggetter
,setter
Methods.
We also found some problems:
nickName
andcareer
thesetter
The method is usedobjc_setProperty
Function assignment;name
thesetter
The method was usedself + OBJC_IVAR_$_SSLPerson$_name
Assign the value by way of memory translation;- We also saw some type encodings
{"nickName", "@16@0:8", _I_SSLPerson_nickName}
Related things, what do they represent?
Let’s tackle these issues.
Second, the coding
Official type code table
To find this, run CMD + shift + 0 -> documentation -> search for ivar_getTypeEncoding -> Type Encodings -> official encoding address.
Code gets the type encoding
void sslTypes(void){ 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)); 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))); }Copy the code
Actual analysis of type encoding
Let’s open the main. CPP file and analyze it with the following code:
{{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_SSLPerson_nickName},
{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_SSLPerson_setNickName_},
Copy the code
@ @ 0:8 16
@
:id
Type;16
: Occupies memory.@
:id
Type parameters;0
From:0
At the beginning of;:
:SEL
;8
From:8
Start at # and push back8 plus 8 is 16
;
v24@0:8@16
v
:void
;24
: Occupies memory.@
: The first parameter isid
Type;0
From:0
At the beginning of;:
:SEL
;8
From:8
At the beginning of;@
: The second parameter isid
Type;16
From:16
Start at # and push back16 plus 8 is 24
;
LLVM explores the attribute method
LLVM explore objc_setProperty
We’ll use LLVM to explore what happens when property assignment uses objc_setProperty. Download the LLVM source Code and open it with Visual Studio Code.
Our exploration idea is to use the backward method, first find the creation of the objc_setProperty function -> find where the objc_setProperty function is called -> find why or what conditions to call the function -> explore the final answer.
If you search for objc_setProperty in VS, you’ll see a lot of code, and you’ll find the following code:
Found the function of creating CGM. CreateRuntimeFunction (FTy effects, “objc_setProperty”), then we see what place call getSetPropertyFn.
Search for getSetPropertyFn to find where this function was called:
GetPropertySetFunction() is the middle layer code that we use to continue our search:
- We find the call to the function
setPropertyFn = CGM.getObjCRuntime().GetPropertySetFunction();
; - Can you call this code from
strategy.getKind()
To determine, which is to sayPropertyImplStrategy
Type.
Let’s first look at the definition of PropertyImplStrategy:
Via annotations can be found StrategyKind objc_setProperty for GetSetProperty and SetPropertyAndExpressionGet calls, then we go to initialize method to find the kind of assignment.
Find the key method IsCopy when Kind = GetSetProperty; So if the property is copy, assign it with objc_setProperty, otherwise assign it with a memory shift.
Another way to explore:
Conclusion: When the modifier is copy or retain, use objc_setProperty.
Verify this:
Define the SSLPerson class:
@interface SSLPerson : NSObject
@property (nonatomic, copy) NSString *aName;
@property (atomic, copy) NSString *bName;
@property (nonatomic, strong) NSString *cName;
@property (atomic, strong) NSString *dName;
@end
Copy the code
Clang -rewrite-objc main.m -o main. CPP
static NSString * _I_SSLPerson_aName(SSLPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_aName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_SSLPerson_setAName_(SSLPerson * self, SEL _cmd, NSString *aName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _aName), (id)aName, 0, 1); }
extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long, bool);
static NSString * _I_SSLPerson_bName(SSLPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _bName), 1); }
static void _I_SSLPerson_setBName_(SSLPerson * self, SEL _cmd, NSString *bName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _bName), (id)bName, 1, 1); }
static NSString * _I_SSLPerson_cName(SSLPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_cName)); }
static void _I_SSLPerson_setCName_(SSLPerson * self, SEL _cmd, NSString *cName) { (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_cName)) = cName; }
static NSString * _I_SSLPerson_dName(SSLPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_dName)); }
static void _I_SSLPerson_setDName_(SSLPerson * self, SEL _cmd, NSString *dName) { (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_dName)) = dName; }
Copy the code
- results
aName
usenonatomic
,copy
Decorate, useobjc_setProperty
The assignment.bName
useatomic
,copy
Decorate, useobjc_setProperty
The assignment.cName
anddName
There is nocopy
Decoration to use memory translation assignment.
- The results are consistent with our conclusions.
LLVM explore objc_getProperty
Objc_getProperty is explored in the second way, again using LLVM.
This implementation was found in the search for objc_getProperty:
This is a concatenation, similar to the c++ code we compile:
Complete code:
void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, ObjCImplementationDecl *IMD, ObjCCategoryImplDecl *CID) { // ... unsigned Attributes = PD->getPropertyAttributes(); if (mustSynthesizeSetterGetterMethod(IMD, PD, True /*getter*/) {// Make objc_getProperty (bool GenGetProperty = // is not nonatomic and is modified with retain or copy! (Attributes & ObjCPropertyAttribute::kind_nonatomic) && (Attributes & (ObjCPropertyAttribute::kind_retain | ObjCPropertyAttribute::kind_copy)); std::string Getr; / /... Getr += "{ "; // Synthesize an explicit cast to gain access to the ivar. // See objc-act.c:objc_synthesize_new_getter() for details. If (GenGetProperty) {return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) // The return type _TYPE is omitted... Getr += "return (_TYPE)"; Getr += "objc_getProperty(self, _cmd, "; RewriteIvarOffsetComputation(OID, Getr); Getr += ", 1)"; } else Getr += "return "+ getIvarAccessString(OID); Getr += "; } "; InsertText(startGetterSetterLoc, Getr); } if (PD->isReadOnly() || ! mustSynthesizeSetterGetterMethod(IMD, PD, false /*setter*/)) return; // Generate the 'setter' function. std::string Setr; / / determine whether generates setProperty bool GenSetProperty = / / retain or copy modify Attributes & (ObjCPropertyAttribute: : kind_retain | ObjCPropertyAttribute::kind_copy); / /... Setr += "{ "; // Synthesize an explicit cast to initialize the ivar. // See objc-act.c:objc_synthesize_new_setter() for details. if (GenSetProperty) {// use objc_setProperty Setr += "objc_setProperty (self, _cmd, "; RewriteIvarOffsetComputation(OID, Setr); Setr += ", (id)"; Setr += PD->getName(); Setr += ", "; If (the Attributes & ObjCPropertyAttribute: : kind_nonatomic) / / whether nonatomic Setr + = "0,"; else Setr += "1, "; If (the Attributes & ObjCPropertyAttribute: : kind_copy) / / whether the copy Setr + = "1)"; else Setr += "0)"; } else {Setr += getIvarAccessString(OID) + "= "; Setr += PD->getName(); } Setr += "; }\n"; InsertText(startGetterSetterLoc, Setr); }Copy the code
Look at the key code:
We conclude that objc_getProperty is used when it is not nonatomic and is decorated with retain or copy.
Verify this:
Define the SSLPerson class:
@interface SSLPerson : NSObject
@property (nonatomic, copy) NSString *aName;
@property (atomic, copy) NSString *bName;
@property (nonatomic, strong) NSString *cName;
@property (atomic, strong) NSString *dName;
@end
Copy the code
Clang -rewrite-objc main.m -o main. CPP
static NSString * _I_SSLPerson_aName(SSLPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_aName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_SSLPerson_setAName_(SSLPerson * self, SEL _cmd, NSString *aName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _aName), (id)aName, 0, 1); }
extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long, bool);
static NSString * _I_SSLPerson_bName(SSLPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _bName), 1); }
static void _I_SSLPerson_setBName_(SSLPerson * self, SEL _cmd, NSString *bName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SSLPerson, _bName), (id)bName, 1, 1); }
static NSString * _I_SSLPerson_cName(SSLPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_cName)); }
static void _I_SSLPerson_setCName_(SSLPerson * self, SEL _cmd, NSString *cName) { (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_cName)) = cName; }
static NSString * _I_SSLPerson_dName(SSLPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_dName)); }
static void _I_SSLPerson_setDName_(SSLPerson * self, SEL _cmd, NSString *dName) { (*(NSString **)((char *)self + OBJC_IVAR_$_SSLPerson$_dName)) = dName; }
Copy the code
- results
bName
useatomic
,copy
Decorate, useobjc_getProperty
The values.aName
,cName
anddName
No simultaneous useatomic
,copy
Modified to use a memory translation value.
- The results are consistent with our conclusions.
conclusion
- use
retain
orcopy
When decorating a property, useobjc_setProperty
Otherwise, use a memory translation to assign. - The use of
nonatomic
Modify, and useretain
orcopy
When decorating a property, useobjc_getProperty
Value; otherwise, use memory translation.