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