Detailed explanation: Objective-C Class Ivar Layout

Detailed explanation: Objective-C Class Ivar Layout

This exploration stems from a question asked by a friend. When we define an instance variable of a class, we can specify its modifier:

  1. @interface Sark : NSObject {
  2. __strong id _gayFriend; // Objects without modifiers will be added with __strong by default  
  3. __weak id _girlFriend;
  4. __unsafe_unretained id _company;
  5. }
  6. @end  

This allows ivar (instance variable) to be correctly reference counted under ARC just like properties.

So the question is, if this class is dynamically generated:

  1. Class class = objc_allocateClassPair(NSObject. class , "Sark" , 0 );
  2. class_addIvar( class , "_gayFriend" , sizeof(id), log2(sizeof(id)), @encode (id));
  3. class_addIvar( class , "_girlFriend" , sizeof(id), log2(sizeof(id)), @encode (id));
  4. class_addIvar( class , "_company" , sizeof(id), log2(sizeof(id)), @encode (id));
  5. objc_registerClassPair( class );

How do you add property modifiers to ivar like above?

After some investigation, I found that the modification information of ivar is stored in the Ivar Layout of Class:

  1. struct class_ro_t {
  2. uint32_t flags;
  3. uint32_t instanceStart;
  4. uint32_t instanceSize;
  5. #ifdef __LP64__
  6. uint32_t reserved;
  7. #endif
  8. const uint8_t * ivarLayout; // <- records which ivars are strong  
  9.  
  10. const   char * name;
  11. const method_list_t * baseMethods;
  12. const protocol_list_t * baseProtocols;
  13. const ivar_list_t *ivars;
  14. const uint8_t * weakIvarLayout; // <- records which ivars are weak  
  15. const property_list_t *baseProperties;
  16. };

ivarLayout and weakIvarLayout respectively record which ivars are strong or weak. What is not recorded are the basic types and __unsafe_unretained object types.

These two values ​​can be accessed through several APIs provided by the runtime:

  1. const uint8_t *class_getIvarLayout(Class cls)
  2. const uint8_t *class_getWeakIvarLayout(Class cls)
  3. void class_setIvarLayout(Class cls, const uint8_t *layout)
  4. void class_setWeakIvarLayout(Class cls, const uint8_t *layout)

But we are unlikely to use these APIs. The value of IvarLayout is determined by the runtime, so there is no need to care about its existence. However, in order to solve the above problems, we tried to crack the encoding method of IvarLayout.

For example, if the class is defined as:

  1. @interface Foo : NSObject {
  2. __strong id ivar0;
  3. __weak id ivar1;
  4. __weak id ivar2;
  5. }
  6. @end  

The value of ivarLayout storing strong ivar is 0x012000

The value of weakIvarLayout that stores weak ivar is 0x1200

A uint8_t is two digits in hexadecimal, so the encoded value is a pair of two digits. Take the above ivarLayout as an example:

The first two digits 01 indicate that there are 0 non-strong objects and 1 strong object.

The next two digits 20 indicate that there are 2 non-strong objects and 0 strong objects.

***The two 00s are the end character, just like the \0 in cstring

Similarly, the weakIvarLayout above:

The first two digits 12 indicate that there is 1 non-weak object and the next 2 consecutive weak objects.

00 End character

In this way, two layout code values ​​can be used to check whether an ivar is strong or weak. If neither is found, it means that the object is unsafe_unretained.

As an exercise, if the class is defined as:

  1. @interface Bar : NSObject {
  2. __weak id ivar0;
  3. __strong id ivar1;
  4. __unsafe_unretained id ivar2;
  5. __weak id ivar3;
  6. __strong id ivar4;
  7. }
  8. @end  

The value of ivarLayout storing strong ivar is 0x012100

