Method Swizzle for runtime related problems

The remaining problems in memory are mainly as follows:

  1. Method SwizzleMatters needing attention
  2. What is the internal implementation of the attribute modifier Atomic? Is it thread safe
  3. What are some ways of introspection in iOS? What is the internal implementation
  4. class,objc_getClass,object_getclassWhat’s the difference?

Method SwizzleMatters needing attention

  1. Note the side effects of the implementation of the exchange method.method_exchangeImplementations(). Swapping method functions will end up withobjc_msgSend()Way to call, side effects are mainly focused on the first parameter as shown in the following example
objc_msgSend(payment, @selector(quantity))
Copy the code

Calling the Quantity method after the method exchange might crash. The way to resolve this side effect is to replace the original exchange with method_setImplementation(), which makes the most sense.

  1. Avoid swapping superclass methods

If the current class does not implement the swapped method and the parent class does, then the implementation of the parent class will be swapped. If multiple successors of the parent class are swapped, multiple exchanges will be caused and chaos will occur. At the same time, calling the parent class method may crash because the method signature cannot be found. So before the exchange should check whether the current class to add a new implementation of the swapped function IMP, this process is roughly divided into 3 steps

  • class_addMethodCheck Whether a method can be added
  BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
Copy the code

Add an implementation IMP to SEL of CLS, return YES to indicate that CLS does not implement the method, return NO to indicate that CLS has implemented the method. Note: The success of adding this method depends entirely on the class itself, regardless of whether the parent class has this method or not.

  • class_replaceMethodThe function that replaces SEL of class CLS is implemented as IMP
    class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
             const char * _Nullable types)
Copy the code
  • method_exchangeImplementationsFinal method interchange
 method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
Copy the code
  1. The swap method should be in the +load method

We talked about this earlier when we talked about message forwarding, that +load is not implemented as message forwarding and is called when the class is loaded during runtime initialization, and the parent class, current class,category, subclass, and so on are all called once. So this is the best place to write hook(Method Swizzle) for Method swapping.

  1. Swapped classification methods should add custom prefixes to avoid conflicts

Method swizzling (swizzling, swizzling, swizzling, swizzling, swizzling

What is the internal implementation of the attribute modifier Atomic? Is it thread safe?

Atomic internal implementation

id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { ... id *slot = (id*) ((char*)self + offset); if (! atomic) return *slot; // Atomic retain release world spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); id value = objc_retain(*slot); slotlock.unlock(); return objc_autoreleaseReturnValue(value); }Copy the code
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) { ... if (! atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); } objc_release(oldValue); }Copy the code

Property atomic is implemented using spinlock_t spin locks.

Is it thread safe?

Atomic by this method. At run time only the atomicity of set and GET methods is guaranteed. So using atomic is not thread-safe.

What are some ways of introspection in iOS? What is the internal implementation?

The first thing to understand is the term introspection, which in iOS development we call reflection.

Introspection methods such as isKindOfClass in NSObject, which is a common way of doing introspection or reflection, but I think NSClassFromString() should be a reflection method as well.

Several methods of introspection in iOS

Let’s look at it from nsobject.h

- (BOOL)isKindOfClass:(Class)aClass; - (BOOL)isMemberOfClass:(Class)aClass; - (BOOL)conformsToProtocol:(Protocol *)aProtocol; + (BOOL)conformsToProtocol:(Protocol *) Protocol; - (BOOL)respondsToSelector:(SEL)aSelector; / / interpretation instance such method + (BOOL) instancesRespondToSelector (SEL) aSelector; // Check whether the class has this method...Copy the code

Internal implementation principle

  1. isKindOfClass:
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
	
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
Copy the code

The class method is used to determine whether it ISA subclass of a class by getting the address bit of the metaclass where the ISA pointer data is stored. The instance method is implemented by using the objc_object::getIsa() function to retrieve the class flat check from the class of the ISA pair in the form of a stored tag_ext table.

  1. isMemberOfClass:
+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
Copy the code

These two methods are very simple to compare directly to the ISA pointer

  1. conformsToProtocol:
+ (BOOL)conformsToProtocol:(Protocol *)protocol { if (! protocol) return NO; for (Class tcls = self; tcls; tcls = tcls->superclass) { if (class_conformsToProtocol(tcls, protocol)) return YES; } return NO; } - (BOOL)conformsToProtocol:(Protocol *)protocol { if (! protocol) return NO; for (Class tcls = [self class]; tcls; tcls = tcls->superclass) { if (class_conformsToProtocol(tcls, protocol)) return YES; } return NO; }Copy the code

Isa ->data()->protocols

BOOL class_conformsToProtocol(Class cls, Protocol *proto_gen) { protocol_t *proto = newprotocol(proto_gen); if (! cls) return NO; if (! proto_gen) return NO; mutex_locker_t lock(runtimeLock); checkIsKnownClass(cls); ASSERT(cls->isRealized()) for (const auto& proto_ref : cls->data()->protocols) { protocol_t *p = remapProtocol(proto_ref); if (p == proto || protocol_conformsToProtocol_nolock(p, proto)) { return YES; } } return NO; }Copy the code

Here you can clearly see the for loop fetching the relevant protocol pointer and comparing the pointer to the proTO generated by the passed parameter

  1. respondsToSelector:
+ (BOOL)respondsToSelector:(SEL)sel {
    return class_respondsToSelector_inst(self, sel, self->ISA());
}

- (BOOL)respondsToSelector:(SEL)sel {
    return class_respondsToSelector_inst(self, sel, [self class]);
}
Copy the code

The source code for this is a little bit more cumbersome and I’m just going to give you a little bit of a rundown of the call stack where you go all the way to the method that the current instance can respond to, and if the current class doesn’t have one, you go to the parent class, if the parent class doesn’t have one, you go to the metaclass.

| __ lookUpImpOrNil respondsToSelector: | __ class_respondsToSelector_inst () () | __ lookUpImpOrForward () returns the IMPCopy the code

This is the whole message forwarding process is not described here.

I’ve listed some of the common introspection methods above, but the rest of the methods are basically nothing special. They are the return results of the functions that get the relevant attributes inside the various ISA operations.

class,objc_getClass,object_getclassWhat’s the difference?

I’m just going to create a demo in Xcode and print out the viewControoller

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    Class cls1 = [self class];
    Class cls2 = object_getClass(cls1);
    Class cls3 = objc_getClass(object_getClassName([self class]));
    NSLog(@"%p",cls1);
    NSLog(@"%p",cls2);
    NSLog(@"%p",cls3);
}
@end
Copy the code

The output

2020-08-31 16:15:48.150285+0800 ClassDemo[5582:55836] 0x10205b3b0
2020-08-31 16:15:48.150456+0800 ClassDemo[5582:55836] 0x10205b3d8
2020-08-31 16:15:48.150575+0800 ClassDemo[5582:55836] 0x10205b3b0
Copy the code

I have a simple list

operation class object_getclass() objc_getClass()
Incoming parameters N/a Id type A string for the class name
Action object obj The Class to which the ISA pointer to this ID points The class object of this class
Instance object andobject_getclass()consistent andclassconsistent N/a
Class object/metaclass object The returned message object itself The next object is returned N/a

Reason: Because class returns self and object_getClass returns the object to which Isa points

conclusion

These are the remaining parts of the “Efficient iOS interview questions organized answers runtime related questions 3”. Although the answers are short, each question is very accurate. Next time we will cover the notification part and try to organize all the questions as soon as possible.

Sharing:Big factory iOS often ask interview questions (with answers)

Since the | address