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,_ageThese inside curly braces are called member variables;
  • Instance variables: like_hobby,_objThese belong toNSObjectClass or inherited fromNSObjectClasses are also called instance variables;

attribute

  • likenickName,name,careerThese are@propertyThose 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,_ageIt’s just pure at the bottomMember variables, no other;
  • attributenickName,name,careerIs compiled to tape at the bottom level_Member variables and the correspondinggetter,setterMethods.

We also found some problems:

  • nickNameandcareerthesetterThe method is usedobjc_setPropertyFunction assignment;
  • namethesetterThe method was usedself + OBJC_IVAR_$_SSLPerson$_nameAssign 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
    1. @ : idType;
    2. 16: Occupies memory.
    3. @ : idType parameters;
    4. 0From:0At the beginning of;
    5. : : SEL
    6. 8From:8Start at # and push back8 plus 8 is 16;
  • v24@0:8@16
    1. v : void;
    2. 24: Occupies memory.
    3. @: The first parameter isidType;
    4. 0From:0At the beginning of;
    5. : : SEL
    6. 8From:8At the beginning of;
    7. @: The second parameter isidType;
    8. 16From:16Start 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 functionsetPropertyFn = CGM.getObjCRuntime().GetPropertySetFunction();;
  • Can you call this code fromstrategy.getKind()To determine, which is to sayPropertyImplStrategyType.

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
    • aNameusenonatomic,copyDecorate, useobjc_setPropertyThe assignment.
    • bNameuseatomic,copyDecorate, useobjc_setPropertyThe assignment.
    • cNameanddNameThere is nocopyDecoration 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
    • bNameuseatomic,copyDecorate, useobjc_getPropertyThe values.
    • aName,cNameanddNameNo simultaneous useatomic,copyModified to use a memory translation value.
  • The results are consistent with our conclusions.

conclusion

  • useretainorcopyWhen decorating a property, useobjc_setPropertyOtherwise, use a memory translation to assign.
  • The use ofnonatomicModify, and useretainorcopyWhen decorating a property, useobjc_getPropertyValue; otherwise, use memory translation.