博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
extension(类扩展)和 category(类别)
阅读量:4600 次
发布时间:2019-06-09

本文共 8435 字,大约阅读时间需要 28 分钟。

extension(类扩展)

 

简单来说,extension在.m文件中添加,所以其权限为private,所以只能拿到源码的类添加extension。另外extension是编译时决议,和interface和implement里的代码融合在一块了一般。category(类别)category能在不继承类的情况下给类动态添加方法。1、创建category

 

关于@dynamic的特性及用法可参考:https://blog.csdn.net/qq_28446287/article/details/790944912、category的优缺点    可以将类的实现代码分散到多个不同的文件或框架中

 

创建对私有方法的前向引用    OC语法中,你不能对一个类的方法定义为private,只有+、-,对实例变量可以private、public。    具体可参考此文档http://www.cnblogs.com/stevenwuzheng/p/5457487.html    向对象添加非正式协议    对NSObject进行一个类别叫做非正式协议,可以只实现想要的方法3、category在runtime中的源码typedef struct objc_category *Category;    1struct objc_category {    //类别的名字    char * _Nonnull category_name                            OBJC2_UNAVAILABLE;    //该类的名字    char * _Nonnull class_name                               OBJC2_UNAVAILABLE;    //实例方法列表    struct objc_method_list * _Nullable instance_methods     OBJC2_UNAVAILABLE;    //类方法列表    struct objc_method_list * _Nullable class_methods        OBJC2_UNAVAILABLE;    //协议列表    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;}                                                            OBJC2_UNAVAILABLE;明显看出,Category没有容纳变量的地方。4、category的原理objective-c的运行依赖runtime,runtime依赖于dyld动态加载,查看objc-os.mm文件发现其调用栈如下:void _objc_init(void){    static bool initialized = false;    if (initialized) return;    initialized = true;    // fixme defer initialization until an objc-using image is found?    environ_init();    tls_init();    static_init();    lock_init();    exception_init();    // Register for unmap first, in case some +load unmaps something    _dyld_register_func_for_remove_image(&unmap_image);    dyld_register_image_state_change_handler(dyld_image_state_bound,                                             1/*batch*/, &map_2_images);    dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);}category被附加到类上是在map_images的时候发生的。在new-ABI的标准下,_objc_init函数里调用的map_iamges最终会调用objc-runtime-new.mm中的_read_images函数。_read_images中部分代码: // Discover categories.     for (EACH_HEADER) {        category_t **catlist =             _getObjc2CategoryList(hi, &count);        for (i = 0; i < count; i++) {            category_t *cat = catlist[i];            Class cls = remapClass(cat->cls);            if (!cls) {                // Category's target class is missing (probably weak-linked).                // Disavow any knowledge of this category.                catlist[i] = nil;                if (PrintConnecting) {                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "                                 "missing weak-linked target class",                                  cat->name, cat);                }                continue;            }            // Process this category.             // First, register the category with its target class.             // Then, rebuild the class's method lists (etc) if             // the class is realized.             bool classExists = NO;            if (cat->instanceMethods ||  cat->protocols                  ||  cat->instanceProperties)             {                addUnattachedCategoryForClass(cat, cls, hi);                if (cls->isRealized()) {                    remethodizeClass(cls);                    classExists = YES;                }                if (PrintConnecting) {                    _objc_inform("CLASS: found category -%s(%s) %s",                                  cls->nameForLogging(), cat->name,                                  classExists ? "on existing class" : "");                }            }            if (cat->classMethods  ||  cat->protocols                  /* ||  cat->classProperties */)             {                addUnattachedCategoryForClass(cat, cls->ISA(), hi);                if (cls->ISA()->isRealized()) {                    remethodizeClass(cls->ISA());                }                if (PrintConnecting) {                    _objc_inform("CLASS: found category +%s(%s)",                                  cls->nameForLogging(), cat->name);                }            }        }    }    ts.log("IMAGE TIMES: discover categories");从代码可以看出:将category和它的主类(或元类)注册到哈希表中.如果主类(或元类)已经实现,那么重建它的方列表。category中的实例方法和属性被整合到主类中,而类方法被整合到元类中。而协议被同时整合到了主类和元类中。/************************************************************************ remethodizeClass* Attach outstanding categories to an existing class.* Fixes up cls's method list, protocol list, and property list.* Updates method caches for cls and its subclasses.* Locking: runtimeLock must be held by the caller**********************************************************************/static void remethodizeClass(Class cls){    category_list *cats;    bool isMeta;    runtimeLock.assertWriting();    isMeta = cls->isMetaClass();    // Re-methodizing: check for more categories    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {        if (PrintConnecting) {            _objc_inform("CLASS: attaching categories to class '%s' %s",                          cls->nameForLogging(), isMeta ? "(meta)" : "");        }        attachCategories(cls, cats, true /*flush caches*/);                free(cats);    }}如注释所说,此函数将未处理的category整合到主类(或元类),整合cls的方法、协议、属性列表,更新cls及其子类的方法缓存。查看其中的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);        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);}通过while循环,遍历所有的category,得每个category的方法列表mlist、proplist和protolist并存入主类(或元类)的mlists、proplists和protolists中。从而更新类的数据字段data()中mlist、proplist和protolist的值。category没有替换掉原来类的方法,也就是说如果category和原来类有method1,那么在将category整合到原来类之后,类的method_list会有两个method1category中的method1会被放在类的method_list前面,而原来类的method1被放 到了method_list后面,在调用时候会先调用method_list前面的,所以看起来是将原来类的method1覆盖了,实际上并不是那么回事。5、category的两个面试题3.1 一个类和它的category同时拥有一个方法,在调用时候调用哪一个?答案参考“2、category的原理”3.2 一个类有多个category并都拥有同一个方法,在调用时候调用哪一个?答案参考“2、category的原理”举个例子://#import "type.m"- (void)test{    NSLog(@"type class!!");}//#import "type+xxxx.m"- (void)test{    NSLog(@"category xxxx");}//#import "type+xxxx1.m"- (void)test{    NSLog(@"category xxxx1");}//#import "type+xxxx2.m"- (void)test{    NSLog(@"category xxxx2");}

 

 

 

 

 

