The problem first

1. What is the difference between initalize and Load? 2. What is the output result of the following code?

@implementation ASSon

- (instancetype)init {
    if (self == [super init]) {
        NSLog(@"[self class]=%@,[super class]=%@,",[self class],[super class]);
    }
    return self;
}

@end
Copy the code

3Can a compiled class add member variables? Can classes created at run time add member variables?

4. What is the output result of the following code?

[[[ASSon alloc]init] faction1];
Copy the code

Do associated objects need to be released by themselves?

Initalize analysis

The official description Initializes the class before it receives its first message

Called before the class receives the first message

In light of this important comment, if we look at the process method path for sending the message, it is not hard to see that there is an Initalize correlation in the logic that determines whether the class is initialized, that is, initialized.

The related paths are as follows

lookUpImpOrForward->realizeAndInitializeIfNeeded_locked->initializeAndLeaveLocked->initializeAndMaybeRelock->initializeN onMetaClass->callInitialize

NEVER_INLINE IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) { const IMP forward_imp = (IMP)_objc_msgForward_impcache; IMP imp = nil; Class curClass; runtimeLock.assertUnlocked(); /// code omitted...... checkIsKnownClass(cls); // check whether the class is implemented. If it is not implemented, implement it first. At this point is to determine the purpose of the parent class chain, method of subsequent cycle CLS = realizeAndInitializeIfNeeded_locked (inst, CLS, behaviors & LOOKUP_INITIALIZE); // runtimeLock may have been dropped but is now locked again runtimeLock.assertLocked(); curClass = cls; /// code omitted...... return imp; }Copy the code
static Class realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize) { runtimeLock.assertLocked(); if (slowpath(! cls->isRealized())) { cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); RuntimeLock may have been dropped but is now locked again} if (slowPath (initialize &&! cls->isInitialized())) { cls = initializeAndLeaveLocked(cls, inst, runtimeLock); // runtimeLock may have been dropped but is now locked again // If sel == initialize, class_initialize will send +initialize and // then the messenger will send +initialize again after this // procedure finishes. Of course, if this is not being called // from the messenger then it won't happen. 2778172 } return cls; }Copy the code
// Locking: caller must hold runtimeLock; this may drop and re-acquire it
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
    return initializeAndMaybeRelock(cls, obj, lock, true);
}
Copy the code
static Class initializeAndMaybeRelock(Class cls, id inst, mutex_t& lock, bool leaveLocked) { lock.assertLocked(); ASSERT(cls->isRealized()); if (cls->isInitialized()) { if (! leaveLocked) lock.unlock(); return cls; } // Find the non-meta class for cls, if it is not already one. // The +initialize message is sent to the non-meta class object. Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst); // Realize the non-meta class if necessary. if (nonmeta->isRealized()) { // nonmeta is cls, which was already realized // OR nonmeta is distinct, but is already realized // - nothing else to do lock.unlock(); } else { nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock); // runtimeLock is now unlocked // fixme Swift can't relocate the class today, // but someday it will: cls = object_getClass(nonmeta); } // runtimeLock is now unlocked, for +initialize dispatch ASSERT(nonmeta->isRealized()); /// initializeNonMetaClass(nonmeta); if (leaveLocked) runtimeLock.lock(); return cls; }Copy the code
void initializeNonMetaClass(Class cls) { ASSERT(! cls->isMetaClass()); Class supercls; bool reallyInitialize = NO; // Make sure super is done initializing BEFORE beginning to initialize cls. // See note about deadlock above. supercls =  cls->getSuperclass(); if (supercls && ! Supercls ->isInitialized()) {/// Recursively call the parent initializeNonMetaClass(supercls); } /// the code is omitted...... {/// send message initialize callInitialize(CLS); /// code omitted...... return; } /// the code is omitted...... }Copy the code
void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}
Copy the code
  • initializeCalled before the first method of the class or its subclass is called (before the message is sent)
  • Of the parent classinitializeMethods are executed before subclasses
  • When a subclass is not implementedinitializeMethod, the parent class is calledinitializeMethods; The subclass implementationinitializeMethod overrides the parent classinitializemethods
  • When more than one classification is implementedinitializeMethod, which overrides the methods in the class and executes only one (which executes the classifiers last loaded into memory)

[self class], [super class

CPP files are compiled using CLang, and then the core code is analyzed

// @implementation ASSon

static instancetype _I_ASSon_init(ASSon * self, SEL _cmd) {
    if (self == ((ASSon *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ASSon"))}, sel_registerName("init"))) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_nm_y1t1syks0yj_jrzr73yc4qp00000gn_T_ASSon_b9cdb4_mi_0,((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")),((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ASSon"))}, sel_registerName("class")));
    }
    return self;
}

// @end

struct __rw_objc_super { 
	struct objc_object *object; 
	struct objc_object *superClass; 
	__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};

Copy the code

[self class]

[self class] sends a message through objc_msgSend. The receiver of the message is self and the method number is class

- (Class)class {
    return object_getClass(self);
}
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
Copy the code

[self class] [isa] [self class] [isa] [self class] [isa] [self class] [isa] [self class]

[super class]

[super Class] is sent via objc_msgSendSuper with the __rw_objc_super structure type parameter and the method number class

Note that becauseobjcThere will be some compilation optimizations, so let’s see if that happens through assemblyobjc_msgSendSuperCompiler optimization of methods.Discovery is ultimately calledobjc_msgSendSuper2Methods.

// objc_msgSendSuper2() takes the current search class, not its superclass. OBJC_EXPORT id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...). OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0, 2.0);Copy the code

__rw_objc_super is an intermediate structure in the. CPP file.

/// Specifies the superclass of an instance. struct objc_super { /// Specifies an instance of a class. __unsafe_unretained _Nonnull id receiver; /// Specifies the particular superclass of the instance to message. #if ! defined(__cplusplus) && ! __OBJC2__ /* For compatibility with old objc-runtime.h header */ __unsafe_unretained _Nonnull Class class; #else __unsafe_unretained _Nonnull Class super_class; #endif /* super_class is the first class to search */ };Copy the code

The receiver of the current message is the parameter receiver, which is passed in to self. Therefore, self in [super class] is the instance object after init, and its isa is the message receiver of ASSon class.

superIs the keyword official explanation

usesuperThe keyword send message is converted into a call by the compilerobjc_msgSendSuperAnd the correlation function

Dealloc analysis

deallocThe flow chartThrough the flow chart, we can see that the system will automatically perform some operations

  • 1.C++Function release:objc_cxxDestruct
  • 2. Remove associated attributes:_object_remove_assocations
  • Set weak references to nil automatically:weak_clear_no_lock(&table.weak_table, (id)this)
  • 4. Reference counting processing:Table. Refcnts. Erase (this)
  • 5. Destruction Object:free(obj)

Answer questions first

1. What is the difference between initalize and Load? By analyzing initalize and load, you can compare invocation timing, invocation sequence, and execution mode.

Call timing initialize: The method is called before the class receives the first message. A class is called only once. If the class is not initialized, this will not be called. Methods are called when the program loads classes and classes. Each class and class is called only once, regardless of whether the class is in use or not, before main

Initialize: the parent class -> this class will automatically call the initialize method if the parent class does not implement initialize. Parent class -> this class -> class. The load method of the parent class will not be called if there is no load method in the child class

Load: Initialize: objc_msgSend Sends messages

2. What is the output result of the following code? The results are as follows. For specific reasons, see [self class] and [super class]

[self class]=ASSon,[super class]=ASSon
Copy the code

Can a compiled class add member variables? Can classes created at run time add member variables? (ios-objc_object & objc_class) and ios-class loading (a) previously analyzed the instance variable is stored in class_ro_t, which is generated at the compile stage (class loading) and is read-only property, can not be modified later.

Objc_allocateClassPair creates the class, class_addIvar adds member variables to the class, and objc_registerClassPair registers the class. It is important to note, however, that once a class is registered, it cannot be added.

4. What is the output result of the following code?

Original class +[ASSon load] category 2+[ASSon(Son) Load] Category 1+[ASSon(Son) Initialize] Category 1-[ASSon(Son) faction1]Copy the code
  • If a method of the same name is a common method (includinginitialize), only the classification method is called. Because the classification method is in the classrealizeAfter implementationattachIt is inserted before the methods of the class, so only the methods of the class are called.
  • If the method of the same name isloadMethod, first the original classload, after the classificationloadYou can refer toIOS – Class Loading (2)Analysis of the

Note that if there are multiple categories, the loading order is based on the category order here

Do associated objects need to be released by themselves? According to the dealloc analysis above, the system will automatically remove the association attribute, so there is no need to release it by itself.