类扩展(extension)
1. 为一个类增加私有的:属性,方法,成员变量
2. 类扩展没有 独立的实现部分(@implementation部分),和本类共享一个实现。类扩展所声明的方法必须依托对应宿主类的实现部分来实现。实现写在原类的 .m实现文件中。
都是私有的,默认是@private类型,在外界中无法直接使用,只能在定义声明的.m 文件中使用,作用范围只能在自身类。
扩展特点 : 编译时决议
1. 不能为系统类添加扩展
2. 类扩展添加的新方法,一定要实现, 不然会编译警告⚠️, 类扩展是在编译阶段被添加到类中
类扩展 (extension)是 分类 (category) 的一个特例,也被称为匿名分类或者私有类
Category 为原类提供了拓展性:category_t 存储了类别中可以拓展的实例方法、类方法、协议、实例属性和类属性。
注意:类属性是 Objective-C 2016 年新增的特性,沾 Swift 的光。所以 category_t 中有些成员变量是为了兼容 Swift 的特性,Objective-C 暂没提供接口,仅做了底层数据结构上的兼容。
分类结构体添加处理category到类的工作会先于类的加载处理 (+load方法的执行)。
在load方法中可以调用category中声明的方法。
在dyld调用静态构造函数之前,libc 会调用 _objc_init()
category被附加到类上面是在map_images的时候发生的,在new-ABI的标准下,objc_init里面调用的map_images最终会调用 read_images方法。
read_images 函数间接调用到 attachCategories 函数,完成向类中添加 Category 的工作。原理就是向 class_rw_t 中的 method_array_t, property_array_t, protocol_array_t 数组中分别添加 method_list_t, property_list_t, protocol_list_t 指针。
1. 先使用 unattachedCategoriesForClass 函数获取类中还未添加的类别列表。这个列表类型为 locstamped_category_list_t,它封装了 category_t 以及对应的 header_info。
2. attachCategories 将 locstamped_category_list_t.list 列表中每个 locstamped_category_t.cat 中的方法、协议和属性。分别添加到类的 class_rw_t 对应列表中。
3. header_info 存储了实体在镜像中的加载和初始化状态,以及一些偏移量,在加载 Mach-O 文件相关函数中经常用到。header_info 中的信息决定了是否是元类,应该是添加实例方法还是类方法、实例属性还是类属性等。
分类(category)的作用
1. 可以在不修改原来类的基础上,为一个类扩展方法。例如:给系统自带的类扩展方法。
注意:扩充系统自带的类的功能。例如,给NSObject增加分类会影响到所有的类,需要谨慎使用
2. 分类中可以添加实例方法、类方法、协议、属性。从 category_t 结构体中可以看出分类结构体中不存在成员变量的,因此分类是不允许添加成员变量的。
注⚠️:成员变量是存放在类结构体对象中的,并且编译的那一刻就已经决定好了。而分类是在运行时才去加载的。就无法在程序运行时将分类的成员变量中添加到类的结构体中。因此分类中不可以添加成员变量。
3. 可以访问原来类中的成员变量,但是只能访问@protect和@public形式的变量。如果想要访问本类中的私有变量,分类和子类一样,只能通过方法来访问
4. Category可以重新定义新方法,也可以override继承过来的方法。
5. 运行时决议, 分类是在运行时添加到类中。
6. 如果在分类用@property 定义生命属性,只能生成getter ,setter方法的声明,不会生成setter和getter方法 和成员变量。
注:实际上可以通过 runtime 来关联对象。但是使用runtime 也只能实现 setter 和 getter 方法,而没有_成员变量,如果调用_成员变量,程序还是会报错。
7. 分解体积庞大的类文件,减少单个文件的体积,按照功能分组,放到不同的分类里,使类结构更清晰,降低耦合性,同一个类可以有多个开发人员进行开发,模拟多继承
8. 分类中有和原有类同名的方法,会优先调用分类中的方法,就是说会忽略原有类的方法。所以同名方法调用的优先级为:分类>本类>父类
9. 如果多个分类中都有和原有类同名的方法,那么调用该用法的时候执行谁由 编译器决定,编译器会执行最后一个被编译的分类中的方法。
注意:
1. 分类的实现原理是将 category中的方法,属性,协议数据放在 category_t 结构体中,然后将 分类category 中的方法列表拷贝插入到类对象的方法列表中,头插法(放在数组最前面)
2. 分类方法会覆盖原类方法,是编译器决定的,编译器首先编译类,然后再编译分类,在类和分类里面写两个相同的方法,2个方法都存在method_list中。然而在方法调用的时候, objc_msgSend() 函数是通过 selector 去查找类中对应的 struct objc_method,这个涉及到objc_msgSend() 在method_list 查找 struct objc_method 方式和顺序。由于是栈结构,找到分类的方法,就不会继续查找本类,所以看起来是覆盖了,实际这两个方法都存在
协议 & 继承 & 分类 & 组合的区别和优缺点:
继承: 一个子类拥有被继承类(父类)的全部属性和方法。
OC是单继承:一个类只能继承一个直接父类
继承的缺点:
1. 高耦合性 ,父类的改变影响所有的子类。代码的复用性较差。修改父类的代码,对所有的子类有影响,子类都要进行适配修改。
注意:某个类代码复用时,要拷贝所有相关依赖类;牵一而动全身。
2. 对父类很大的依赖,要求对父类的工作流程相对熟悉
3. 继承体系如果太复杂会导致整个系统混乱,难以维护。最好不要超过2层
继承的优点:
1. 代码重用,可以提供一系列公用接口和属性,具体的实现由子类来根据不同的业务进行补充。方法的实现可以选择性的部分重写实现。
父类只提供方法,默认什么都不做,需要子类重写。例如:layoutSubviews
1. 1: 当子类中需要有自己独特的行为,不想使用父类的方法,可以把父类的方法覆盖掉:直接在子类.m 文件中重写相同名字的方法。
1.2.: 如果重写了父类的方法,还想使用父类的功能。使用 super 调用父类的这个同名方法。super 代指父类。
2. 子类是可以访问父类category中的方法,需要引入分类的头文件。
比如要定义一个继承自UIViewController的类,就不能用Category,因为,定义的这个类中,要实现UIViewController中的viewDidLoad、init等方法,用了category后父UIViewController中的这些方法将无法被调用;
Protocol : 提供一套公用的接口,不能提供实现,实现方式类似于抽象基类,并不是一个类。
协议中的方法可以选择性实现,协议也可以增加属性。
协议和代理并无必然的联系,在 OC 中常用协议来实现代理,但是不用协议也可以实现委托。把一个对象A 设置为对象B 的一个属性,通过B.A 去调用A 的方法等。
Category优缺点:
是对原类的一种补充,这个类的主要基本功能都具备了,用category为这个类添加不同的组件,使得这个类能够适应不同情况的需求(但是这些不同需求最核心的需求要一致)。一个类可以有多个不同的分类。Category可以按不同的功能将类的实现分在不同的模块中实现。避免类过于臃肿复杂。
缺点:
1. 一个类可以定义多个category,但是如果不同category中存在相同方法,编译器调用的是最后一个被编译分类中的方法,不能保证你自己方法一定会被调用
2. 重写系统方法,会导致这个方法的实现在整个框架中发生了变化。如果你增加了NSObject中 windowWillClose:的实现,这会导致所有的窗口调用那个新实现的方法,从而改变所有NSWindows实例的行为。这会带来很多不确定性, 并很有可能导致程序的崩溃
3. 不提倡对原有方法进行重载。在category中进行重载,无法对原方法进行访问。
优点:
1. category 可以仅仅给出方法定义,而不需要给出具体的实现。这在程序增量开发时是非常有帮助的;
2. category是可以被继承的。在某个父类中定义了category,那么他所有的子类都具有该category;
3. 在需要为某个类创建私有成员方法时,也用category的方式来实现。扩展(匿名分类)
组合:可以复用代码。继承is A的关系,组合是has A 的关系
组合就是B 类拥有一个A类的对象。这样B 就可以使用A 这个类中的所有方法和属性。A 类和B类 之间可以使用protocol进行联络。 类似于delegate 。比如:UITableview 。
组合的优点:
①:类B 只能通过所包含的类A 去调用A 的方法,对象A的内部细节类B是不可见的。
②:当低耦合关系,如果修改类A中代码不需要修改B的代码。
③:B对象可以在运行时动态的绑定所包含的对象A。可以通过set方法给所包含对象赋值。
组合的缺点:①:容易产生过多的对象。
组合比继承更具灵活性和稳定性,在设计的时候优先使用组合。
多继承的间接实现:多继承3种实现方法,协议,分类,消息转发
消息转发和继承的区别 :NSObject类中 respondsToSelector: 和 isKindOfClass: 这类方法只会考虑继承体系,不会考虑转发链。如果通过消息转发实现了messageA 方法 ,在调用类的respondsToSelector:@(messageA)时还是返回NO 。
参考:
iOS分类(category),类扩展(extension)—史上最全攻略 - 简书
iOS 分类(Category)_xiaoxiaobukuang的专栏-CSDN博客_ios分类 底层讲解,详细👍
category 详解_Kurry的博客-CSDN博客 👍👍👍,里面有参考链接
继承 & 组合 & 协议的区别:
继承和面向接口(iOS架构思想篇) - CocoaChina_一站式开发者成长社区
底层了解:
iOS 懒加载类和非懒加载类 - 简书 map_images 方法详解
iOS底层探索之_objc_init - 简书 运行C++静态构造函数。在dyld调用静态构造函数之前,libc 会调用 _objc_init()
网友评论