extension(类扩展)

简单来说,extension在.m文件中添加,所以其权限为private,所以只能拿到源码的类添加extension。另外extension是编译时决议,和interface和implement里的代码融合在一块了一般。


category(类别)

category能在不继承类的情况下给类动态添加方法。

1、创建category

关于@dynamic的特性及用法可参考:
https://blog.csdn.net/qq_28446287/article/details/79094491


2、category的优缺点

  • 可以将类的实现代码分散到多个不同的文件或框架中

  • 创建对私有方法的前向引用
    OC语法中,你不能对一个类的方法定义为private,只有+、-,对实例变量可以private、public。
    具体可参考此文档http://www.cnblogs.com/stevenwuzheng/p/5457487.html

  • 向对象添加非正式协议
    对NSObject进行一个类别叫做非正式协议,可以只实现想要的方法


3、category在runtime中的源码

typedef struct objc_category *Category;
struct 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 allocationsmethod_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 firstint 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会有两个method1

category中的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];
}@end

objc_setAssociatedObject和objc_getAssociatedObject的描述可参考:https://www.cnblogs.com/liuting-1204/p/6526342.html

extension(类扩展)和 category(类别)相关推荐

  1. iOS中分类(category)和类扩展(Extension)的区别

    iOS中分类(category)和类扩展(Extension)的区别 一.分类的定义: 分类是OC中的特有语法,它是表示一个指向分类结构体的指针,它是为了扩展系统类的方法而产生的一种方式. 二.分类的 ...

  2. objective-c中Category类别(扩展类)专题总结

    objective-c中Category类别(扩展类)专题总结 objective-c中Category 类别.扩展 专题总结 http://blog.csdn.net/jiajiayouba/art ...

  3. Objective C 类别(Category)与类扩展(Extensions)

    一.类别(Category) 类别(Category)是一种可以为现有的类(包括类簇:NSString...,甚至源码无法获得的类)添加新方法的方式无需从现有的类继承子类.类别添加的新方法可以被子类继 ...

  4. 类扩展(Class Extension)

    1.什么是类扩展 延展类别又称为扩展(Extendsion),Extension是Category的一个特例 可以为某个类扩充一些私有的成员变量和方法 写在.m文件中 英文名是Class Extens ...

  5. Category 类别 -Objective-C

    category 是Objective-C 里面最常用到的功能之一.category 可以为已经存在的类增加方法,而不需要增加一个子类.而且,我们可以在不知道某个类内部实现的情况下,为该类增加方法.如 ...

  6. 12月25号 Category类别

    Category类别 1.在已有类的基础上进行扩展,无需像继承一样子类化,就可以直接添加一些方法 2.继承不仅可以添加方法还可以添加属性,类别只能添加方法 3.类别不会改变现有类的方法,万一重写,自己 ...

  7. ios category类别的使用

    ios category类别的使用 ios category类别的使用 Objective-C提供了一个非常灵活的类(Class)扩展机制-类别(Category).类别用于对一个已经存在的类添加方法 ...

  8. TVM apps extension示例扩展库

    TVM apps extension示例扩展库 此文件夹包含TVM的示例扩展库.演示了其它库如何在C++和Python API中扩展TVM. 该库扩展了TVM的功能. python模块加载新的共享库, ...

  9. Swift类扩展使用方法

    2019独角兽企业重金招聘Python工程师标准>>> Swift是没有OC中的分类的. 但可以进行类扩展, 一般用于实现代理方法. 类扩展中无法定义属性 使用方法: extensi ...

最新文章

  1. 外网远程桌面连接设置
  2. [二叉树]已知后序/中序遍历,求先序遍历
  3. Microbiome:微生物组研究中优化方法和规避误区
  4. AutoX全无人驾驶出租车正式对公众开放试运营
  5. Silverlight导航应用程序Uri映射问题的分析与解决
  6. C# 解决LISTVIEW控件显示数据出现闪屏的问题
  7. pythonfillcolor_openpyxl 填充颜色(单元格)
  8. 不可求的电脑上必备软件,你也许听过
  9. 数据仓库分层 (ODS、DWD、DWS)
  10. ps如何增加可撤回的步数
  11. 资深摄影师眼中 青岛值得一游的景点有哪些之4
  12. 01_安卓APP开发流程概览
  13. pands 画图 调整大小_关于cad画图比例,图纸比例,打印比例问题详解
  14. JavaScript - 正则表达式
  15. Google Play如何做ASO优化?影响搜索排名的主要因素.
  16. 网络基本功:TCP报文及可靠性保证
  17. 【安全知识分享】建筑设计防火规范常用数据.pptx(附下载)
  18. 《科学工程技术、技术到学术的升级演变》
  19. Google Heap Profiler使用方法
  20. 神经网络常见的激活函数汇总

热门文章

  1. nginx error.log中的 favicon.ico 错误
  2. Apache ShenYu ModifyResponse插件使用
  3. opencv实时识别指定物体
  4. html设置行的水平对齐
  5. 第九周项目5 三色球
  6. 抓包导出的har格式解析
  7. 怎么把计算机e盘的文件移动到d盘,怎么把d盘的东西移到e盘
  8. ov5640_mipi.c分析
  9. 查看全文的css,如何通过纯CSS实现“点击展开全文”功能
  10. 2018-2019第一学期Java助教心得