Previous article: Use of Runtime at work

1. What happens when objC sends a message to an object?

Objc when sending message to an object, the runtime will according to the actual object isa pointer to find the object belongs to the class, then the list of methods in the class and its parent class method list looking for a way to run, if all the way to the root class haven’t found, turn to intercept calls, message forwarding mechanism, once found, is to carry out its implementation IMP.

A: See Runtime at work in Chapter 2.

2. What happens when objC sends a message to a nil object?

If you send a message to a nil object, the first thing you do when you look for the isa pointer to the object is that the 0 address is returned, so you don’t get any errors. It doesn’t collapse.

A good answer: If a method returns an object, the message sent to nil returns 0(nil);

If the method returns a pointer whose pointer is an integer scalar less than or equal to sizeof(void*), float, double, long double, or long long, the message sent to nil will return 0;

If the method returns a struct, the message sent to nil returns 0. The value of each field in the structure will be 0;

If the return value of a method is not one of the cases mentioned above, the return value of a message sent to nil will be undefined.

3. Send messages [obj foo] and to an object in objcobjc_msgSend()What are the relationships between functions?

When objC is compiled, [obj foo] is converted to: objc_msgSend(obj, @selector(foo)); .

A: See Runtime at work in Chapter 2.

4. When is an exception raised for an unrecognized selector?

When objC sends a message to an object, the Runtime library will find the class to which the object actually belongs according to the isa pointer of the object, and then run the method in the method list of the class and its parent class. If no corresponding method can be found in the topmost parent class, the runtime library will enter the message forwarding stage. If the three-way message forwarding process is not implemented, the program hangs at run time and throws an exception unrecognized selector sent to XXX.

A: Please refer to the application of Runtime in the third chapter.

5. Can I add instance variables to the compiled class? Can I add instance variables to classes created at run time? Why is that?

You cannot add instance variables to compiled classes.

Ability to add instance variables to classes created at run time

1. Because the compiled class is registered with the Runtime, the linked list of objc_iVAR_list instance variables and the memory size of instance_size instance variables in the class structure have been determined. At the same time, the Runtime calls class_setVARLayout or class_setWeaklvarLayout to handle strong weak references. You cannot add instance variables to existing classes.

2. Create classes at runtime that can add instance variables and call class_addIvar. But after calling objc_allocateClassPair, before objc_registerClassPair, for the same reason.

6. What elements in the class structure change when an attribute is added to a class?

Instance_size: memory size of the instance; Objc_ivar_list *ivars: Property list

7. What does an isa pointer to an objc object point to? What does it do?

Points to his class object so that you can find methods on that object

Best Answer: The following diagram illustrates the relationship between objects, classes, and metaclasses:

The solid line is the super_class pointer and the dotted line is the ISA pointer.

  1. Root class is really just NSObject, and NSObject has no superclass, so the superclass of Root class points to nil.
  2. Each Class has an ISA pointer to a unique Meta Class
  3. The superclass of Root class(meta) points to Root class(class), which is NSObject, forming a loop.
  4. The ISA pointer to each Meta class points to Root class (Meta).

8.[self class] and [super Class]

What does the following code output?

   @implementation Son : Father
   - (id)init
   {
       self = [super init];
       if (self) {
           NSLog(@"% @", NSStringFromClass([self class]));
           NSLog(@"% @", NSStringFromClass([super class]));
       }
       return self;
   }
   @end
Copy the code

NSStringFromClass([self class]) = Son NSStringFromClass([super class]) = Son

A: This question is about understanding self and super in Objective-C.

Self is the class’s hidden argument, pointing to the instance of the class that is currently calling the method;

Super is essentially a compiler identifier that is the same message recipient that self points to. The difference is that super tells the compiler that when a method is called, it calls the method of the parent class, not the method in this class.

When a method is called with self, it looks from the list of methods in the current class. If not, it looks from the parent class. When you use super, you start with the list of methods in the parent class. Then call this method of the parent class.

When calling [super Class], the Runtime will call objc_msgSendSuper instead of objc_msgSend;

OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )

/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained id receiver;

    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained Class class;
#else
    __unsafe_unretained Class super_class;
#endif
    /* super_class is the first class to search */
};
Copy the code

In the objc_msgSendSuper method, the first argument is an objc_super structure that has two variables, the receiver for the message and the super_class, the parent of the current class.

Objc_msgSendSuper should work like this: Look for a selector from the superClass parent’s list of methods pointed to by the objC_super structure, and call the parent’s selector with objC -> Receiver. Note that the last caller is objc->receiver, not super_class!

Then objc_msgSendSuper ends up changing to:

