问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

iOS中的category和extension

发布网友 发布时间:2022-10-16 22:36

我来回答

1个回答

热心网友 时间:2023-10-24 23:43

(一)Category

category是Objective-C 2.0之后添加的语言特性,别人口中的分类、类别其实都是指的category。category的主要作用是为已经存在的类添加方法。除此之外,apple还推荐了category的另外两个使用场景。

可以把类的实现分开在几个不同的文件里面。这样做有几个显而易见的好处。

可以减少单个文件的体积

可以把不同的功能组织到不同的category里

可以由多个开发者共同完成一个类

可以按需加载想要的category

声明私有方法

apple 的SDK中就大面积的使用了category这一特性。比如UIKit中的UIView。apple把不同的功能API进行了分类,这些分类包括UIViewGeometry、UIViewHierarchy、UIViewRendering等。

不过除了apple推荐的使用场景,广大开发者脑洞大开,还衍生出了category的其他几个使用场景:

模拟多继承(另外可以模拟多继承的还有protocol)

把framework的私有方法公开

2、category特点

category只能给某个已有的类扩充方法,不能扩充成员变量。

category中也可以添加属性,只不过@property只会生成setter和getter的声明,不会生成setter和getter的实现以及成员变量。

如果category中的方法和类中原有方法同名,运行时会优先调用category中的方法。也就是,category中的方法会覆盖掉类中原有的方法。所以开发中尽量保证不要让分类中的方法和原有类中的方法名相同。避免出现这种情况的解决方案是给分类的方法名统一添加前缀。比如category_。

如果多个category中存在同名的方法,运行时到底调用哪个方法由编译器决定,最后一个参与编译的方法会被调用。

如下图,给UIView添加了两个category(one 和 two),并且给这两个分类都添加了名为log的方法:

在viewController中引入这两个category的.h文件。调用log方法:

当编译顺序如下图所示时,调用UIView + one.m的log方法,如下图:

将UIView + one.m移动到UIView + two.m上面,调用UIView + two.m的log方法,如下图:

3、调用优先级

分类(category) > 本类 > 父类。即,优先调用cateory中的方法,然后调用本类方法,最后调用父类方法。

注意:category是在运行时加载的,不是在编译时。

4、为什么category不能添加成员变量?

Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。它的定义如下:

typedef struct objc_class *Class;

14struct objc_class {

Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__

Class super_class                       OBJC2_UNAVAILABLE;// 父类

const char *name                        OBJC2_UNAVAILABLE;// 类名

long version                            OBJC2_UNAVAILABLE;// 类的版本信息,默认为0

long info                               OBJC2_UNAVAILABLE;// 类信息,供运行期使用的一些位标识

long instance_size                      OBJC2_UNAVAILABLE;// 该类的实例变量大小

struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;// 该类的成员变量链表

struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;// 方法定义的链表

struct objc_cache *cache                OBJC2_UNAVAILABLE;// 方法缓存

struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;// 协议链表

#endif

} OBJC2_UNAVAILABLE;

在上面的objc_class结构体中,ivars是objc_ivar_list(成员变量列表)指针;methodLists是指向objc_method_list指针的指针。在Runtime中,objc_class结构体大小是固定的,不可能往这个结构体中添加数据,只能修改。所以ivars指向的是一个固定区域,只能修改成员变量值,不能增加成员变量个数。methodList是一个二维数组,所以可以修改*methodLists的值来增加成员方法,虽没办法扩展methodLists指向的内存区域,却可以改变这个内存区域的值(存储的是指针)。因此,可以动态添加方法,不能添加成员变量。

5、category中能添加属性吗?

Category不能添加成员变量(instance variables),那到底能不能添加属性(property)呢?

这个我们要从Category的结构体开始分析:

8typedef struct category_t {

const char *name;//类的名字

classref_t cls;//类

struct method_list_t *instanceMethods;//category中所有给类添加的实例方法的列表

struct method_list_t *classMethods;//category中所有添加的类方法的列表

struct protocol_list_t *protocols;//category实现的所有协议的列表

struct property_list_t *instanceProperties;//category中添加的所有属性

} category_t;

从Category的定义也可以看出Category的可为(可以添加实例方法,类方法,甚至可以实现协议,添加属性)和不可为(无法添加实例变量)。

但是为什么网上很多人都说Category不能添加属性呢?