可以知道,输出的结果跟compile source文件中的category的.m文件放置顺序有关。且放最底部的时候输出(主类.m文件的放置不影响,理由参考”2、category的原理”)6、category动态添加变量@interface type (xxxx)@property (nonatomic, assign)  NSInteger number;@endstatic void *numberKey = &numberKey;@implementation type (xxxx)- (void)setNumber:(NSInteger)number{    objc_setAssociatedObject(self, &numberKey, [NSString stringWithFormat:@"%ld",number], OBJC_ASSOCIATION_ASSIGN);}- (NSInteger)number{    return [objc_getAssociatedObject(self, &numberKey) integerValue];}@endobjc_setAssociatedObject和objc_getAssociatedObject的描述可参考:https://www.cnblogs.com/liuting-1204/p/6526342.html

 

转载于:https://www.cnblogs.com/Free-Thinker/p/10797048.html

你可能感兴趣的文章
Java MVC 分页实例
查看>>
响应式布局1--媒体查询和-webkit-min-device-pixel-ratio
查看>>
CocoaPods应用于iOS项目框架管理方案
查看>>
POJ-3233 Matrix Power Series 矩阵A^1+A^2+A^3...求和转化
查看>>
IIS是如何处理ASP.NET请求的
查看>>
SSIS之Foreach循环容器应用
查看>>
局域网内访问机器时出现“未授予在次计算机上的请求登陆类型”
查看>>
硬币组合问题
查看>>
(9)模板层 - templates(模板语言、语法、取值、过滤器、变量的使用)
查看>>
P3469 [POI2008]BLO-Blockade
查看>>
P1171 售货员的难题
查看>>
DevOps之持续交付
查看>>
有趣的数学(一)
查看>>
迟来的2013年总结及算法工程师/研究员找工作总结
查看>>
java面向对象中的关键字
查看>>
网络类型IPv4和IPv6什么意思?区别?
查看>>
6周学习计划,攻克JavaScript难关(React/Redux/ES6 etc.)
查看>>
大对象堆及.NET垃圾回收器的改进
查看>>
utf-8引发的页面空白
查看>>
MicroPHP 2.2.0 发布
查看>>