A basis,

Before we dive into isa Pointers, we need to prepare some basics, including bit-fields, unions, and memory allocation.

1.1 a domain

// Define a bit segment
struct mybitfields  
{  
    unsigned short a : 4;  
    unsigned short b : 5;  
    unsigned short c : 7;  
} test;  
  
int main( void );  
{  
    test.a = 2;  
    test.b = 31;  
    test.c = 0;  
}  

// The final display is as follows
00000001 11110010  
cccccccb bbbbaaaa
Copy the code

Bit-fields represent the storage of data in bits within a structure.

There are a few points worth noting about memory allocation in bit-fields:

  • The minimum memory in a bit-field cannot be less than the largest modifier in a bit-field, as the above instances areshort, that is, the minimum cannot be less thanshortThe size of the;
  • The minimum memory for a bit-field cannot be less than the combined size of all the bit-field fields, and depending on memory alignment, excess may be allocated. Assuming that all bit-field fields add up to 18 bits, 32 bits, or 4 bytes, are allocated.
  • The nameless bit field forces the next bit field to be aligned to the boundary of its next type bit field if there is no nameless bit field. (C/C++ bit-field structure in-depth parsing)

1.2 a consortium

Reference C language common (Union)

#include <stdio.h>
union data{
    int n;
    char ch;
    short m;
};
int main(a){
    union data a;
    printf("%d, %d\n".sizeof(a), sizeof(union data) );
    a.n = 0x40;
    printf("%X, %c, %hX\n", a.n, a.ch, a.m);
    a.ch = '9';
    printf("%X, %c, %hX\n", a.n, a.ch, a.m);
    a.m = 0x2059;
    printf("%X, %c, %hX\n", a.n, a.ch, a.m);
    a.n = 0x3E25AD54;
    printf("%X, %c, %hX\n", a.n, a.ch, a.m);  
    return 0;
}
Copy the code

Running results:

4, 4
40, @, 40
39, 9, 39
2059, Y, 2059
3E25AD54, T, AD54
Copy the code

For bit fields and bit operations, here’s a Demo, originally from MJ.

Second, the isa

Before arm64, ISA was a pointer to a class object or metaclass object.

But after ARM64, it is a union, which contains richer instance object information and stores more runtime information.

It is important to note that the ISAs discussed in this series are iOS only, and 64-bit only.

2.1 isa

Let’s review the nature and classification of objective-C objects and explore isa Pointers:

And, we’ve seen in practice how to get class addresses through ISA:

Now, from objc’s source code, we need to rediscover what ISA is!

2.2 isaOther fields in

We can see that the union also contains other useful fields, as listed below:

Looking at these fields, we verified the following code, which is placed here:

    NSLog(@"1----%p", [BFPerson class]);

    //0: nonpointer: indicates whether the pointer is optimized. 1 indicates that more information is stored
    BFPerson *person1 = [[BFPerson alloc] init];
    Person1 -> ISA 0x000001a100d88e39
    
    //has_assoc, add the associated object
    objc_setAssociatedObject(person1, @"test".@"good", OBJC_ASSOCIATION_RETAIN);
    //person1->isa	0x000001a100d88e3b
    
    // ShiftClass, isa address
    BFPerson *person2 = person1;
    //person1->isa	0x000021a100d88e3b
    
    // Weakly_referenced, is weakly_referenced
    __weak BFPerson *person3 = person1;
    //person1->isa	0x000025a100d88e3b
Copy the code

The results of one validation:

An obscure example

Now that we know all about Isa, let’s take a look at an interesting example from one of Sunny’s interview questions.

I’ve changed it a little bit. Here’s the code,

@interface BFPerson : NSObject
@property (nonatomic.copy) NSString * name;
- (void)print;
@end

@implementation BFPerson
- (void)print
{
    NSLog(@"my name is %@".self.name);
}
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    id cls = [BFPerson class];
    void *obj = &cls;
    [(__bridge id)obj print];
}
@end
Copy the code