// Start with the parent class msgSend, Objc_msgSend (objc_super->receiver, @selector(class)) /// Specifies an instance of a class. This is an instance of the class __unsafe_unretained ID receiver; // This is an instance call, so it is a minus method - (Class) Class {return object_getClass(self);
}
Copy the code

The IMP of the class method in NSObject was found, and the incoming parameter objc_super->receiver = self was passed. Self is son, calling class, so the parent class executes IMP, and the output is son, and the output is son, both are the same.

9. How does runtime find the corresponding IMP address by selector?

Each class object has a method list, which records the name of the method, the method implementation, and the parameter type. In fact, the selector is essentially the method name, and the corresponding method implementation can be found in the method list through the method name.

10. What does the _objc_msgForward function do, and what happens when you call it directly?

_objc_msgForward is an IMP type for message forwarding: when a message is sent to an object and it is not implemented, _objc_msgForward will attempt to forward the message.

A: _objc_msgForward involves the following methods in the process of message forwarding:

  1. resolveInstanceMethod:Method (orresolveClassMethod:).
  2. forwardingTargetForSelector:methods
  3. methodSignatureForSelector:methods
  4. forwardInvocation:methods
  5. doesNotRecognizeSelector:methods

For details, see: Application of Runtime in Work chapter 3 Runtime method Call Process;

11. How does runtime automatically set weak variables to nil? You know SideTable?

The Runtime lays out the registered classes and puts weak objects into a hash table. If the reference count of this object is 0, dealloc will be dealloc. If the memory address of the weak object is A, then a will be searched in the weak table, find all the weak objects with a as the key, and set to nil.

A more detailed answer:

1. Initialization: The Runtime calls the objc_initWeak function and initializes a new weak pointer to the address of the object. 2. When adding a reference: the objc_initWeak function will call objc_storeWeak(), which updates the pointer pointer and creates the corresponding weak reference table. 3. When releasing, call the clearDeallocating function. The clearDeallocating function first fetches an array of weak pointer addresses based on the object’s address, then iterates through the array to set it to nil, deletes the entry from the Weak table, and clears the object’s record.

The SideTable structure is responsible for managing the reference count table and weak table of the class.

1. Initialization: The Runtime calls the objc_initWeak function and initializes a new weak pointer to the address of the object.

{
    NSObject *obj = [[NSObject alloc] init];
    id __weak obj1 = obj;
}
Copy the code

When we initialize a weak variable, the Runtime calls the objc_initWeak function in nsobject. mm.

// the compiler's mock code id obj1; objc_initWeak(&obj1, obj); /* objc_destroyWeak(&obj1); /* objc_destroyWeak(&obj1);Copy the code

Initialize the “variable with the weak modifier (obj1)” with the objc_initWeak function, and release the variable (obj1) at the end of the scope with the objc_destoryWeak function.

2. When adding a reference: the objc_initWeak function will call objc_storeWeak(), which updates the pointer pointer and creates the corresponding weak reference table.

After initializing the “variable with the weak modifier (obj1)” to nil, the objc_initWeak function calls the objc_storeWeak function with the “assignment object” (obj) as an argument.

Obj1 = 0; obj_storeWeak(&obj1, obj);Copy the code

In other words:

The pointer to the weak modifier defaults to nil (it is safe to send messages to nil in Objective-C)

The obj_destroyWeak function then calls the objc_storeWeak function, taking 0 (nil) as an argument.

objc_storeWeak(&obj1, 0);
Copy the code

The previous source code is the same as the following.

// the compiler's mock code id obj1; obj1 = 0; objc_storeWeak(&obj1, obj); / *... Obj's reference count becomes 0, set to nil... */ objc_storeWeak(&obj1, 0);Copy the code

The objc_storeWeak function takes the memory address of the assignment object (obj) of the second argument as the key value and registers the memory address of the attribute variable (obj1) modified by the first __weak argument into the weak table. If the second argument (obj) is 0 (nil), remove the address of the variable (obj1) from the weak table.

Since an object can be assigned to multiple variables with an __weak modifier, the addresses of multiple variables can be registered for a single key value.

Objc_storeWeak (&a, b) is objc_storeWeak(value, key), and when the key becomes nil, set value to nil. A and B point to the same memory address when B is non-nil, and a becomes nil when B becomes nil. Sending a message to A does not crash at this point: it is safe to send a message to nil in Objective-C.

3. When releasing, call the clearDeallocating function. The clearDeallocating function first fetches an array of weak pointer addresses based on the object’s address, then iterates through the array to set it to nil, deletes the entry from the Weak table, and clears the object’s record.

What happens to the weak pointer when the object to which the weak reference points is released? When an object is released, the basic flow is as follows:

1. Call objc_release 2. Execute dealloc 3 because the reference count of the object is 0. In dealloc, the _objc_rootDealloc function 4 is called. Object_dispose function 5 is called in _objc_rootDealloc. Call objc_destructInstance 6. Finally call objc_clear_dealLocating

The objc_clear_deallocating function that is called when the object is released:

2. Assign nil to all addresses containing the weak modifier variable contained in the record. 3. Delete the address of the weak object from the weak table. 4. Delete the address of the weak object from the reference count table

Conclusion:

The Weak table is a hash table where Key is the address of the Weak object and Value is the address of the Weak pointer.

12. IsKindOfClass isMemberOfClass

What does the following code output?

@interface Sark : NSObject
@end
@implementation Sark
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
        BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
        BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
        BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
        NSLog(@"%d %d %d %d", res1, res2, res3, res4);
    }
    return 0;
}
Copy the code

1000

A:

In isKindOfClass, there is a loop that evaluates whether the class is equal to the super class of the meta class, and then evaluates whether the class is equal to the super class of the meta class, and so on.

[NSObject Class] after it’s done, it calls isKindOfClass, and the first time it checks whether the meta class of NSObject and NSObject are equal, and when WE talked about meta class we put a very detailed graph, and we saw from that graph, The meta class of NSObject is not the same as itself. Then the second loop checks whether NSObject is equal to the superclass of the Meta Class. Again, from that diagram, we can see that the superclass of Root class(meta) is Root class(class), which is NSObject itself. So the second loop is equal, so the first line res1 output should be YES.

Similarly, isKindOfClass is called after the execution of [Sark class]. The first for loop, Sark’s Meta class is different from that of [Sark class]. The second for loop, The super Class of the Sark Meta Class refers to the NSObject Meta Class, which is not equal to the Sark Class. The third for loop, the super Class of NSObject Meta Class points to NSObject Class, which is not equal to Sark Class. The fourth loop, the super Class of NSObject Class points to nil, not equal to Sark Class. After the fourth loop, the loop exits, so the third line res3 outputs NO.

IsMemberOfClass gets its own isa pointer and compares it to see if it is equal. The second line isa refers to the Meta Class of NSObject, so it is not equal to the NSObject Class. In line 4, isa refers to Sark’s Meta Class, and Sark Class is not equal, so res2 and res4 both print NO.

13. Should objects associated with the Runtime Associate method be released when the main object is dealloc?

Not required either under MRC or ARC. Associated objects are released much later in their life cycle than the objects themselves in object_Dispose () method called by nsobject-dealloc.

A:

Call-release: The reference count goes to zero. The object is being destroyed and its life cycle is about to end. No new __weak references, 2, parent class call -dealloc in the most directly inherited parent class call -dealloc if MRC code will manually release the instance variables (iVars) in the inheritance of each layer of the parent class call again Call object_Dispose () method in Objective-C Runtime >4. Call object_dispose() for C++ instance variables (iVars) and call destructors -release for ARC instance variables (iVars) to disengage all use of runtime The Associate method removes all __weak references to the object and calls free().Copy the code

14. What is Method Swizzling?

Simply put, it’s a method swap

A: See the application of Runtime at work in Chapter 5.

When you call a method in Objective-C, you’re actually sending a message to an object, and the only way to find the message is by the name of the selector. By taking advantage of the dynamic characteristics of Objective-C, the corresponding method implementation of selector can be swapped at run time to achieve the purpose of hook method.

Each class has a list of methods, storing the Method name and Method implementation mapping, selector is the essence of the Method name, IMP is a bit like a function pointer, pointing to a specific Method implementation, by selector you can find the corresponding IMP.

Several ways to change methods

  • Method_exchangeImplementations swaps implementations of two methods
  • Implementation of the class_replaceMethod replacement method
  • Use method_setImplementation to set the IMP of a method directly

126.compile Error/Runtime Crash/NSLog… 126.compile Error/Runtime Crash/NSLog… ?

The following code will? Compile Error/Runtime Crash/NSLog… ?

@interface NSObject (Sark)
+ (void)foo;
- (void)foo;
@end

@implementation NSObject (Sark)
- (void)foo {
    NSLog(@"IMP: -[NSObject (Sark) foo]"); } @end // Test code [NSObject foo]; [[NSObject new] performSelector:@selector(foo)];Copy the code

IMP: -[NSObject(Sark) foo], all output normal, compile and run without problems.

A:

The first call will find the method from the metaclass, but the method is not in the metaclass, so find the superclass of the metaclass. The method is defined in the Category of NSObject, and because NSObject has a special object model, the superclass of the metaclass is a class object, you find the method from the class object and call it.

Thanks to:

Frost God, iOS program dog Yuan, Sunnyxx