2019Objective_C 语言特性 分类 扩展 代理 通知 KVO KVC 属性关坚持

一、分类

  • 1.分类的作用?
    1. 声明私有方法
    2. 分解体积庞大的类文件 把 framework 的私有方法公开
  • 2.分类的特点
    1.运行时决议,
    2.可以为系统类添加分类
    说的详细点儿 在运行时时期, 将 Category 中的实例方法列表、协议列表、属性列表添加到主类中后(将 Category 中 的方法在方法列表中的位置在主类的同名方法之前之前), 然后会递归的调用所有类的 load 方法, 这一切都在 main 函数之前执行的
  • 3.分类可以添加哪些内容
    实例方法 类方法 协议 属性(添加了 setter 和 getter 方法, 并没有实例变量, 需要用关联对象技术)
  • 4.如果工程里有两个分类 A 和 B, 两个分裂中有一个同名方法, 哪个方法最终实现
    取决于分类的编译书序, 最后编译的那个分类的同名方法最终生效.而之前的都会被覆盖掉(这里并不是真正的覆盖, 因为其余方法仍然存在, 只是访问不到, 因为在动态添加类的方法的时候是倒序遍历方法列表的, 而最后编译的分类的方法会放在方法列表的签名, 方法的时候会被优先方访问到, 同理如果声明了一个和原类方法同名的方法, 也会被覆盖掉原类的方法)
  • 5.如果声明了两个同名的分类会怎么样?
    会报错, 所以第三方的分类, 一般都带有命名前缀
  • 6.分类能添加成员变量吗?
    不能向编译后的类中添加成员变量, 但是可以向动态的类中添加成员变量, 利用的是关联对象技术. objc_setAssociatedObject来模拟实现成员变量, 但其实是关联内容
    所有关联对象的关联内容都放在一个同一个全局容器哈希表中, 由一个 AssociationsManager统一管理.

二、扩展

  • 1.一般用扩展做什么?

    1. 声明私有属性
    2. 声明私有成员变量
    3. 声明方法什么都不做
  • 2.扩展的特点

    1. 编译时决议
    2. 只能以声明的形式存在, 多数情况下寄生在宿主类的.m 中, 不能为系统类添加扩展

三、代理

image.png

代理是一种设计模式, 以@protocol 形式体现, 一般是一对一传递, 一般以 weak 关键词以规避循环引用

四、通知

通知是用观察者模式来实现的, 用于跨层传递信息的机制传递方式一是一对多

  • 如何实现通知机制?


    image.png

五、KVO

KVO 是观察者模式的另一实现
使用了 isa 混写(isa-swizzling)来实现的 KVO


image.png

使用 setter 方法改变值 KVO 会生效, 使用 setValue:forKey 即KVC 改变值 KVO 也会生效, 因为 KVC 会调用 setter 方法

- (void)setValue:(id)value{
   [self willChangeValueForKey:@"key"];   
 
   [super setValue:value];

   [self didChangeValueForKey:@"key"];
}
  • 那么通过直接赋值成员变量会触发 KVO 吗?
    不会, 因为不会调用 setter 方法, 需要手动加上
    willChangeValueForKey和didiChangeValueForKey 方法来手动出发 KVO 才行

六、KVC

-(void)setValue:(id)value forKey:(NSString *)key;
-(id)valueForKey:(NSString *)key;

KVC 是指在 iOS 开发中, 可以允许开发者通过 Key 名直接访问对象的属性, 或者给对象的属性赋值, 而不需要调用明确的存取方法, 这样就可以在运行时动态地访问和修改对象的属性 而不是在编译时确定, 这也是iOS 开发中的黑魔法之一, 很多高级的 iOS 开发技巧都是基于 KVC 实现的
当调用setValue: forKey:@"name"的代码时, 底层的实现机制如下:

  • 程序优先调用 setkey:属性值方法, 代码通过 setter 方法完成设置, 注意, 这里的 key是指成员变量名字, 首字母大小写要符合 KVC 的命名规则, 下同
  • 如果没有找到 setName 方法, KVC 机制会检查+(BOOL) accessInstanceVariablesDirectly方法有没有返回 YES, 默认方法返回 YES, 如果你重写了改方法返回了 NO 的话, 那么在这一步 KVC 会执行 setValue:forUndefinedKey方法, 不过, 一般开发者不会这么做, 所以 KVC 机制会搜索该类里面的有没有名为 key 的成员变量, 无论该变量是在类接口定义还是在类实现定义, 也无论用了什么样的访问修饰符, 只在存在以key 命名的变量, KVC 都可以对该成员变量赋值
  • 如果该类即没有 setKey 方法, 也没有 key 成员变量, KVC 机制会搜索_iskey 的成员变量
  • 和上面一样, 如果该类即没有 setKey 方法, 也没有_key 和_isKey 成员变量, KVC 机制hi 继续在搜索 key 和 isKey 的成员变量, 再给它们赋值
  • 如果上面列出的方法或者成员变量都不存在, 系统将会执行该对象的 setValue:forUndefinedKey 方法, 默认是抛出异常
    即如果没有找到 SetKey方法的话, 会按照_key _iskey key iskey 的顺序搜索成员变量并进行赋值操作

