GitHub Repo: BoyangBlog

ARC is the default if MRC is not specified. Objective-C Automatic Reference Counting (ARC)

We know that in ARC, blocks are created on the stack except for global blocks. When used, it is automatically copied to the heap. It goes through the objc_retainBlock -> _Block_copy -> _Block_copy_internal method chain. In other words, it is important to note that every block we use that intercepts an automatic variable goes through this writing method.

From previous research, we learned that __main_block_IMPL_0 holds referenced variables. In converted block code, the block holds the external object it intercepts, creating a strong reference whether it has been changed or not.

To prepare, let’s look at the implementation of **__strong and __weak**.

__strong and __weak

__strong

__strong is actually a default method.

{
    id __strong obj = [[NSObject alloc] init];
}
Copy the code

The code will be converted to something like this

id __attribute__((objc_ownership(strong))) obj = 
((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), 
sel_registerName("alloc")), 
sel_registerName("init"));
// The code is actually only one line long, with a newline for viewing
Copy the code

Actually, these are the three main methods

id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj,selector(init));
objc_release(obj);
Copy the code

That is, objects under ARC are normally __strong.

__weak

Clang-rewrite-objc-fobjc-arc-stdlib =libc++ -mmacosx-version-min= 10.7-fobjc-runtime =macosx-10.7 -wno-deprecated -declarations main.m is deprecated as C++ code because __weak is only available in ARC state. There is no limit.

Declare an __weak object

{
    id __weak obj = strongObj;
}
Copy the code

After the transformation

id __attribute__((objc_ownership(none))) obj1 = strongObj;
Copy the code

The corresponding will be called

id obj ;
objc_initWeak(&obj,strongObj);
objc_destoryWeak(&obj);
Copy the code

As you can see from the name, one is create and one is destroy.

Here the LLVM document is slightly different from the OBJc_723 document. I’m using the latest objc_723 code here, which is better than the previous one:

