iOS Category 与 Class Extension

2020-02-19  本文已影响0人  CrystalZhu

Category

概念: OC中特有预防,它表示一个指向分类的结构体指针,原则上只能增加方法,不能增加成员变量.
定义:

typedef struct objc_category *Category;
struct objc_category{
  char *category_name  /分类名
  char *class_name  /分类所属的类名
  struct objc_method_list  *instance_methods  /示例方法列表
  struct objc_method_list  *class_methods  /类方法列表
  struct objc_protocol_list *protocol  / 分类所实现的协议列表
}

这个结构体里根本没有属性列表
主要包含了分类定义的实例方法和类方法

注意⚠️:
  1. 分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表(原则上只能添加方法,不能添加属性,实际上可以通过runtime添加属性)
    2.分类中的可以写@property,但不会生成setter和getter方法,也不会生成实现以及私有的成员变量,编译时会报警告.
    3.可以在分类中访问原有类中.h的属性
    4.若分类中有和原类同名的方法,会优先调用分类中的方法,忽略原有类的方法,所以同名方法调用的优先级:
    分类 > 本类 > 父类
    5.若多个分类中都有原有类中同名的方法,那么调用该方法的时候执行谁由编译器决定: 编译器会执行最后一个参与编译的分类中的方法.

格式:
声明部分:
@interface 待扩展的类(分类的名称)
@end
实现部分:
@implementation 待扩展的类(分类的名称)

OC类必须包括两部分: interface部分和implementation部分
(OC中将成员变量和成员方法的声明部分放置在interface部分中,包括继承关系, protocol实现关系,将实现部分放在implementation部分中)

为什么分类不能添加属性,为什么在分类中声明属性时,运行不会出错呢?

我们知道一个类中用@property声明属性,编译器会自动帮我们生成 成员变量和setter/getter方法,但分类的指针结构体中,根本没有属性列表.所以在分类中用@property声明属性,既无法生成成员变量也无法生成setter/getter.
因此结论是:
可以用@property声明属性,编译和运行都会通过,只要不使用程序也不会崩溃.但如果调用了_成员变量和setter/getter方法.报错就在所难免了.
通过其他方式添加属性:
由于OC是动态语言,方法真正的实现是通过runtime完成的,通过runtime手动添加setter/getter方法

#import <objc/runtime.h>
static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey";
@implementation Programmer (Category)
//运行时实现setter方法
-(void)setNameWithSetterGetter:(NSString *) nameWithSetterGetter{
  objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);
}
//运行时实现getter方法
-(NSString *)nameWithSetterGetter{
  return objc_getAssociatedObject(self, &nameWithSetterGetterKey);
}
@end

以上仅仅是实现了setter和getter方法,但是调用_成员变量依然会报错

优点:
  1. Category用于大型类有效分解.通常一个大型类的方法可以根据某种逻辑或是相关性分解成不同的组,当有多个开发者共同完成一个项目时,每个人所承担的是单独的category的开发和维护,这样版本控制就更加简单了,冲突也会减少.
    2.不需要通过增加子类而增加现有类的方法
缺点:

1.无法添加实例变量,只能通过定义子类的方法添加
2.category中的方法与原始类和父类相比具有更高的优先级,若覆盖父类的方法,可能会导致super消息的断裂,因此最好不要覆盖原始类中的方法.

Class Extension

Extension是Category的一个特例.类扩展与分类相比,只少了分类的名称.所以称之为“匿名分类”,可以为某个类扩充一些私有成员变量和方法

格式:在.m文件中声明实现方法仅自己可见

#import "Person.h"
@interface Person(){
  int _age;
}
-(void)say;
@end
@implementation Person
-(void)say{
  doing something
}

和category不同的是extension不但可以声明方法,还可以声明属性,成员变量.extension一般用于声明私有方法,私有属性,私有成员变量.

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

@interface ViewController()
@end

当然我们也可以创建一个单独的extension文件.

Swift中的扩展

在swift中已经没有分类这种写法了,只有扩展Extension,而且具有很大强大的功能.
扩展就是向一个已有的类,结构体,枚举类型或者协议类型添加新功能(functionality)这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模).扩展和Objectve-C中的分类类似.(不过更加强大,而且与Object-C不同的是,Swift的扩展没有名字).
优点:
1.可以把代码进行模块化区分,把功能性相同的代码放在一个扩展(Extension)中,使得代码层次清晰明了.
2.与OC中的扩展不同,在swift中类的扩展里的方法,属性,在外部都可以使用,而且支持继承

extension ViewController{
  var name:String? {
    return "name"
  }
  var age: String? {
    get{
      return "18"
    }
    set{
      self.age = newValue
    }
  }
}

扩展中不能有存储类型的属性,只可以添加计算型实例属性和计算型类型属性.也可以利用runtime,如下:

// runtime给继承自OC中的类添加属性
private var key: Void?
extension UITextField{
  var bcTextField: IndexPath? {
    get{
      return objc_getAssociatedObject(self, &key) as? IndexPath
    }
    set(newValue){
      objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
}
上一篇下一篇

猜你喜欢

热点阅读