如果开发者想让这个类禁用 KVC, 那么重写+(BOOL) accessInstanceVariablesDirectly让其返回 NO 即可, 这样的话如果 KVC 没有找到 setKey 属性名时, 会直接用 setValue:forUndefinedKey方法

当调用valueForKey:@”name“的代码时,KVC对key的搜索方式不同于setValue:属性值 forKey:@”name“,其搜索方式如下:

  • 首先按get ,,is的顺序方法查找 getter 方法, 找到的话会直接调用, 如果是 BOOL 或者 int 等值类型, 会将其包装成一个 NSNumber 对象
  • 如果上面的getter没有找到, KVC则会查找countOf,objectInAtIndex或AtIndexes格式的方法。如果countOf方法和另外两个方法中的一个被找到,那么就会返回一个可以响应NSArray所有方法的代理集合(它是NSKeyValueArray,是NSArray的子类),调用这个代理集合的方法,或者说给这个代理集合发送属于NSArray的方法,就会以countOf,objectInAtIndex或AtIndexes这几个方法组合的形式调用。还有一个可选的get:range:方法。所以你想重新定义KVC的一些功能,你可以添加这些方法,需要注意的是你的方法名要符合KVC的标准命名方法,包括方法签名。
  • 如果上面的方法没有找到,那么会同时查找countOf,enumeratorOf,memberOf格式的方法。如果这三个方法都找到,那么就返回一个可以响应NSSet所的方法的代理集合,和上面一样,给这个代理集合发NSSet的消息,就会以countOf,enumeratorOf,memberOf组合的形式调用。
  • 如果还没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默认行为),那么和先前的设值一样,会按_,_is,,is的顺序搜索成员变量名,这里不推荐这么做,因为这样直接访问实例变量破坏了封装性,使代码更脆弱。如果重写了类方法+ (BOOL)accessInstanceVariablesDirectly返回NO的话,那么会直接调用valueForUndefinedKey:方法,默认是抛出异常。

七、属性关键字

  • 1.读写权限: readwrite(默认) readonly
  • 2.原子性:atomic(默认), nonatomic.atomic是读写线程安全的, 但是效率低,而且也不是绝对的安全的. 比如如果修饰的是数组, 那么对数组的读写是安全的, 但如果是操作数组进行添加移除其中的对象的话, 那就不保证安全了
  • 3.引用计数
    • retain/strong
    • assign: 修饰基本数据类型, 修饰对象类型时, 不改变其引用计数器, 会产生悬垂指针 修饰的对象在释放后, assign指针仍然指向原对象内存地址, 如果使用assign指针继续访问原来的对象的话, 就可能会导致内存泄露或者程序异常
    • weak:不改变被修饰对象的引用计数, 所指对象在被释放后, weak 指针会自动置为 nil
    • copy:分为深拷贝和浅拷贝
      浅拷贝:对内存地址的复制, 让目标对象指针和原对象指向同一片内存空间会增加引用计数
      深拷贝: 对对象内容的复制 开辟新的内存空间


      image.png

可变对象的 copy 和 mutableCopy 都是深拷贝
不可变对象的 copy 是浅拷贝 , mutableCopy 是深拷贝
copy 方法返回的都是不可变对象

- @property (nonatomic, copy) NSMutableArray * array;这样写有什么影响?

因为 copy 方法返回的都是不可变对象, 所以 array 对象实际上不可变的, 如果对其进行操作如添加或者移除对象, 则会造成 crash.

你可能感兴趣的:(2019Objective_C 语言特性 分类 扩展 代理 通知 KVO KVC 属性关坚持)