preface
In the last article, class exploration and analysis (PART 1), we made a preliminary exploration of the class, got the bitmap of ISA, and also got the attributes and methods of the class. In isa bitmaps we mentioned metaclasses, so why metaclasses, and what is the difference between an attribute and an instance variable? What if we have @v: in the property? This article will continue to explore the class from these aspects.
A,WWDC2020
WWDC2020
In, we have a new understanding of the class:
Changes to loading classes from memory:
The loading process from memory is shown below:
clean Memory
和 Dirty Memory
clean Memory
Is memory that does not change after loading.clean Memory
It can be removed to save more space. Because if you need toclean Memory
Data can be loaded from memory.Dirty Memory
Is memory that has changed at runtime. Once used, the class becomesDirty Memory
Because the runtime writes new data to it.For example, create a method cache and point to it from the class.Dirty Memory
thanclean Memory
expensiveAs long as the process is running, it will always exist.
Second,attribute
andMember variables
- We first in
main.m
To define aWSPerson
Class, and then useclang
Compile amain.cpp
File:
// main.m
@interface WSPerson : NSObject {
// Member variables
NSString *wsSubject;
int wsAge;
// WSTeacher *teacher; The instance variables
}
/ / property
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSString *nickName;
@end
@implementation WSPerson
// main.cpp
struct WSPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *wsSubject;
int wsAge;
NSString *_name;
NSString *_nickName;
};
// name get
static NSString * _I_WSPerson_name(WSPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool.bool);
// name set
static void _I_WSPerson_setName_(WSPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct WSPerson, _name), (id)name, 0.1); }
// nickName get
static NSString * _I_WSPerson_nickName(WSPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_nickName)); }
// nickName get
static void _I_WSPerson_setNickName_(WSPerson * self, SEL _cmd, NSString *nickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_nickName)) = nickName; }
// {(struct objc_selector *)"name", "@16@0:8", (void *)_I_WSPerson_name}
// (struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_WSPerson_setName_}
@end
Copy the code
At the bottom level, we can see that the defined properties are in the WSPerson_IMPL structure as member variables, and then the corresponding setter and getter methods are generated, while the class’s member variables are only in the structure as member variables.
- conclusion:
-
- Property = underline member variable + setter + getter.
-
- Instance variables = special member variables (instantiation of the class).
-
But here’s the problem:
NickName (‘ name ‘, ‘nickName’, ‘name’, ‘nickName’, ‘name’, ‘nickName’) 2. What is the data in a format similar to @16@0:8?
coding
For codings like @16@0:8, we can open the Apple development document with CMD +shift+0, search for ivar_getTypeEncoding, and then click Type Encodings in Discussion, which introduces the functions of relevant characters in the encoding.
- Let’s take an example
@ @ 0:8 16
andv24@0:8@16
:
From the diagram, we can understand the meaning of coding more clearly.
objc_setProperty
LLVM analysis
- Download and compile it firstllvm”And then search inside
objc_setProperty
In theCGObjCMac.cpp
We found a create in the fileobjc_setProperty
:
llvm::FunctionCallee getSetPropertyFn(a) {
CodeGen::CodeGenTypes &Types = CGM.getTypes(a); ASTContext &Ctx = CGM.getContext(a);// void objc_setProperty (id, SEL, ptrdiff_t, id, bool, bool)
CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType());
CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType());
CanQualType Params[] = {
IdType,
SelType,
Ctx.getPointerDiffType() - >getCanonicalTypeUnqualified(),
IdType,
Ctx.BoolTy,
Ctx.BoolTy};
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params));
return CGM.CreateRuntimeFunction(FTy, "objc_setProperty");
}
Copy the code
- This returns create
objc_setProperty
So let’s go back, create here, there must be somewhere to call, and then searchgetSetPropertyFn
Method, discoveryGetPropertySetFunction
The return from this method isgetSetPropertyFn
Type, we continue to search and find an enumeration like this:
switch (strategy.getKind()) {
case PropertyImplStrategy::Native: {
// size is 0, return
if (strategy.getIvarSize().isZero())
return;
Address argAddr = GetAddrOfLocalVar(*setterMethod->param_begin());
LValue ivarLValue =
EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), ivar, /*quals*/ 0);
Address ivarAddr = ivarLValue.getAddress(*this);
llvm::Type *bitcastType =
llvm::Type::getIntNTy(getLLVMContext(),
getContext().toBits(strategy.getIvarSize()));
argAddr = Builder.CreateElementBitCast(argAddr, bitcastType);
ivarAddr = Builder.CreateElementBitCast(ivarAddr, bitcastType);
llvm::Value *load = Builder.CreateLoad(argAddr);
requirements.
llvm::StoreInst *store = Builder.CreateStore(load, ivarAddr);
store->setAtomic(llvm::AtomicOrdering::Unordered);
return;
}
case PropertyImplStrategy::GetSetProperty:
case PropertyImplStrategy::SetPropertyAndExpressionGet: {
llvm::FunctionCallee setOptimizedPropertyFn = nullptr;
llvm::FunctionCallee setPropertyFn = nullptr;
if (UseOptimizedSetter(CGM)) {
And iOS 6.0 code and GC is off
setOptimizedPropertyFn =
CGM.getObjCRuntime().GetOptimizedPropertySetFunction(
strategy.isAtomic(), strategy.isCopy());
if(! setOptimizedPropertyFn) { CGM.ErrorUnsupported(propImpl, "Obj-C optimized setter - NYI");
return; }}else {
setPropertyFn = CGM.getObjCRuntime().GetPropertySetFunction(a);if(! setPropertyFn) { CGM.ErrorUnsupported(propImpl, "Obj-C setter requiring atomic copy");
return; }}Copy the code
- Content from enumeration is all in
PropertyImplStrategy
Class, when isGetSetProperty
andSetPropertyAndExpressionGet
Is calledGetPropertySetFunction
, and then we look for the class, which has an enumerationStrategyKind
Inside we find the classGetSetProperty
andSetPropertyAndExpressionGet
:
class PropertyImplStrategy {
public:
enum StrategyKind {
/// The 'native' strategy is to use the architecture's provided
/// reads and writes.
Native,
/// Use objc_setProperty and objc_getProperty.
GetSetProperty,
/// Use objc_setProperty for the setter, but use expression
/// evaluation for the getter.
SetPropertyAndExpressionGet,
/// Use objc_copyStruct.
CopyStruct,
/// The 'expression' strategy is to emit normal assignment or
/// lvalue-to-rvalue expressions.Expression }; . .PropertyImplStrategy(CodeGenModule &CGM,
const ObjCPropertyImplDecl *propImpl);
Copy the code
- in
PropertyImplStrategy
In, there is an assignment methodPropertyImplStrategy
, let’s check again:
if (IsCopy) {
Kind = GetSetProperty;
return;
}
if (setterKind == ObjCPropertyDecl::Retain) {
if (CGM.getLangOpts().getGC() == LangOptions::GCOnly) {
} else if (CGM.getLangOpts().ObjCAutoRefCount && ! IsAtomic) {if (ivarType.getObjCLifetime() == Qualifiers::OCL_Strong)
Kind = Expression;
else
Kind = SetPropertyAndExpressionGet;
return;
} else if(! IsAtomic) { Kind = SetPropertyAndExpressionGet;return;
} else {
Kind = GetSetProperty;
return; }}Copy the code
objc_setProperty
The entire search process is as follows:
validation
- From the above analysis:
Kind = GetSetProperty
orKind=SetPropertyAndExpressionGet
, the system callsobjc_setProperty
, let’s test again:
// OC main.m
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSString *nickName;
@property (atomic, copy) NSString *hobby;
@property (atomic, strong) NSString *job;
@property (nonatomic, retain) NSString *rname;
@property (atomic, retain) NSString *rnickName;
@property (nonatomic) NSString *nonName;
@property (atomic) NSString *aName;
Copy the code
Define such a property in WSPerson and compile it into C++ to see:
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool.bool);
static void _I_WSPerson_setName_(WSPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct WSPerson, _name), (id)name, 0.1); }
static NSString * _I_WSPerson_nickName(WSPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_nickName)); }
static void _I_WSPerson_setNickName_(WSPerson * self, SEL _cmd, NSString *nickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_nickName)) = nickName; }
extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long.bool);
static NSString * _I_WSPerson_hobby(WSPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct WSPerson, _hobby), 1); }
static void _I_WSPerson_setHobby_(WSPerson * self, SEL _cmd, NSString *hobby) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct WSPerson, _hobby), (id)hobby, 1.1); }
static NSString * _I_WSPerson_job(WSPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_job)); }
static void _I_WSPerson_setJob_(WSPerson * self, SEL _cmd, NSString *job) { (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_job)) = job; }
static NSString * _I_WSPerson_rname(WSPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_rname)); }
static void _I_WSPerson_setRname_(WSPerson * self, SEL _cmd, NSString *rname) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct WSPerson, _rname), (id)rname, 0.0); }
static NSString * _I_WSPerson_rnickName(WSPerson * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct WSPerson, _rnickName), 1); }
static void _I_WSPerson_setRnickName_(WSPerson * self, SEL _cmd, NSString *rnickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct WSPerson, _rnickName), (id)rnickName, 1.0); }
static NSString * _I_WSPerson_nonName(WSPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_nonName)); }
static void _I_WSPerson_setNonName_(WSPerson * self, SEL _cmd, NSString *nonName) { (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_nonName)) = nonName; }
static NSString * _I_WSPerson_aName(WSPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_aName)); }
static void _I_WSPerson_setAName_(WSPerson * self, SEL _cmd, NSString *aName) { (*(NSString **)((char *)self + OBJC_IVAR_$_WSPerson$_aName)) = aName; }
Copy the code
That is:
// Get the result
@property (nonatomic.copy) NSString *name; // objc_setProperty
@property (nonatomic.strong) NSString *nickName;
@property (atomic, copy) NSString *hobby; // objc_getProperty, objc_setProperty
@property (atomic, strong) NSString *job;
@property (nonatomic.retain) NSString *rname; // objc_setProperty
@property (atomic, retain) NSString *rnickName; // objc_getProperty, objc_setProperty
@property (atomic, copy) NSString *rhobby; // objc_getProperty objc_setProperty
@property (atomic, strong) NSString *rjob;
@property (nonatomic) NSString *nonName;
@property (atomic) NSString *aName;
Copy the code
Conclusion: When attributes are used
copy
andretain
Is called when you decorateobjc_setProperty
We found an objc_getProperty in the print. What does this have to do with it
objc_getProperty
With the above experience, we look again in LLVM:
- 1. Find create
objc_getProperty
Place:
llvm::FunctionCallee getGetPropertyFn(a) {
CodeGen::CodeGenTypes &Types = CGM.getTypes(a); ASTContext &Ctx = CGM.getContext(a);// id objc_getProperty (id, SEL, ptrdiff_t, bool)
CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType());
CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType());
CanQualType Params[] = {
IdType, SelType,
Ctx.getPointerDiffType() - >getCanonicalTypeUnqualified(), Ctx.BoolTy};
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(IdType, Params));
return CGM.CreateRuntimeFunction(FTy, "objc_getProperty");
}
Copy the code
- 2. Look for
getGetPropertyFn
:
llvm::FunctionCallee GetPropertyGetFunction(a) override {
return ObjCTypes.getGetPropertyFn(a); }Copy the code
- 3. To find again
GetPropertyGetFunction
, find enumeration:
case PropertyImplStrategy::GetSetProperty: {
llvm::FunctionCallee getPropertyFn =
CGM.getObjCRuntime().GetPropertyGetFunction(a); . }Copy the code
-
4. The value of kind (GetSetProperty) assigned by PropertyImplStrategy is the same as above.
-
5. Overall process:
According to theobjc_setProperty
We came to the conclusion that:
Conclusion: When attribute
When the element child property is atomic, either copy
orretain
Is called when you decorateobjc_setProperty
- The summary is as follows:
Why have metaclasses
- In the last article, we looked at
Instance methods
Was not found inClass method
And finally theThe metaclass
theInstance methods
I found thisClass method
. thenWhy do metaclasses exist?Let’s take an example:
@interface WSPerson : NSObject {
NSString *subject;
}
@property (nonatomic.strong) NSString *name;
@property (nonatomic.strong) NSString *nickName;
- (void)sayNB;
+ (void)sayNB;
@end
Copy the code
Then we use LLDB to take methods step by step and get the result:
(lldb) p $6.get(0).big()
(method_t::big) $7 = {
name = "sayNB"
types = 0x0000000100003f79 "v16@0:8"
imp = 0x0000000100003cd0 (KCObjcBuild`-[WSPerson sayNB])
}
Copy the code
Is sayNB a class method or an instance method? So Apple solved this problem by creating metaclasses.
Class method
Is alsoInstance methods
, it isInstance method of a metaclass
.