The life cycle of weak

The life cycle of weak

The life cycle of weak

We all know that weak refers to a weak reference, which does not increase the reference count of the object, and after the object it points to is released, the weak pointer will be set to nil. Weak references are usually used to deal with circular references, such as in the use of proxies and blocks, which are relatively more commonly used.

I have a little understanding of the implementation of weak before, and I know its basic life cycle, but I don't know how it is implemented clearly. Today I read the explanation of __weak in "Advanced Programming in Objective-C" again, and I'll take a note here.

Let's take the following line of code as an example:

Code Listing 1: Sample code

  1. {
  2. id __weak obj1 = obj;
  3. }

When we initialize a weak variable, the runtime calls the objc_initWeak function. The declaration of this function in Clang is as follows:

  1. id objc_initWeak(id *object, id value);

The specific implementation is as follows:

  1. id objc_initWeak(id *object, id value)
  2. {
  3. *object = 0 ;
  4. return objc_storeWeak(object, value);
  5. }

The example code is converted into the compiler simulation code as follows:

  1. id obj1;
  2. objc_initWeak(&obj1, obj);

So, what is done here is to initialize obj1 to 0 (nil) first, and then pass the address of obj1 and obj as parameters to the objc_storeWeak function.

The objc_initWeak function has a prerequisite: object must be a valid pointer that has not been registered as a __weak object. And value can be null or point to a valid object.

If value is a null pointer or the object it points to has been released, object is zero-initialized. Otherwise, object will be registered as a __weak object pointing to value. This should be done by the objc_storeWeak function. The function declaration of objc_storeWeak is as follows:

  1. id objc_storeWeak(id *location, id value);

The specific implementation is as follows:

  1. id objc_storeWeak(id *location, id newObj)
  2. {
  3. id oldObj;
  4. SideTable *oldTable;
  5. SideTable *newTable;
  6.  
  7. ......
  8.  
  9. // Acquire locks for old and new values.  
  10. // Order by lock address to prevent lock ordering problems.  
  11. // Retry if the old value changes underneath us.  
  12. retry:
  13. oldObj = *location;
  14.  
  15. oldTable = SideTable::tableForPointer(oldObj);
  16. newTable = SideTable::tableForPointer(newObj);
  17.  
  18. ......
  19.  
  20. if (*location != oldObj) {
  21. OSSpinLockUnlock(lock1);
  22. # if SIDE_TABLE_STRIPE > 1  
  23. if (lock1 != lock2) OSSpinLockUnlock(lock2);
  24. #endif
  25. goto retry;
  26. }
  27.  
  28. if (oldObj) {
  29. weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
  30. }
  31. if (newObj) {
  32. newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
  33. // weak_register_no_lock returns NULL if weak store should be rejected  
  34. }
  35. // Do not set *location anywhere else. That would introduce a race.  
  36. *location = newObj;
  37.  
  38. ......
  39.  
  40. return newObj;
  41. }

Let's look at what this code does by omitting the various lock operations in the source code. Before that, let's first understand the weak table and SideTable.

The weak table is a weak reference table, implemented as a weak_table_t structure, which stores all weak reference information related to an object. Its definition is as follows (specifically defined in objc-weak.h):

  1. struct weak_table_t {
  2. weak_entry_t *weak_entries;
  3. size_t num_entries;
  4. ......
  5. };

Among them, weak_entry_t is an internal structure stored in the weak reference table, which is responsible for maintaining and storing all weak reference hash tables pointing to an object. Its definition is as follows:

  1. struct weak_entry_t {
  2. DisguisedPtr<objc_object> referent;
  3. union {
  4. struct {
  5. weak_referrer_t *referrers;
  6. uintptr_t out_of_line : 1 ;
  7. ......
  8. };
  9. struct {
  10. // out_of_line=0 is LSB of one of these (don't care which)  
  11. weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
  12. };
  13. };
  14. };

The referent is the object being referenced, i.e. the obj object in the sample code. The following union stores all weak references to the object. As can be seen from the comments, when out_of_line is equal to 0, the hash table is replaced by an array. In addition, the addresses of all weak reference objects are stored in the address of the weak_referrer_t pointer. Its definition is as follows:

typedef objc_object ** weak_referrer_t;

SideTable is a class implemented in C++. Its specific definition is in NSObject.mm. Let's take a look at the definitions of some of its member variables:

  1. class SideTable {
  2. private :
  3. static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
  4.  
  5. public :
  6.  
  7. RefcountMap refcnts;
  8. weak_table_t weak_table;
  9.  
  10. ......
  11.  
  12. }

