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-
- Dedicated to the class
Adding new methods
- Dedicated to the class
-
Cannot add a member attribute to a class
The member attribute cannot be retrieved
-
You can add properties to classes through Runtime
, needs to be rewrittensetter
andgetter
methods
-
- Classification using
@property
Define variables,Will only generateThe variableSetter and getter
methodsThe statement
.Cannot generate method implementation
andThe underlined member variable
.
- Classification using
-
extension
: class extensions-
- It can be said that
Special classification
Also calledAnonymous classification
- It can be said that
-
- can
Add member attributes to the class
, but isPrivate variables
- can
-
- can
Add methods to classes
Also,Is a private method
- can
-
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
- again
clang
generatemain.cpp
, and then searchext_instance_method
:
- found
Extension method
, all inmethod_list_t
No new structure is generated. So the extension method will add directly tomethod_list_t
? The next inobjc4-812
To 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.
- in
WSPerson
Two attributes are defined in the classification and then implementedSetter and getter
Methods:
- The association class is used
objc_setAssociatedObject
andobjc_getAssociatedObject
Let’s analyze what they do
objc_setAssociatedObject
objc_setAssociatedObject
Need toFour parameters
, respectively,By association
.Associated markup
.The value of the associated object
andRelated 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_reference
What does it mainly do
DisguisedPtr<objc_object>
DisguisedPtr<objc_object> disguised{(objc_object *)object};
Copy the code
- This is the
object
Equivalent toobjc_object
Type, 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 first
AssociationsManager
objectmanager
, and then get the globalhashMap
Table and then according tovalue
For processing
The destructor
The source code for AssociationsManager is as follows:
- Among them
AssociationsManager()
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 a
A constructor
anddestructor
:
struct Sport {
Sport() { NSLog(@" I am a pickle fish ~"); }
~Sport() { NSLog(@" I am a braised chicken ~"); }};Copy the code
- then
main
The 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 look
AssociationsManager
, 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 if
value
There are values:associations
Call thetry_emplace
I created an objectrefs_result
Print this type, and the breakpoint runs at this point:
- get
refs_result
Is a longer type, looks more scary, but the essence of only need to usesecond
Parameter. Let’s analyze it againtry_emplace
function
For the first time try_emplace
It is implemented as follows:
-
- Let’s look at
LookupBucketFor
There are two functions with the same name:
- Let’s look at
Because the type passed in is notconst
The breakpoint goes to one of the following methods, but the middle code still goes to the topLookupBucketFor
, its core implementation is familiar to us:
和cache
In looking forbucket
Process the same
-
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 the
TheBucket
And then toTheBucket
Of,first
andsecond
Perform related assignments:
TheBucket
thefirst
isobject
Strong goDisguisedPtr<objc_object>
typesecond
isObjectAssociationMap
table
The second try_emplace
On the second call, the parameters passed in are different, that is, the parameters to TheBucket are different
TheBucket
thefirst
isconst void *
The type ofkey
second
isObjcAssociation
Type, andObjcAssociation
The store ispolicy
andvalue
erase
- when
value
Is called when the value does not existerase
Methods 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_reference
Method, which is implemented as follows:
- Among them
find
The function is to findbucket
Process:
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 total
HashMap
-> then according toobject
To obtainObjectAssociationMap bucket
– > get againObjectAssociationMap
-> Then getObjcAssociation bucket
– > get againObjcAssociation
-> < p style = “text-align: centerpolicy
returnvalue