美文网首页Objective-c
iOS进阶回顾二「Category的研究」

iOS进阶回顾二「Category的研究」

作者: Coder东 | 来源:发表于2019-08-26 22:25 被阅读0次

iOS进阶回顾一「Runtime」

分类相信大家都不陌生,经常会用到但是它到底是怎么实现的呢?
  • 首先我们先新建一个分类:
//
//  SFPerson+Helper.h
//  Category
//
//  Created by 随风流年 on 2019/8/26.
//  Copyright © 2019 随风流年. All rights reserved.
//

#import "SFPerson.h"

NS_ASSUME_NONNULL_BEGIN
@interface SFPerson (Helper)
-(void)run;
@end

NS_ASSUME_NONNULL_END


#import "SFPerson+Helper.h"
@implementation SFPerson (Helper)
-(void)run {
    NSLog(@"SFPerson (Helper) run");
}
@end
  • 然后利用终端切换到当前文件夹下输入:
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc SFPerson+Helper.m
    就可以得到编译后的.cpp文件得到如下图所示的分类的底层结构:
image.png
从上图可以看到分类是一个category_t 的struct每一个分类都会生成这样的一个结构体
继续查看我们会看到一个变量:
B5832FA14B2395EE9124029BC6C30423.png
在这个结构体变量中又可以查看到我们的方法 1641451F798259ABC9A60DFD18430F5A.png
由此可见分类是生成一个category_t的一个结构体,并没有合并到类中

Category编译后的底层结构是 struct category_t,里面存放着分类的对象方法、类方法、属性、协议信息,在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中)

查看底层实现:

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);
}
附加的实现 D65FDC798C1F6C5037D3441B03D731EA.png
  • 我们可以看到 附加时,里面 有一个realloc方法重新分配内存,利用oldCount + addedCount 对数组进行扩容。利用memmovememcpy进行地址的移动和拷贝,将原来列表的位置移动或者拷贝到数组的后面,使分类的列表放在数组前面的位置
 memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));

所以同样的方法会优先调用分类

  • 类的扩展也简单说下:
  • 类的扩展是(匿名分类\类扩展),它一编译就合并到类中,可用于把属性或者成员变量私有化,外部不可以方法在.m中建立 @interface SFPerson()进行声明属性方法

相关文章

网友评论

    本文标题:iOS进阶回顾二「Category的研究」

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