OC

OC语言的基础理论tips

Posted by FishYan on June 19, 2019

OC语言的基础理论Tips

Tip1. 分类

Category 的实现原理

  • category 实际上是结构体,运行时决定
  • 可以添加类方法,方法,协议,属性。
  • 新添加的方法会在运行时倒序插入到原有的类最前面,
  • Category中有同名方法,最后编译的那个方法会生效。(swift中不允许同名扩展方法)
  • 作用:将一个类拆分为多个,是不相关的业务相互分离

Category 是否能添加成员变量

Category 是不能添加成员变量的,因为在运行时实例内部的成员变量已经确定,这时再添加成员变量会破坏实例内部布局。

但是我们可以通过关联对象的方式去实现类似添加成员变量(与类本身的成员变量有本质区别),通过添加属性,自定义实现setter和getter方法

关联对象:通过objc_setAssociatedObject和objc_getAssociatedObject来关联对象,关联对象的值存在全局哈希表中,由AssociationsManager统一管理

Tip2. 扩展

  • 编译时决定
  • 作用: 声明私有属性,成员变量,声明私有方法(没什么卵用)
  • 作用在.m文件中,不能为系统类扩展

    Tip3.代理

代理6步

  • 声明协议
  • 调用代理方法
  • 设置代理属性
  • 遵循代理
  • 指定代理对象
  • 实现代理方法

    delegate为什么要使用weak修饰

  • weak 是弱引用,不会使引用计数+1,否则self.xx.delegate = self会造成循环引用

    Tip4. 通知

  • 基于观察者模式实现,
  • 用于跨层传递消息,
  • 传递方式一对多
  • 在通知中心,内部维护map表,字典中key值为通知名称,value为数组,每个元素包含通知接收观察者,观察者回调方法,参数数据等相关信息。

    Tip5. 通知,block,代理分别适合什么场景

  • 通知:一对多,并且不会引入新类,适合一个类对多个毫无关联的类传递数据
  • 代理:一对一,适合多个业务方法及多个参数的场景
  • block:一对一,适合参数不多,单一业务方法的场景

    Tip6. KVO

    key-value-observing, 观察者设计模式的实现,使用isa混写(isa-swizzling)实现 ```Swift (void)setValue:(id)value { [self willChangeValueForKey:@”key”];

    [super setValue:value];

    [self didChangeValueForKey:@”key”]; } ``` 步骤:

  • 观察者观察对象A某属性或成员变量
  • 系统运行时动态创建 NSKVONotifying_A 这样的类
  • 将A的isa指针指向 NSKVONotifying_A
  • NSKVONotifying_A 是原有类的子类,是继承关系
  • NSKVONotifying_A 重写setter方法,以达到通知所有观察对象

注意:

  • KVO使用setter方法会生效
  • 使用setvalue:forkey会生效
  • 成员变量需要手动触发KVO
  • 直接修改属性内容,不会生效(因为不走setter方法)

    Tip7. KVC

    键值编码 key-value-coding

    -(id)valueForKey:(NSString *)key

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

  • KVC会破坏面向对象,因为key可以为任意值,不受限制,私有变量也可以通过KVC访问
  • setValue时查找顺序:set< key > → _key → _iskey → key → iskey → setValue:forUndefinedKey
  • 重写 accessInstanceVariablesDirectly 返回 NO,KVC只会查找set< key >
  • valueForKey时查找顺序 get< key > → _key → _iskey → key → iskey → valueforUndefinedKey 并抛出异常
  • 同样,重写 accessInstanceVariablesDirectly 返回 NO, KVC只会查找get < key > —

## Tip8. @property

  • @property 是由ivar(实例变量)和setter、getter(存取方法)组成
  • @property 在runtime中是objc_property_t是结构体如下
     struct property_t {
      const char *name;
      const char *attributes;
     }
    

    而attributes本质是objc_property_attribute_t,定义了property一些属性如类型,原子性,内存语义和对应的实例变量等,也是结构体:

     struct property_attribute_t {
       const char *name;
       const char *value;
     }
    
  • 按成属性定义后,编译器会自动编写访问属性的方法并进行自动合成,自动合成的执行过程是在编译期,所以在代码中看不到。

Tip9. 属性关键字

@synthesis 和 @dynamic

  • @synthesis: 默认为 @synthesis 会自动生成setter getter 方法
  • @dynamic: 不会自动生成setter getter方法,告诉编译器已在其他地方实现了setter getter方法

原子性

  • atomic:属性默认是atomic是线程安全的,但不是绝对安全,线程开销大
  • nonatomic:编程中一般设置nonatomic,线程开销小

读写关键字

  • readonly: 只读属性
  • readwrite: 可读可写

引用计数

  • assign: 用于非指针变量,一般用于基础类型和C类型,这些类型不是对象,存在栈中,由系统管理,如果对象用assign修饰,那么对象在释放后,指针地址仍存在,会造成野指针。

  • weak: 弱引用,将指针地址copy一份,不增加引用计数,也不持有对象,当对象消失后自动指向nil,用于防止循环引用
    • IBOutlet 为什么要使用weak:因为storyboard或xib强引用了该视图,并且在指定对应vc时强引用了vc,如果vc再强引用视图,会造成循环引用
    • delegate 为什么要使用weak:遵循代理的对象一般是当前对象即self,delegate强引用了self,如果声明代理时,self强引用了delegate就会循环引用,
  • copy: 建立一个新对象,赋值时对传入值进行拷贝,分深浅copy
    • block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 环境下,编译器会根据情況自动将栈上的 block 复制到堆上
  • strong: 强引用,会增加引用计数,创建新指针指向原有内存地址,如果指向nil会造成野指针

  • retain/unsafe_unretained: MRC时代关键字,ARC已经不再使用。

深浅copy

深copy:指针和内存地址都会被copy

浅copy:只copy指针,不copy内存地址

  • 不可变对象 可变对象  
    copy 浅copy 深copy
    mutableCopy 深copy 深copy