Look up there, two questions, right?

  1. Does this code work?

  2. Running, what’s the end result?

    The answer is yes, and the output looks like this:

my name is <ViewController: 0x7fc1c6e15df0>

So the question comes, this' name 'how to come. To explain this, we need to understand the memory layout of 'OC/C' and the function call stack frame: the first question is: can it run?Copy the code

3.1 Can it run?

Let’s look at how the method is normally called:

	BFPerson *person = [[BFPerson alloc] init];
  [person print];
Copy the code

Let’s take a look at this interview question:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    id cls = [BFPerson class];
    void *obj = &cls;
    [(__bridge id)obj print];
}
Copy the code

Now, we compare the two calls as follows:

As can be seen from the above picture:

The way they work in memory is very similar. Let’s recall how an instance object calls an instance method:

  1. Based on the instance object, find itisaPointer;
  2. According to theisaFind the class object, the class object stores instance methods, find the method list, find instance methods;
  3. Call instance methods;

Whether it’s [obj print] or [Person print], the goal is to find the class object, where person is a pointer to the instance object, so of course you can find it.

So! Can 'OBj' be found? Yes, 'obj' points to 'CLS' and 'CLS' points directly to a class object, so 'obj' can find a class object by 'CLS', which is the equivalent of the pointer to 'isa'. So, now that you can find the class object, it makes sense to call the instance method.Copy the code

Our goal is to find the first pointer variable after CLS.

3.2 Discussion on running results

Here is our final output function, which prints the name of the instance object:

- (void)print
{
    NSLog(@"my name is %@".self.name);
}
Copy the code

From the above discussion, we know that CLS is very similar to the ISA in the instance object Person, which is essentially a variant of the ISA pointer.

And finally person finds its name property which is the first member variable value directly after the isa pointer, and it has to be a pointer variable, because we know name isa pointer variable.

So, we just need to find the first pointer variable after CLS.

Now, back to the function:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    id cls = [BFPerson class];
    void *obj = &cls;
    [(__bridge id)obj print];
}
Copy the code

ViewDidLoad is a function, local variables distributed in stack space according to function stack frame.

Now convert the above code to C++ code.

//[super viewDidLoad];
(
    (__rw_objc_super){
        self,
        class_getSuperclass(objc_getClass("ViewController"))
    }, sel_registerName("viewDidLoad")); id cls = (objc_msgSend)(objc_getClass("BFPerson"), sel_registerName("class"));
void *obj = &cls;
(objc_msgSend)(obj, sel_registerName("print"));
Copy the code

Focus on the first line. In OC, calling super converts super to a structure, as we’ll see later:

struct objc_super {
    __unsafe_unretained _Nonnull id receiver;
    __unsafe_unretained _Nonnull Class class;
};
Copy the code

The other lines are very familiar.

Now let’s look at the local variables inside the function:

  • objc_superTemporary variable
  • cls
  • obj

Their memory space distribution is as follows:

We have now found the first pointer variable to CLS, self, and this is the result, given at the beginning:

my name is <ViewController: 0x7fc1c6e15df0>

We can also verify this:

Let’s see if it’s the first variable after CLS:

    NSString *test = @"good";
    id cls = [BFPerson class];
    void *obj = &cls;
    [(__bridge id)obj print];
Copy the code

The above will print the value of test directly.

In addition, let’s check if it’s ok if it’s not a pointer variable.

    NSString *test = @"good";
    int age = 18;
    id cls = [BFPerson class];
    void *obj = &cls;
    [(__bridge id)obj print];
Copy the code

The final result is as follows:

reference

link

  1. C/C++ bit-field structure in-depth parsing
  2. C Language Common (Union)
  3. Objc4 source

The sample code

  1. Bit operation
  2. Isa pointer
  3. Interesting examples of ISA