The two common declarations for @Property are copy and strong:

@interface LGPerson() @property (copy, nonatomic) NSString *nickName; // copy modifiers @property (strong, nonatomic) NSString *name; // strong modifiers @endCopy the code

After being rewritten in c++ via clang, the related code is generated as follows:

.// copy decorates -> to make objc_setProperty
static void _I_LGPerson_setNickname_(LGPerson * self, SEL _cmd, NSString *nickName) { 
	objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _nickName), (id)nickName, 0.1); 
}

// The getter method is obtained directly from the address offset
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) {
    return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); 
}

// select * from memory; // select * from memory
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { 
	(*(NSString *__strong *)((char *)self + OBJC_IVAR_$_LGPerson&_name)) = name;
}
Copy the code

The analysis is divided into two parts:

  1. Copy modifier, the bottom will goobjc_getPropertyandobjc_setProperty
  2. The strong modifier undergoes other methods

The underlying method of the copy modifier in @Property

Using the copy decoration takes the objc_getProperty and objc_setProperty methods from the underlying LLVM

For getter methods, the source code in LLVM is as follows

Objc_getProperty (id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { if (offset == 0) { return object_getClass(self); } id *slot = (id*) ((char*)self + offset); // Return the object if (!) if (!) if (!) if (!) if (!) atomic) return *slot; // Global property lock!! spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); Id value = objc_retain(*slot); +1 slotlock.unlock(); // For performance, we (safely) issue the autorelease OUTSIDE of the spinlock. So it's thread-safe return objc_autoreleaseReturnValue(value); }Copy the code

A bit of information about global locks used in the atomic state is as follows:

  1. PropertyLocksIs aStripedMap<spinlock_t>The type ofThe global variableAnd should be one according to the nameHashMap
  2. StripedMapIt’s an array implementationHashMap.keyIs a slot pointer, and value is a type isAn object.

In simple terms, the getter method that is atomic modifieduses the slot address as the key at the bottom level, then acquirea set of locks that are maintained in a global HashMap, and then locks them in subsequent key processes!!

When the object is finally returned, its reference count is +1 and it is added to the AutoReleasePool

You can see that the atomic locking method is very poor, and it shares global locks.

This lock is related to the address of object _ivar. It is possible that multiple objects of different types share a global lock!

For objc_setProperty, there is information in the LLVM source code:

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) {
    boolcopy = (shouldCopy && shouldCopy ! = MUTABLE_COPY);bool mutableCopy = (shouldCopy == MUTABLE_COPY);
    reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}

/** self: implicit argument, object message receiver _cmd: implicit argument, NewValue: specifies the value to be passed in the setter, offset: specifies the offset to the pointer to the property
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) {
    if (offset == 0) { // The cheap pointer is 0, which represents self itself
        object_setClass(self, newValue); 
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset); // Member variable object pointer

    if (copy) {
        // If it is a shallow copy, then call the copyWithZone method to make a shallow copy of the new object passed in and assign a value to the newValue variable
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        // In the case of a deep copy, the mutableCopyWithZone method is called to make a deep copy of the new object passed in, and the value is assigned to the variable newValue
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        // if newValue is not copy, determine whether newValue and oldValue are the same. C++ does this a lot
        if (*slot == newValue)
            return;
        // Assign a new value!
        
        // The newValue object references +1 and assigns the return value to the newValue variable
        newValue = objc_retain(newValue);
    }
    
    // This is followed by assigning true to the slot and releasing oldValue
    if(! atomic) { oldValue = *slot; *slot = newValue; }else {
        // Global lock! In front of the said
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock(a); oldValue = *slot; *slot = newValue; slotlock.unlock(a); }objc_release(oldValue);
}
Copy the code

From the above code logic can be very clean, here take a closer look at the copy related content:

  1. If it is the copy method, then the copyWithZone method of the object is called

  2. If it is mutablecopy, then the object’s mutableCopyWithZone method is called

  3. If copy = 0 and mutablecopy = 0, then the objc_retain method will eventually be called

Anyway, copy is only valid for setters, not getters!!

Actually in ARC these two methods will not trigger in many situations. It’s a different logic. For example, in strong or assign above, the setter will assign directly to the address offset, not to the objc_setProperty method. At the same time, there is no strong weak assign logic in either method.

OC object attributes directly obtain member variables and assign logic analysis

We know:

  1. The essence of getters is to get information about member variables generated in object properties!

  2. The essence of setters is to assign values to member variables generated by object properties!!

@property will generate _ivar getters and setters

Here are the runtime functions:

id object_getIvar(id obj, Ivar ivar) {
    if(! obj || ! ivar || obj->isTaggedPointer()) return nil;
    ptrdiff_t offset;
    // Member variables can be managed using ARC: strong, weak, Unretained or MRC
    objc_ivar_memory_management_t memoryManagement;
    _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
    id *location = (id *)((char *)obj + offset);
   
    if (memoryManagement == objc_ivar_memoryWeak) {
        return objc_loadWeak(location); // Weak modifiers to remove weak reference table queries
    } else {
        return *location; // Non-weak reference, strong, unretain direct value}}void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong) {
    if(! obj || ! ivar || obj->isTaggedPointer()) return;

    ptrdiff_t offset;
    objc_ivar_memory_management_t memoryManagement;
    _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);

    // The memory management mode of member variables is unknown. Assume strong or unretain
    if (memoryManagement == objc_ivar_memoryUnknown) {
        if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
        else memoryManagement = objc_ivar_memoryUnretained;
    }
    
    id *location = (id *)((char *)obj + offset);

    switch (memoryManagement) {
    case objc_ivar_memoryWeak:       
            objc_storeWeak(location, value); Weak_table = weak_table; weak_table = weak_table
            break;
    case objc_ivar_memoryStrong:     
            objc_storeStrong(location, value); // The strong modifier
            break;
    case objc_ivar_memoryUnretained:  // Unretain memory management mode! Direct assignment operation!! No memory operations
            *location = value; 
            break;
    case objc_ivar_memoryUnknown:    
            _objc_fatal("impossible"); }}Copy the code

From the above, we can simply summarize:

  1. Weak, the association method isobjc_loadWeakandobjc_storeWeak
  2. The strong modifier, the getter method, evaluates directly!! And the setter method isobjc_storeStrong
  3. Under the unretain modifier, getters and setters are directly on member variables*locationValue assignment without any memory management operations

Strong modifier

For objc_storeStrong, which is associated with the strong modifier, the code is very clean, which is some memory management methods:

void objc_storeStrong(id *location, id obj) { id prev = *location; If (obj == prev) {return; } // New value memory management objc_retain(obj); // Place the object pointer to the new value *location = obj; // Release the old value objc_release(prev); }Copy the code

Weak modifier

Related to the weak modifier are objc_loadWeak and objc_storeWeak, but I’ll focus on storeWeak here

static id storeWeak(id *location, objc_object *newObj) { assert(haveOld || haveNew); if (! haveNew) assert(newObj == nil); Class previouslyInitializedClass = nil; id oldObj; SideTable *oldTable; SideTable *newTable; retry: if (haveOld) { oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } if (haveNew) { newTable = &SideTables()[newObj]; } else { newTable = nil; } SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable); // if haveOld is true and the location pointer is not oldObj if (haveOld && *location! = oldObj) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); goto retry; } // Prevent a deadlock between the weak reference machinery // and the +initialize machinery by ensuring that no // weakly-referenced object has an un-+initialized isa. if (haveNew && newObj) { Class cls = newObj->getIsa(); if (cls ! = previouslyInitializedClass && ! ((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); _class_initialize(_class_getNonMetaClass(cls, (id)newObj)); previouslyInitializedClass = cls; goto retry; } } if (haveOld) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } if (haveNew) { newObj = (objc_object *) weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating); if (newObj && ! newObj->isTaggedPointer()) { newObj->setWeaklyReferenced_nolock(); } *location = (id)newObj; } SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); return (id)newObj; }Copy the code

Weak assignment operation is much more complex than strong. Strong member variable only increases the reference count of the held object, while weak member variable needs to handle weak_table. More on weak here will be devoted to memory management in a future article.

About the copy decoration in iOS

Here’s a summary of copy, which I post directly:

As far as iOS development is concerned, there are several concepts about copy: 1. Copy: the purpose of copy is to produce copies, so that the original object and the copy are independent and do not affect each other; 2. Immutable copy: The copy method produces an immutable copy regardless of whether the original object is mutable. 3. MutableCopy: the mutableCopy method produces a mutableCopy of the original object regardless of whether it is mutable. 4. Deep copy: The content is copied to create new objects. 5. Shallow copy: A pointer is copied without generating new objects. If the source object is immutable, the copy method is a shallow copy. If the source object is immutable, the copy method is a shallow copy. - If the source object is variable, the copy method is a deep copy. The mutableCopy method is a deep copy in any case; The purpose of copy is to create a separate and independent copy of each other. 2. Copy is either a direct call or a modified property, its essence is to call copyWithZone and mutableCopyWithZone methods; 3. The difference between deep and shallow copy is whether the return value is a newly created object, regardless of which method of copy is called. 4. The key purpose of the copy attribute is to tell the user not to directly modify the value in memory to which the attribute points. 5. Do not use copy to decorate mutable objects, such as mutable arrays. 6. The essence of copy is to call the two methods in the copy protocol, but the system implements the two methods of the protocol for strings, arrays, dictionaries, NSNumbers and blocks, and the two methods implement the same logic. 7. The nature of the copy attribute is to automatically call the copy method of the new value to get an immutable object. The attribute is not mutable, because it is not necessary; 8. Copy refers to a Block that is stored on the stack and is transferred to the heap. Otherwise, when a Block is destroyed on the stack, the pointer to the Block will be accessed, causing bad memory access problems. Source: https://juejin.cn/post/6844904033019232264 the nuggets copyright owned by the author. Commercial reprint please contact the author to obtain authorization, non-commercial reprint please indicate the source.Copy the code

Added: a few questions related to the @ Property modifier

Why do you use copy for NSString attributes?

NSString, NSArray, NSDictionary, and so on often use the copy keyword because they have mutable types: NSMutableString, NSMutableArray, and NSMutableDictionary.

In general, copy and strong have the same meanings, but in special cases:

For example, an NSString property might be assigned to an NSMutableString instance, in which case a mutable type change would break the encapsulation of the @property. Such as:

@interface Person:NSObject @property(nonatomic, strong) NSSring *name; @end int main(){ Person *person = [Person new]; NSMutableString *testName = [NSMutableString stringWithString:@"hello"]; person.name = testName; NSLog("%@", person.name); // Print: hello [testName appendString:@"world"]; NSLog("%@", person.name); // Print helloWorld return 0; }Copy the code

It’s clear here that NSString property decorated with strong holds an NSMutableString object, and when that object changes, it breaks the encapsulation of NSString!!

In simple terms: to prevent the case that if you assign a mutable string to the string object without using the copy method, the string will also be passively modified if you change the original string

2. @property (nonatomic, copy) NSMutableArray *array;What’s wrong with that?

Use copy to decorate the NSMutableArray member, because copy produces an immutable object, NSArray. On subsequent calls to add, delete, or modify NSMutableArray methods, the actual message sent to NSArray will crash because the method cannot be found.

reference

www.jianshu.com/p/4259ea33c…

Blog.51cto.com/u_12801393/…

Juejin. Cn/post / 684490…