实际上,Category实际上允许添加属性的,同样可以使用@property,但是不会生成_变量(带下划线的成员变量),也不会生成添加属性的getter和setter方法的实现,所以,尽管添加了属性,也无法使用点语法调用getter和setter方法(实际上,点语法是可以写的,只不过在运行时调用到这个方法时候会报方法找不到的错误,如下图)。但实际上可以使用runtime去实现Category为已有的类添加新的属性并生成getter和setter方法。详细内容可以看峰哥之前的文章:《 iOS Runtime之四:关联对象 》

需要注意的有两点:

1)、category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA。

2)、category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休,殊不知后面可能还有一样名字的方法。

(二)Extension

1、什么是extension

extension被开发者称之为扩展、延展、匿名分类。extension看起来很像一个匿名的category,但是extension和category几乎完全是两个东西。和category不同的是extension不但可以声明方法,还可以声明属性、成员变量。extension一般用于声明私有方法,私有属性,私有成员变量。

2、extension的存在形式

category是拥有.h文件和.m文件的东西。但是extension不然。extension只存在于一个.h文件中,或者extension只能寄生于一个类的.m文件中。比如,viewController.m文件中通常寄生这么个东西,其实这就是一个extension:

2@interface ViewController ()

@end

当然我们也可以创建一个单独的extension文件,如下图:

UIView_extension.h中声明方法:

导入UIView_extension.h文件进行使用:

注意:extension常用的形式并不是以一个单独的.h文件存在,而是寄生在类的.m文件中。

(三)category和extension的区别

就category和extension的区别来看,我们可以推导出一个明显的事实,extension可以添加实例变量,而category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。

extension在编译期决议,它就是类的一部分,但是category则完全不一样,它是在运行期决议的。extension在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它、extension伴随类的产生而产生,亦随之一起消亡。

extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension,除非创建子类再添加extension。而category不需要有类的源码,我们可以给系统提供的类添加category。

extension可以添加实例变量,而category不可以。

extension和category都可以添加属性,但是category的属性不能生成成员变量和getter、setter方法的实现。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
Linux系统安装FTP服务器 Linux系统的网络文件共享 建筑的七盏明灯的内容简介 面向对象设计七大原则 简单说 交互设计七大定律 交互设计的“根”——七大定律 交互设计原则和理论2——七大定律 七大设计原则 附近的加油站有哪些 附近的加油站有哪些地方 丰台区槐树岭至古城的327公交线南营站正在建别墅小区(国望府),请问怎么与售楼处联系? 怎么样让QQ显示错误的地理位置 怎样让自己QQ显示错误的地理位置 如何让QQ报告错误的地理位置? 赏樱花作文 鼋头渚的樱花,比日本强太多了 情定鼋头渚的樱花雨,春天里最浪漫的情侣约会圣地 韩国现代集团的现代旗下产业 四川双马是什么公司?四川双马连续三年业绩?四川双马股票属于什么股票? 四川双马是什么行业板块?四川双马什么时候公布业绩?四川双马属于哪个版块? 番茄火锅的制作方法 番茄火锅怎么做 扔文具刀有什么忌讳吗? 马钢股份的股市行情,600808的股市行情 广州市海珠区怡乐路嘉怡酒店到白云机场多远 从化海景酒店在什么位置 昆明哪里有卖烩面的? 广州火车站附近怎么搭车去台山的嘉怡温泉大酒店 鄂尔多斯市东胜区凤凰城东嘉怡大酒店对面的建行是什么支行 梦见水有什么暗示 怎么开始学习炒股?求解答 IOS中堆和栈的区别 设计题目—单向链表 vivo手机密码保险箱在哪 怎样恢复出厂设置啊,我现在把密码给忘了,非常 老公有外遇我如何处理 开机后就弹出一个很A的广告删不掉关不掉 A站发广告怎么直接就封禁 国外广告最高级别是几A? 什么是四A广告公司? 待处理财产损益不是资产 但是属于资产类账户 为什么? 资产类账户和资产是同一个概念吗? 梅毒患者能活多久 有先天性梅毒的人能活多久? 梅毒不治能活多久?治疗能活多久? 国珍亚麻油的好处是什么? 关于国珍亚麻籽油的问题? 农业资源与环境怎么样 给婴儿用睡袋好吗?怎样选择合适的睡袋以及选睡袋的一些注意事项有哪些? 美国多肽 HGH真有用么?用过的进来说下。谢谢 芊姿美态胶原多肽是骗局吗 在直银行存款有风险?