RefcountMap refcnts, you should be able to guess what this is used for, right? It looks like a reference count or something. Haha, it seems to be, this thing stores the reference count information of an object. Of course, we will not explore it here, we are concerned about weak_table. This member variable points to the weak table of an object.

Now that we know about the weak table and SideTable, let's look back at objc_storeWeak. First, we need to find the old object pointed to by the weak pointer:

  1. oldObj = *location;

Then get the SideTable objects related to the new and old objects:

  1. oldTable = SideTable::tableForPointer(oldObj);
  2. newTable = SideTable::tableForPointer(newObj);
  3.  
  4. The next thing to do is to remove the pointing information in the weak table of the old object and create the associated information in the weak table of the new object:
  5.  
  6. if (oldObj) {
  7. weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
  8. }
  9. if (newObj) {
  10. newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
  11. // weak_register_no_lock returns NULL if weak store should be rejected  
  12. }

Next, let the weak reference pointer point to the new object:

  1. *location = newObj;

*** will return this new object:

  1. return newObj;

This is the basic implementation of objc_storeWeak. Of course, when objc_storeWeak is called in objc_initWeak, the old object is empty, so the weak_unregister_no_lock operation will not be executed.

When the object pointed to by the weak reference is released, how to deal with the weak pointer? When releasing an object, the basic process is as follows:

Calling objc_release

Because the reference count of the object is 0, dealloc is executed

In dealloc, the _objc_rootDealloc function is called

In _objc_rootDealloc, the object_dispose function is called

Calling objc_destructInstance

***Call objc_clear_deallocating

Let's focus on the last step. The specific implementation of objc_clear_deallocating is as follows:

  1. void objc_clear_deallocating(id obj)
  2. {
  3. ......
  4.  
  5. SideTable *table = SideTable::tableForPointer(obj);
  6.  
  7. // clear any weak table items  
  8. // clear extra retain count and deallocating bit  
  9. // (fixme warn or abort if extra retain count == 0?)  
  10. OSSpinLockLock(&table->slock);
  11. if (seen_weak_refs) {
  12. arr_clear_deallocating(&table->weak_table, obj);
  13. }
  14. ......
  15. }

We can see that in this function, the SideTable instance corresponding to the object is first retrieved. If the object has an associated weak reference, arr_clear_deallocating is called to clear the weak reference information of the object. Let's take a look at the specific implementation of arr_clear_deallocating:

  1. PRIVATE_EXTERN void arr_clear_deallocating(weak_table_t *weak_table, id referent) {
  2. {
  3. weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
  4. if (entry == NULL) {
  5. ......
  6. return ;
  7. }
  8. // zero out references  
  9. for ( int i = 0 ; i < entry->referrers.num_allocated; ++i) {
  10. id *referrer = entry->referrers.refs[i].referrer;
  11. if (referrer) {
  12. if (*referrer == referent) {
  13. *referrer = nil;
  14. }
  15. else   if (*referrer) {
  16. _objc_inform( "__weak variable @ %p holds %p instead of %p\n" , referrer, *referrer, referent);
  17. }
  18. }
  19. }
  20.  
  21. weak_entry_remove_no_lock(weak_table, entry);
  22. weak_table->num_weak_refs--;
  23. }
  24. }

This function first finds the weak_entry_t list corresponding to the object, and then sets the weak references to nil one by one. ***Clear the object records.

Through the above description, we can basically understand the process of a weak reference from birth to death. From this process, we can see that the processing of a weak reference involves various table lookups, additions and deletions, which still consumes a certain amount of time. Therefore, if __weak variables are used in large quantities, it will have a certain impact on performance. So, when should we use weak? The advice given to us by "Objective-C Advanced Programming" is to use the __weak modifier only when avoiding circular references.

In addition, clang also provides a lot of weak reference processing functions, such as objc_loadWeak, objc_destroyWeak, objc_moveWeak, etc. We can find relevant implementations in Apple's open source code. I will study them carefully when I have time.

refer to

"Objective-C Advanced Programming" 1.4: __weak modifier

Clang 3.7 documentation – Objective-C Automatic Reference Counting (ARC)

apple opensource – NSObject.mm

Odds and Ends

CAGradientLayer

The CAGradientLayer class is used to draw a color gradient on its background color to fill the entire shape of the layer, including rounded corners. This class inherits from the CALayer class and is very convenient to use.

