extension(类扩展)和 category(类别)
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(类别)相关推荐
- iOS中分类(category)和类扩展(Extension)的区别
iOS中分类(category)和类扩展(Extension)的区别 一.分类的定义: 分类是OC中的特有语法,它是表示一个指向分类结构体的指针,它是为了扩展系统类的方法而产生的一种方式. 二.分类的 ...
- objective-c中Category类别(扩展类)专题总结
objective-c中Category类别(扩展类)专题总结 objective-c中Category 类别.扩展 专题总结 http://blog.csdn.net/jiajiayouba/art ...
- Objective C 类别(Category)与类扩展(Extensions)
一.类别(Category) 类别(Category)是一种可以为现有的类(包括类簇:NSString...,甚至源码无法获得的类)添加新方法的方式无需从现有的类继承子类.类别添加的新方法可以被子类继 ...
- 类扩展(Class Extension)
1.什么是类扩展 延展类别又称为扩展(Extendsion),Extension是Category的一个特例 可以为某个类扩充一些私有的成员变量和方法 写在.m文件中 英文名是Class Extens ...
- Category 类别 -Objective-C
category 是Objective-C 里面最常用到的功能之一.category 可以为已经存在的类增加方法,而不需要增加一个子类.而且,我们可以在不知道某个类内部实现的情况下,为该类增加方法.如 ...
- 12月25号 Category类别
Category类别 1.在已有类的基础上进行扩展,无需像继承一样子类化,就可以直接添加一些方法 2.继承不仅可以添加方法还可以添加属性,类别只能添加方法 3.类别不会改变现有类的方法,万一重写,自己 ...
- ios category类别的使用
ios category类别的使用 ios category类别的使用 Objective-C提供了一个非常灵活的类(Class)扩展机制-类别(Category).类别用于对一个已经存在的类添加方法 ...
- TVM apps extension示例扩展库
TVM apps extension示例扩展库 此文件夹包含TVM的示例扩展库.演示了其它库如何在C++和Python API中扩展TVM. 该库扩展了TVM的功能. python模块加载新的共享库, ...
- Swift类扩展使用方法
2019独角兽企业重金招聘Python工程师标准>>> Swift是没有OC中的分类的. 但可以进行类扩展, 一般用于实现代理方法. 类扩展中无法定义属性 使用方法: extensi ...
最新文章
- 外网远程桌面连接设置
- [二叉树]已知后序/中序遍历,求先序遍历
- Microbiome:微生物组研究中优化方法和规避误区
- AutoX全无人驾驶出租车正式对公众开放试运营
- Silverlight导航应用程序Uri映射问题的分析与解决
- C# 解决LISTVIEW控件显示数据出现闪屏的问题
- pythonfillcolor_openpyxl 填充颜色(单元格)
- 不可求的电脑上必备软件,你也许听过
- 数据仓库分层 (ODS、DWD、DWS)
- ps如何增加可撤回的步数
- 资深摄影师眼中 青岛值得一游的景点有哪些之4
- 01_安卓APP开发流程概览
- pands 画图 调整大小_关于cad画图比例,图纸比例,打印比例问题详解
- JavaScript - 正则表达式
- Google Play如何做ASO优化?影响搜索排名的主要因素.
- 网络基本功:TCP报文及可靠性保证
- 【安全知识分享】建筑设计防火规范常用数据.pptx(附下载)
- 《科学工程技术、技术到学术的升级演变》
- Google Heap Profiler使用方法
- 神经网络常见的激活函数汇总