The value of weakIvarLayout that stores weak ivar is 0x01211000

  1. Class class = objc_allocateClassPair(NSObject. class , "Sark" , 0 );
  2. class_addIvar( class , "_gayFriend" , sizeof(id), log2(sizeof(id)), @encode (id));
  3. class_addIvar( class , "_girlFriend" , sizeof(id), log2(sizeof(id)), @encode (id));
  4. class_addIvar( class , "_company" , sizeof(id), log2(sizeof(id)), @encode (id));
  5. class_setIvarLayout( class , ( const uint8_t *) "\x01\x12" ); // <--- new  
  6. class_setWeakIvarLayout( class , ( const uint8_t *) "\x11\x10" ); // <--- new  
  7. objc_registerClassPair( class );

I thought I had solved this problem, but the runtime continued to slap me in the face, and the strong and weak memory management did not take effect. I continued to study and found that there is a flag in the class flags to record whether the class is ARC. When the class is compiled normally and marked with the -fobjc-arc flag, this flag is 1, but the dynamically created class does not set it. So I can only continue to use black magic to set this flag at runtime. I will not go into details about the exploration process. The implementation is as follows:

  1. static   void fixup_class_arc(Class class ) {
  2. struct {
  3. Class isa;
  4. Class superclass;
  5. struct {
  6. void *_buckets;
  7. uint32_t _mask;
  8. uint32_t _occupied;
  9. } cache;
  10. uintptr_t bits;
  11. } *objcClass = (__bridge typeof(objcClass)) class ;
  12. # if !__LP64__
  13. #define FAST_DATA_MASK 0xfffffffcUL
  14. # else  
  15. #define FAST_DATA_MASK 0x00007ffffffffff8UL
  16. #endif
  17. struct {
  18. uint32_t flags;
  19. uint32_t version;
  20. struct {
  21. uint32_t flags;
  22. } *ro;
  23. } *objcRWClass = (typeof(objcRWClass))(objcClass->bits & FAST_DATA_MASK);
  24. #define RO_IS_ARR 1flags |= RO_IS_ARR;
  25. }

After putting this fixup in objc_registerClassPair(class);, the dynamic class can finally operate ivar like the statically compiled class. You can test it:

  1. id sark = [ class   new ];
  2. Ivar weakIvar = class_getInstanceVariable( class , "_girlFriend" );
  3. Ivar strongIvar = class_getInstanceVariable( class , "_gayFriend" );
  4. {
  5. id girl = [NSObject new ];
  6. id boy = [NSObject new ];
  7. object_setIvar(sark, weakIvar, girl);
  8. object_setIvar(sark, strongIvar, boy);
  9. } // ARC will release the girl and boy in the curly braces here  
  10. // Output: weakIvar is nil, strongIvar has a value  
  11. NSLog(@ "%@, %@" , object_getIvar(sark, weakIvar), object_getIvar(sark, strongIvar));

Done.

<<:  5 Best AngularJS Program Building Frameworks

>>:  4 memory usage issues game developers need to pay attention to

Recommend

What are the ranking factors for link popularity of a specific page?

The ranking factors for link popularity of a spec...

Late? Actually they did it on purpose...

(Image source: BBC) Have you ever seen people lik...

Live streaming sales: Solve these 3 fatal problems first

Today, I suddenly felt like talking to you about ...

Advanced on-demand marketing techniques for video platforms!

Recently, the advanced on-demand broadcast of &qu...

How to place a good advertisement? How to acquire customers at low cost?

1 The purpose of advertising is either branding o...

Ideas for creating Baidu bidding SEM plan!

For the person in charge of bidding SEM, the most...

It turns out there is a flower called Qingming Flower

For many people in the southern region, eating Qi...

Advertising creative ecology sinks

About seven or eight years ago, a friend who ran ...

Are you afraid of getting fat when dining out? You may be blaming carbohydrates

What is the source of happiness? It must be carbo...

User growth analysis: How to segment users?

Introduction: In the growth analysis of a product...