preface

In the previous article, Class Loading Principles (part 2), we looked at the loading of categories. How do categories relate to objects? What is a class extension? What’s the difference between them? This article will explore these questions.

Class extensions

The difference between class extension and classification

  • category: category, category
      1. Dedicated to the classAdding new methods
      1. Cannot add a member attribute to a classThe member attribute cannot be retrieved
      1. You can add properties to classes through Runtime, needs to be rewrittensetterandgettermethods
      1. Classification using@propertyDefine variables,Will only generateThe variableSetter and gettermethodsThe statement.Cannot generate method implementationandThe underlined member variable.
  • extension: class extensions
      1. It can be said thatSpecial classificationAlso calledAnonymous classification
      1. canAdd member attributes to the class, but isPrivate variables
      1. canAdd methods to classesAlso,Is a private method

The underlying implementation of class extensions

Define a WSAnimal class and its extensions in main:

// .h
@interface WSAnimal : NSObject

@property (nonatomic.copy) NSString *name;
@property (nonatomic.assign) int    age;

- (void)instance_method;
+ (void)class_method;

@end

/ / extension
@interface WSAnimal(a)

@property (nonatomic.copy) NSString *ext_name;
@property (nonatomic.assign) int    ext_age;

- (void)ext_instance_method;
+ (void)ext_class_method;

@end

// .m
@implementation WSAnimal

- (void)instance_method {
    NSLog(@"%s", __func__);
}
+ (void)class_method {
    NSLog(@"%s", __func__);
}
- (void)ext_instance_method {
    NSLog(@"%s", __func__);
}
+ (void)ext_class_method {
    NSLog(@"%s", __func__);
}

@end
Copy the code
  • againclanggeneratemain.cpp, and then searchext_instance_method:



  • foundExtension method, all inmethod_list_tNo new structure is generated. So the extension method will add directly tomethod_list_t? The next inobjc4-812To debug, found will be directly added.

Category Associated Object

We know that when you add an object to a class, you need to use Runtime to implement the setter and getter methods, so what does that process look like? Explore the source code to debug.

  • inWSPersonTwo attributes are defined in the classification and then implementedSetter and getterMethods:



  • The association class is usedobjc_setAssociatedObjectandobjc_getAssociatedObjectLet’s analyze what they do

objc_setAssociatedObject

  • objc_setAssociatedObjectNeed toFour parameters, respectively,By association.Associated markup.The value of the associated objectandRelated policies.

Let’s take a look at the underlying implementation:

void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
    _object_set_associative_reference(object, key, value, policy);
}
Copy the code
  • The main call is_object_set_associative_referenceWhat does it mainly do

DisguisedPtr<objc_object>

DisguisedPtr<objc_object> disguised{(objc_object *)object};
Copy the code
  • This is theobjectEquivalent toobjc_objectType, getDisguisedPtr<objc_object>Object of typedisguised, the code is equivalent toDisguisedPtr<objc_object> disguised = (objc_object *)object

ObjcAssociation

ObjcAssociation association{policy, value};
Copy the code

We pass in a policy and value to initialize an ObjcAssociation object, and then copy or retain the value to _value depending on the policy type:

association.acquireValue(a);inline void acquireValue(a) {
    if (_value) {
        switch (_policy & 0xFF) {
        case OBJC_ASSOCIATION_SETTER_RETAIN:
            _value = objc_retain(_value);
            break;
        case OBJC_ASSOCIATION_SETTER_COPY:
            _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
            break; }}}Copy the code

The core code



  • Core code created firstAssociationsManagerobjectmanager, and then get the globalhashMapTable and then according tovalueFor processing
The destructor

The source code for AssociationsManager is as follows:



  • Among themAssociationsManager()Is the constructor
  • ~AssociationsManager()Is the destructor

Destructors: Destructors are special class member functions. In simple terms, destructors do the opposite of what constructors do. They are used to do some cleaning up before an object is deleted.

Code validation
  • Let’s define a simple structure, and then we have aA constructoranddestructor:
struct Sport {
    Sport() { NSLog(@" I am a pickle fish ~"); }
    ~Sport() { NSLog(@" I am a braised chicken ~"); }};Copy the code
  • thenmainThe constructor is called when the completion of the: print as follows:



  • Out of scope, print:



  • The destructor is called out of scope. Let’s take a lookAssociationsManager, its constructor islock, whose destructor isunlock.
AssociationsHashMap

AssociationsHashMap & Associations (manager.get()) gets a HashMap from manager.get(), and its source code is implemented by calling get() from _mapStorage. _mapStorage is static, so this table is unique. This table is a singleton:



  • And then we go to the judgment, what ifvalueThere are values:associationsCall thetry_emplaceI created an objectrefs_resultPrint this type, and the breakpoint runs at this point:



  • getrefs_resultIs a longer type, looks more scary, but the essence of only need to usesecondParameter. Let’s analyze it againtry_emplacefunction
For the first time try_emplace

It is implemented as follows:



    1. Let’s look atLookupBucketForThere are two functions with the same name:



Because the type passed in is notconstThe breakpoint goes to one of the following methods, but the middle code still goes to the topLookupBucketFor, its core implementation is familiar to us:



cacheIn looking forbucketProcess the same

    1. InsertIntoBucket: When it is not found, it calls insert a new onebucket, the method is implemented as follows:
template <typename KeyArg, typename. ValueArgs>BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key, ValueArgs &&... Values) {
      TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
      
      TheBucket->getFirst() = std::forward<KeyArg>(Key); : :new (&TheBucket->getSecond()) ValueT(std::forward<ValueArgs>(Values)...) ;return TheBucket;
  }
Copy the code

Insert (InsertIntoBucketImpl) insert (InsertIntoBucketImpl)



And this is a process that we’re familiar with, which is the processing of the volume at insertion, ifIf the volume is greater than or equal to 3/4, double the capacity.

  • Access to theTheBucketAnd then toTheBucketOf,firstandsecondPerform related assignments:

    • TheBucketthefirstisobjectStrong goDisguisedPtr<objc_object>type
    • secondisObjectAssociationMaptable
The second try_emplace

On the second call, the parameters passed in are different, that is, the parameters to TheBucket are different

  • TheBucketthefirstisconst void *The type ofkey
  • secondisObjcAssociationType, andObjcAssociationThe store ispolicyandvalue
erase
  • whenvalueIs called when the value does not existeraseMethods to removebucket, andObjcAssociation

The flow chart



objc_getAssociatedObject

  • Let’s start with the source code:



Analysis of the

  • It’s basically called_object_get_associative_referenceMethod, which is implemented as follows:



  • Among themfindThe function is to findbucketProcess:
iterator find(const_arg_type_t<KeyT> Val) {
    BucketT *TheBucket;
    if (LookupBucketFor(Val, TheBucket))
        return makeIterator(TheBucket, getBucketsEnd(), true);
    return end(a); }Copy the code
  • The whole process is as follows: First get the totalHashMap-> then according toobjectTo obtainObjectAssociationMap bucket– > get againObjectAssociationMap-> Then getObjcAssociation bucket– > get againObjcAssociation-> < p style = “text-align: centerpolicyreturnvalue

The flow chart