id objc_initWeak(id *location, id newObj) {
    // Check whether the object instance is valid
    // An invalid object directly causes the pointer to be released
    if(! newObj) { *location = nil;return nil;
    }
    
    // Three bool values are passed
    // Use template for constant parameter passing to optimize performance
    // DontHaveOld-- no old objects,
    // DoHaveNew-- new object,
    // DoCrashIfDeallocating-- Crash if newObj has been released
    returnstoreWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating> (location, (objc_object*)newObj); } ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~void objc_destroyWeak(id *location)
{(void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
        (location, nil);
}
Copy the code

Both methods end up pointing to the storeWeak method, which is a very long method:

// Update a weak variable.
// If HaveOld is true, the variable has an existing value 
// that needs to be cleaned up. This value might be nil.
// If HaveNew is true, there is a new value that needs to be 
// assigned into the variable. This value might be nil.
// If CrashIfDeallocating is true, the process is halted if newObj is 
// deallocating or newObj's class does not support weak references.
// If CrashIfDeallocating is false, nil is stored instead.
// Update the weak variable.
// When HaveOld is set to true (DoHaveOld), the weak variable already has a value and needs to be cleaned up. This value can also be nil
// When HaveNew is set to true, DoHaveNew, a new value is assigned to the weak variable. This value can also be nil
// When CrashIfDeallocating is true, DoCrashIfDeallocating, the process is paused if newObj has been released or if newObj is a class that doesn't support weak references
// The classes of DealLocating or newObj do not support weak references
// When CrashIfDeallocating is false, DontCrashIfDeallocating, it stores nil

enum CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>
static id storeWeak(id *location, objc_object *newObj) {
    assert(haveOld  ||  haveNew);
    // Initialize the class object currently +initialize to nil
    if(! haveNew) assert(newObj == nil); Class previouslyInitializedClass = nil; id oldObj;// Declare old and new SideTable,
    SideTable *oldTable;
    SideTable *newTable;

    // Get the new value and the old value latch location (address as a unique identifier)
    // Create index flags by address to prevent bucket duplication
    // The operation pointed to below changes the old value
 retry:
    
    // If weak PTR has a weak reference to obj before, remove the SideTable corresponding to obj and assign it to oldTable
    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 && *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.
    // Prevent deadlocks between weak references and +initialize by ensuring that objects without weak references have an uninitialized ISA.
    // When using the +initialized method, because the method is called before alloc. Otherwise, the storeWeak method may be called in +initialize, but the ISA used in the Weak_register_no_lock method in the storeWeak method has not been initialized.

    if (haveNew  &&  newObj) {
        // Get the ISA pointer to the new object
        Class cls = newObj->getIsa();
        Isa is not empty and has been initialized
        if(cls ! = previouslyInitializedClass && ! ((objc_class *)cls)->isInitialized()) {// Unlock old and new SideTables
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));

            // If this class is finished with +initialize then we're good.
            // If this class is still running +initialize on this thread 
            // (i.e. +initialize called storeWeak on an instance of itself)
            // then we may proceed but it will appear initializing and 
            // not yet initialized to the check above.
            // Instead set previouslyInitializedClass to recognize it on retry.
            // If newObj has completed +initialize is ideal
            // If newObj's +initialize is still executed in the thread
            // newObj +initialize is calling storeWeak.
            // Identify the previousInitializedClass when it is retried by setting it.
            
            previouslyInitializedClass = cls;

            gotoretry; }}// Clean up old value, if any.
    // Clear the old value, actually clear the location in the old object weak_table

    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // Assign new value, if any.
    // Assign a new value, which actually saves location to the weak_table type of the new object

    if (haveNew) {
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
        // weak_register_no_lock returns nil if weak store should be rejected

        // Set is-weakly-referenced bit in refcount table.
        The Weak_register_no_lock method returns nil if the weak reference is freed
        
        // If the new object exists and the TaggedPointer technique is not used, set the if reference flag bit in the reference counter table
        if(newObj && ! newObj->isTaggedPointer()) {Weakly_referenced = true; // Mark new object with weak reference, isa.weakly_referenced = true;
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        // Set the location pointer to newObj
        // Do not set *location elsewhere. That creates competition
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj;
}
Copy the code

I won’t repeat the weak implementation here. If you are interested, you can check out the @property research (2).

Simply put, since weak is also implemented in a hash table, the objc_storeWeak function registers the variable address of the first input parameter into the weak table, and then decides whether to remove it based on the second input parameter. If the second argument is 0, the **__weak** variable is deleted from the weak table and the corresponding key-value record is deleted from the reference count table.

So if the original object referenced by **__weak is released, then the corresponding __weak** object is referred to as nil. This is done through functions like the objc_storeWeak function.

WeakSelf and strongSelf

__weak __typeof(self)weakSelf = self;
__strong __typeof(weakSelf)strongSelf = weakSelf;      
Copy the code

WeakSelf is to make the block not to hold self, avoiding circular reference. If the method and variable using self need to be accessed in the block, it is recommended to use weakSelf.

However, there is a problem. The **self.** variable modified by weakSelf is likely to be released in the process of execution.

Take the following code for example

- (void)blockRetainCycle_1 {
    __weak __typeof(self)weakSelf = self;
    self.block = ^{
        NSLog(@"% @",@[weakSelf]);
    };
}
Copy the code

If we use this function directly, it is possible that weakSelf will be released before printing, and there will be problems when printing out. To solve this problem, we need to use strongSelf.

- (void)blockRetainCycle_2 {
    __weak __typeof(self)weakSelf = self;
    self.block = ^{
        __strong typeof (weakSelf)strongSelf = weakSelf;
        NSLog(@"% @",@[strongSelf]);
    };
}
Copy the code

In this case, we’re using strongSelf, which ensures that underneath strongSelf, until it goes out of scope, there’s this strongSelf.

However, there is still a slight problem: we know that when using weakSelf, there is no guarantee that weakSelf will always be held in scope. Although strongSelf is used, there is still a small probability that weakSelf will be released before strongSelf is created. If we were simply sending messages to self objects, this would not be a problem. OC’s message forwarding mechanism ensures that we can send messages to nil objects without any problems.

But if we had done something else, such as adding a self object to the array, as the code above shows, we would have crashed.

Then we need further protection

- (void)blockRetainCycle_3 {
    __weak __typeof(self)weakSelf = self;
    self.block = ^{
        __strong typeof (weakSelf)strongSelf = weakSelf;
        if (strongSelf) {
            NSLog(@"% @",@[strongSelf]); }}; }Copy the code

The difference between __weak and __block in blocks

It is also possible to prevent block references by using __block.

We can do this in a disguised way by setting __block-modified objects to nil inside blocks.

In memory, __block holds the object, even if it is out of scope, until the block object is destroyed from the heap; __weak assigns that object to the weak object, which becomes nil if the object is destroyed.

In addition, __block objects allow blocks to modify local variables,__weak does not.

The keyword

As we know from previous articles, in ARC, regular blocks are copied from the stack to the heap.

But what about weak? (Assign will not be discussed.)

The system tells us that Core transactions: Literal to a weak property; The object will be released after assignment.

What keywords do you use under ARC? Both strong and copy are available. As you can see from the previous article, blocks are automatically copied from the stack to the heap in ARC. This copy is done automatically, even if strong is used. So, copy is fine for the sake of rigor, but strong is fine as well.

conclusion

  • If a variable is used inside the block and it is local, the block captures the variable and stores it in the underlying block structure.
  • If the captured variable is __weak, then the block uses a weak pointer to the variable (that is, the block does not hold the object), whereas __strong, then the block uses a strong pointer to the object (that is, the block holds the object).
  • Self is also, in a sense, a local variable.
  • If self doesn’t hold the block, no reference to self within the block will cause a circular reference.

Next article Block (4) : Modify the implementation of blocks