Small valley bottom exploration collection

  • Ah,, cause: I encountered a bug in the project these days, and my leader changed the code. I can’t say anything. The legendary black magic (ios-method swizzling) he can’t use, ready to write a little mocking him 😆.

  • There are many related articles on the net, but he does not check, this matter is troubled by ~~ (who let somebody else is a leader.

1. Method swizzlingSimple use of

    1. Common requirement: add dot statistics after coming into the page ~

At this point, of course, you can add them manually. But it will certainly add to your groan.

    1. Heh heh, the right time to introduceMethod swizzling“, which is extremely comfortable
    1. Simply write down the usage code

Guys, I’m just going to write the comments right in the code. (Today is mainly to analyze the source code with brothers ~)


// Let's create a category
#import #import 
       
         @implementation UIViewController (XG) only in load // Swizzling changes the global state. We want to make sure that swizzling executes once and is available on every thread. GCD singleton is perfect static dispatch_once_t onceToken; Dispatch_once (&onceToken, ^{dispatch_once(&onceToken, ^{)); // @selector SEL originalSeletor = @selector(viewDidAppear:); SEL swizzlingSelector = @selector(xg_textViewDidAppear:); //method Method originalMethod = class_getInstanceMethod(class, originalSeletor); Method swizzlingMethod = class_getInstanceMethod(class, swizzlingSelector); // Class class2 = object_getClass((id)self); // Method originalMethod2 = class_getClassMethod(class2, <#SEL _Nonnull name#>) // Method swizzlingMethod2 = class_getClassMethod(class2, BOOL didAddMethod = class_addMethod(class, originalSeletor, originalSeletor) method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod)); If (didAddMethod) {// successful. Replace the class_replaceMethod(class, swizzlingSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else{// Implementations(originalMethod, swizzlingMethod); }}); } - (void) xg_textViewDidAppear:(BOOL)animated{ [self xg_textViewDidAppear:animated]; NSLog(@"
       xg_textViewDidAppear : %@",self); } @endCopy the code

This time when entering the interface will print ~

    1. Let’s just look at the output.

Method Swizzling is quite comfortable to use

2. Method swizzlingThe source code

I was going to look at assembly calls, but when I saw the new objC4-818.2 source code, I did a project debugging on a sudden

2.1. Lock the source code

    1. Most brothers analyze problems by looking at source code. This approach is very 👍. (I am 😆, often under the guise of looking at the source water)

 

    1. Let’s analyze the source code ~
    • 2.1 There are three main methodsclass_addMethod,class_replaceMethod,method_exchangeImplementations

 

    1. Let’s analyzeclass_addMethodclass_replaceMethod

Source code implementation ~

BOOL 
class_addMethod(Class cls, SEL name, IMP imp, const char *types)
{
    if(! cls)return NO;

    mutex_locker_t lock(runtimeLock);
    return! addMethod(cls, name, imp, types ? :"", NO);
}


IMP 
class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
{
    if(! cls)return nil;

    mutex_locker_t lock(runtimeLock);
    returnaddMethod(cls, name, imp, types ? :"", YES);
}
Copy the code
    1. Analysis of difference ~

    1. Breakpoint debugging ~ (I create a new oneXGPersonAnd then call the method)

They all end up with the addMethod method

2.2. Method swizzlingSource code analysis

    1. addMethodmethods
static IMP 
addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
{
    IMP result = nil;

    runtimeLock.assertLocked();

    checkIsKnownClass(cls);
    
    ASSERT(types);
    ASSERT(cls->isRealized());

    method_t *m;
    if ((m = getMethodNoSuper_nolock(cls, name))) {
        // already exists
        if(! replace) { result = m->imp(false);
        } else{ result = _method_setImplementation(cls, m, imp); }}else {
        // fixme optimize
        method_list_t *newlist;
        newlist = (method_list_t *)calloc(method_list_t::byteSize(method_t::bigSize, 1), 1);
        newlist->entsizeAndFlags = 
            (uint32_t)sizeof(struct method_t::big) | fixed_up_method_list;
        newlist->count = 1;
        auto &first = newlist->begin()->big();
        first.name = name;
        first.types = strdupIfMutable(types);
        first.imp = imp;

        addMethods_finish(cls, newlist);
        result = nil;
    }

    return result;
}
Copy the code
    1. Guys, I drew itMind mappingAlthough the comparison of paintinglow, ha ha

    1. method_exchangeImplementationsSource code analysis
void method_exchangeImplementations(Method m1, Method m2)
{
    if(! m1 || ! m2)return;

    mutex_locker_t lock(runtimeLock);

    IMP imp1 = m1->imp(false);
    IMP imp2 = m2->imp(false);
    SEL sel1 = m1->name();
    SEL sel2 = m2->name();

    m1->setImp(imp2);
    m2->setImp(imp1);


    // RR/AWZ updates are slow because class is unknown
    // Cache updates are slow because class is unknown
    // fixme build list of classes whose Methods are known externally?

    flushCaches(nil, __func__, [sel1, sel2, imp1, imp2](Class c){
        return c->cache.shouldFlush(sel1, imp1) || c->cache.shouldFlush(sel2, imp2);
    });

    adjustCustomFlagsForMethodChange(nil, m1);
    adjustCustomFlagsForMethodChange(nil, m2);
}
Copy the code

This code is written clearly ~

And this is also very vivid description of SEL and IMP relationship.

3. The conclusion

  • I hope this article will be helpful to you (I may not be qualified to say ~). More importantly, I hope this article will be seen by the leader ~😆