Similar to the gradient processing in Quartz 2D, a gradient has a starting position (startPoint) and an ending position (endPoint). Between these two positions, we can specify a set of color values ​​(colors, elements are CGColorRef objects), which can be two or more, and each color value corresponds to a location (locations). In addition, gradients are divided into axial gradients and radial gradients.

Let's write an example to see the specific use of CAGradientLayer:

  1. CAGradientLayer * layer = [CAGradientLayer layer];
  2. layer.startPoint = (CGPoint){0.5f, 0.0f};
  3. layer.endPoint = (CGPoint){0.5f, 1.0f};
  4. layer.colors = [NSArray arrayWithObjects:(id)[UIColor blueColor].CGColor, (id)[UIColor redColor].CGColor, (id)[UIColor greenColor].CGColor, nil];
  5. layer.locations = @[@0.0f, @0.6f, @1.0f];
  6. layer.frame = self .view.layer.bounds;
  7.  
  8. [self.view.layer insertSublayer:layer atIndex:0];

refer to

CAGradientLayer Class Reference

Handling Ineligible Devices in Xcode

I changed to a new computer, installed Xcode 6.3, created a new certificate and profile, opened Xcode, and connected my phone. But then I found that the device was marked as Ineligible Devices and was not recognized. The situation is similar to the following picture:

The computer is trusted, and the certificates and profiles are OK. I tried restarting Xcode and reconnecting the phone several times, but it didn't work. I just can't select the device. *** I selected the device in Product->Destination. But I still can't select it in the toolbar. I'm so frustrated. Please help.

Hide the cursor of UITextField after iOS 7

After the new project only supports iOS 7, many things become much simpler, just like hiding the cursor of UITextField, with a simple sentence:

textFiled.tintColor = [UIColor clearColor];

Usually when we use UIPickerView as our UITextField's inputView, we need to hide the cursor. Of course, if you want to change the cursor color, you can do the same.

There is a legacy problem with this approach: usually when we use UIPickerView as the inputView of UITextField, we don't want to perform various menu operations (select all, copy, paste), but when we just want to set the tintColor of UITextField, we can still perform these operations, so additional processing is required. We can handle this problem like this: in textFieldShouldBeginEditing:, we set the userInteractionEnabled of UITextField to NO, and then in textFieldShouldEndEditing:, set this value back. As follows:

  1. - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
  2.  
  3. textField.userInteractionEnabled = NO;
  4.  
  5. return YES;
  6. }
  7.  
  8. - (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
  9.  
  10. textField.userInteractionEnabled = YES;
  11.  
  12. return YES;
  13. }

That's it. Of course, this is just one way we are currently using. There are other methods, just google or stackoverflow.

Left alignment issue of Chinese text in UIAlertView after iOS 7

Before iOS 7, if we want to display the text in UIAlertView on the left, we can use the following code to handle it:

  1. for (UIView *view in alert.subviews) {
  2. if ([[view class ] isSubclassOfClass:[UILabel class ]]) {
  3. ((UILabel*)view).textAlignment = NSTextAlignmentLeft;
  4. }
  5. }

But unfortunately, after iOS 7, Apple doesn't allow us to do this. When we get the subviews of UIAlertView, we get an empty array and we can't get the label we want. What should we do? Three ways: tell the product manager and UED that this can't be implemented (of course, this will be despised, and people will say you are incompetent); write it yourself; find a third-party open source code. Hehe, but because of the tight schedule recently, I decided to tell them that it can't be implemented, haha. But I found an open source on github, Custom iOS AlertView, with a lot of stars. It looks good, I will study it carefully later.

<<:  Use UIVisualEffectView to add special effects to views

>>:  About Tint Color Properties

Recommend

The truth about the 12 programmers

[[121135]] Some truths about programmers. Includi...

How much does it cost to develop a mini program in Guangzhou?

In an era when smartphone use has become a daily ...

The correct way to “value” social apps

“This is a social APP that will subvert WeChat!” ...

How to promote and operate APP? You need to understand the fundamentals!

With the continuous development of the APP indust...

A tearful suggestion on advertising purchased with a budget of 1 million

Summary of advertising placement on 7 major chann...

Deloitte: Renewable Energy Industry Outlook 2023

U.S. renewable energy growth slows in 2022 as sup...

Why did Li Xiang of CHJ Automotive build a range-extended hybrid car?

Recently, Li Xiang of CHJ Automotive Group offici...

China's Internet mobile phone "civil war" spreads to India

Xiaomi's "Internet phone" concept h...

Where does a spacecraft go when it retires?

Recently, the Tianzhou-3 cargo spacecraft re-ente...