OC
- 分类
- 关联对象
- 扩展
- 代理(初级)
- 通知(未开放源码)
- KVO
- KVC
- 属性关键字
分类(Category)
你用分类做了哪些事?
- 声明私有方法
- 分解体积庞大的类文件
- 把Framework的私有方法分开
分类特点
- 运行时决议(通过runtime添加的宿主类)
- 可以为系统类添加分类
分类中都可以添加哪些内容
- 实例方法
- 类方法
- 协议
- 属性(只声明get,set方法,通过关联对象添加实例变量) (看源码,objc-runtime-680版本,去官方下载源码)
加载调用栈
- _objc_init
- map_2_images
- map_images_nolock
- _read_images
- remethodizeClass (image表示镜像)
源码分析 最先访问最后编译的分类(分类的编译顺序) 分类中方法重名,最后编译方法的生效 获取宿主类中的rw(宿主类的方法列表) 运行时才添加到方法列表中(运行时决议) 重新分配内存 内存移动(memmove) 内存拷贝(memcpy):分类方法会"覆盖"宿主类的方法,面试重点 源码分析总结:
- 分类添加的方法可以"覆盖"原类方法(效果上覆盖,原类方法仍然存在)
- 同名分类方法谁能生效取决于编译顺序
- 名字相同的分类会引起编译报错 思考:能否为分类添加实例变量,成员变量,即关联对象。
关联对象
- 能否给分类添加"成员变量"?
id objc_getAssociatedObject(id object,const void *key) void objec_setAssociatedObject(id object,const void *key,id value,objc_associatedPolicy policy) void objc_removeAssociatedObjects(id object)
- 关联对象添加的的成员变量,添加在哪?
关联对象本质
关联对象由AssociationsManager管理并在AssociationsHashMap存储。
所有对象的关联内容都在同一个全局容器(AssociationsHashMap)中。
如下图:
源码分析:
- id objc_getAssociatedObject(id object,const void *key)
- void objec_setAssociatedObject(id object,const void *key,id value,objc_associatedPolicy policy)
- void objc_removeAssociatedObjects(id object)
删除已经被关联的对象上的值,值value设为nil,进行删除.
0x4927298742为对象地址。
扩展
一般用扩展做什么?
- 声明私有属性
- 声明私有方法
- 声明私有成员变量
特点
- 编译时决议
- 只以声明的形式存在,多数情况下寄生在宿主类的.m文件中
- 不能为系统类添加扩展
代理(Delegate)(比较初级)
- 准确说是一种软件设计模式
- iOS当中以@protocol形式体现。
- 传递方式是一对一。
代理并不都是要实现,具体看修饰关键字(requid),
使用代理一般会遇到哪些问题
- 一般声明以weak以规避循环引用
通知(NSNotification)
- 是使用观察者模式来实现的用于跨层传递消息的机制
- 传递方式一对多
如何实现通知机制?
KVO
- KVO是Key-value observing的缩写
- KVO是Objectvie-C对观察着模式的又一实现
- Apple使用了isa混写(isa-swizzling)来实现KVO
isa-swizzling如何实现的?
重写的Setter添加的方法
- -(void)willChangeValueForKey:(NSString *)key
- -(void)didChangeValueForKey:(NSString *)key
- 通过kvc设置value能否生效?(能生效)
- 通过成员变量直接赋值value能否生效?(不能生效)
手动实现KVO,添加以上两个方法
[self willChangeValueForKey"value"];
_value += 1;
[self didChangeValueForKey"value"];
KVO总结:
- 使用setter方法改变值KVO才会生效
- 使用setValue:forKey:改变值KVO才会生效
- 成员变量直接修改需手动添加KVO才会生效
KVC
KVC是Key-value coding的缩写
- -(id)valueForKey:(NSString *)key
- -(void)setValue:(id)value forKey:(NSString *)key
键值编码是否会破坏面向对象的编程思想?会
valueForKey系统的实现流程
Accessor Method的方法是否存在的判断规则
- getKey
- key
- isKey
Instance var 是否存在的判断规则
- _key
- _isKey
- key
- isKey
setValue:forKey:系统实现
属性关键字
- 读写权限
- 原子性
- 引用计数
读写权限(默认readwrite)
- readonly
- readwrite
原子性
- atomic(赋值或获取是线程安全的,对数组操作是无法保证线程安全的)
- nonatomic
引用计数
- retain/strong
- assign/unsafe_unretained
- weak
- copy
assign(assign与weak的区别)
- 修饰基本数据类型,如int,BOOL等
- 修饰对象类型,不改变其引用计数
- 会产生悬垂指针。(assign修饰的对象被释放后仍然指向原对象,释放后访问会产生悬垂指针)
weak
- 不改变被修饰对象的引用计数。
- 所指对象在被释放之后会自动置为nil。(为什么被置为nil,内存管理)
面试题 @property(copy) NSMutableArray *array?
- 如果赋值过来的是NSMutableArray,copy之后是NSArray
- 如果赋值过来的是NSArray,copy之后是NSArray
浅拷贝
浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间
增加引用计数
并没有新的内存分配
深拷贝
深拷贝让目标对象的指针和源对象指针指向两片内容相同的内存空间
不会增加被拷贝对象的引用计数
会产生新的内存分配
深拷贝VS浅拷贝
- 是否开辟了新的内存空间
- 是否影响了引用计数
copy关键字
- 可变对象的copy和mutableCopy都是深拷贝。
- 不可变对象的copy是浅拷贝,mutableCopy是深拷贝。
- copy返回的都是不可变对象。
Objective-C语言笔试题 MRC下如何重写retain修饰变量的setter方法?
@property(nonatomin,retain) id obj;
- (void)setObj:(id)obj
{
if(_obj != obj){
[_obj release];
_obj = [obj retaion];
}
}
请简述分类实现的原理。 KVO的实现原理是怎样的? 能否为分类添加成员变量?