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

  • WWDC2020In, we have a new understanding of the class:

Changes to loading classes from memory:

The loading process from memory is shown below:

clean MemoryDirty Memory

  • clean MemoryIs memory that does not change after loading.clean MemoryIt can be removed to save more space. Because if you need toclean MemoryData can be loaded from memory.
  • Dirty MemoryIs memory that has changed at runtime. Once used, the class becomesDirty MemoryBecause the runtime writes new data to it.For example, create a method cache and point to it from the class.Dirty Memorythanclean MemoryexpensiveAs long as the process is running, it will always exist.

Second,attributeandMember variables

  • We first inmain.mTo define aWSPersonClass, and then useclangCompile amain.cppFile:
// 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:
      1. Property = underline member variable + setter + getter.
      1. 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 16andv24@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 insideobjc_setPropertyIn theCGObjCMac.cppWe 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 createobjc_setPropertySo let’s go back, create here, there must be somewhere to call, and then searchgetSetPropertyFnMethod, discoveryGetPropertySetFunctionThe return from this method isgetSetPropertyFnType, 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 inPropertyImplStrategyClass, when isGetSetPropertyandSetPropertyAndExpressionGetIs calledGetPropertySetFunction, and then we look for the class, which has an enumerationStrategyKindInside we find the classGetSetPropertyandSetPropertyAndExpressionGet:
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
  • inPropertyImplStrategyIn, 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_setPropertyThe entire search process is as follows:



validation

  • From the above analysis:Kind = GetSetPropertyorKind=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 usedcopyandretainIs 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 createobjc_getPropertyPlace:
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 forgetGetPropertyFn:
llvm::FunctionCallee GetPropertyGetFunction(a) override {
    return ObjCTypes.getGetPropertyFn(a); }Copy the code
  • 3. To find againGetPropertyGetFunction, 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_setPropertyWe came to the conclusion that:

Conclusion: When attributeWhen the element child property is atomic, either copyorretainIs called when you decorateobjc_setProperty

  • The summary is as follows:

Why have metaclasses

  • In the last article, we looked atInstance methodsWas not found inClass methodAnd finally theThe metaclasstheInstance methodsI 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 methodIs alsoInstance methods, it isInstance method of a metaclass.