美文网首页
iOS 中方法调用的顺序

iOS 中方法调用的顺序

作者: gxxljss | 来源:发表于2019-12-04 11:34 被阅读0次

iOS中的方法在Runtime时调用的流程大家都很熟悉,通过objc_msgSend方法查找到对应的方法的实现,然后运行。但是,如果一个方法同时在类,类的父类,多个Category中存在,这个方法最后怎么执行的呢?

首先,看看一个方法同时在类及类的父类中存在时的执行顺序。

定一个类Person,类Student是Person的子类,Student中重写Person的study。

@interface Person : NSObject

- (void)speak;

- (void)study;

@end

@implementation Person

- (void)study {
    NSLog(@"Method:%s",__PRETTY_FUNCTION__);
}

- (void)speak {
    NSLog(@"Method:%s",__PRETTY_FUNCTION__);
}

@end

@interface Student : Person

- (void)study;

@end

@implementation Student

- (void)study {
    NSLog(@"Method:%s",__PRETTY_FUNCTION__);
}

@end

执行下面的代码

Student *student = [[Student alloc] init];
[student study];
[student speak];

输出log

2019-11-27 14:40:56.225884+0800 TestMethod[31612:209770] Method:-[Student study]
2019-11-27 14:40:56.226024+0800 TestMethod[31612:209770] Method:-[Person speak]

可以看出study执行的是Student中的方法,speak执行的是Person中的方法。

这其实与objc_msgSend执行有关

1、通过对象的isa指针找到类对象

2、在类对象的objc_cache中查找cache的方法,如果查找到,则直接执行,否则执行下一下

3、在类对象的objc_method_list中查找,如果查找到,则直接执行,并添加到cache,否则执行下一下

4、在类对象的super_class中查找,如果查找到,则直接执行,并添加到cache中

在工程中再添加一个Student的Category Student(pupil)

@interface Student (pupil)

- (void)study;

@end

@implementation Student (pupil)

- (void)study {
    NSLog(@"Method:%s",__PRETTY_FUNCTION__);
}

@end

重新执行上面的代码,结果如下

2019-11-27 15:47:59.121235+0800 TestMethod[40834:289455] Method:-[Student(pupil) study]
2019-11-27 15:47:59.121369+0800 TestMethod[40834:289455] Method:-[Person speak]

可以看到study方法执行的是Category中的方法,speak执行的是Person中的方法

这是因为Category在关联到类的时候会执行attachCategories,整理类得方法、属性和协议列表

// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order, 
// oldest categories first.
static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    while (i--) {
        auto& entry = cats->list[I];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist = 
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    auto rw = cls->data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}

将Category中的方法放到了类中原方法的前面,所以当我们调用的时候会优先调用Category中的方法。

在工程中再添加一个Student的Category Student(junior)

@interface Student (junior)

- (void)study;

@end

@implementation Student (junior)

- (void)study {
    NSLog(@"Method:%s",__PRETTY_FUNCTION__);
}

@end

重新执行上面的代码,结果如下

2019-11-27 16:19:49.194570+0800 TestMethod[45288:330161] Method:-[Student(pupil) study]
2019-11-27 16:19:49.194698+0800 TestMethod[45288:330161] Method:-[Person speak]

在Compile Source中修改Student+pupil和Student+junior的顺序


Xcode Compile Sources.png

重新执行上面的代码,结果如下

2019-11-27 16:22:29.941082+0800 TestMethod[45670:333499] Method:-[Student(junior) study]
2019-11-27 16:22:29.941269+0800 TestMethod[45670:333499] Method:-[Person speak]

可以看出Category中相同的方法,最后执行与文件编译的前后有关系,执行后编译的Category中的方法

相关文章

网友评论

      本文标题:iOS 中方法调用的顺序

      本文链接:https://www.haomeiwen.com/subject/afgkgctx.html