美文网首页
继承,分类 和 扩展&组合&协议 & 关联对象

继承,分类 和 扩展&组合&协议 & 关联对象

作者: 小李不木 | 来源:发表于2021-08-06 12:30 被阅读0次

    类扩展(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开发的分类和扩展 - 简书

    iOS分类(category),类扩展(extension)—史上最全攻略 - 简书

    给分类(Category)添加属性 - 简书

    iOS 分类(Category)_xiaoxiaobukuang的专栏-CSDN博客_ios分类  底层讲解,详细👍

    category 详解_Kurry的博客-CSDN博客  👍👍👍,里面有参考链接

    继承 & 组合 & 协议的区别:

    iOS架构师之路:慎用继承 - 华过的痕迹 - 博客园  

    继承和面向接口(iOS架构思想篇) - CocoaChina_一站式开发者成长社区

    iOS我不使用继承的原因和总结 - 简书

    底层了解:

    iOS底层探索之map_images - 简书 

    iOS 懒加载类和非懒加载类 - 简书  map_images 方法详解 

    iOS底层探索之_objc_init - 简书 运行C++静态构造函数。在dyld调用静态构造函数之前,libc 会调用 _objc_init()

    相关文章

      网友评论

          本文标题:继承,分类 和 扩展&组合&协议 & 关联对象

          本文链接:https://www.haomeiwen.com/subject/bqxvvltx.html