iOS知识整理-OC特性

分类

分类的作用

分解体积庞大的类文件
为系统类添加方法
声明私有方法
把Framework的私有方法公开

分类添加过程

在程序运行时候,runtime会把分类的实例方法等信息合并到类对象的实例方法列表中,会把分类的类方法合并到元类对象的类方法列表中(在原方法之前)。
以添加实例方法为例:
运行时候,会遍历分类列表,拿到每一个分类的实例方法列表

分类能添加成员变量吗?

不能。只能通过关联对象(objc_setAssociatedObject)来模拟实现成员变量,但其实质是关联内容,所有对象的关联内容都放在同一个全局容器哈希表中:AssociationsHashMap,由AssociationsManager统一管理。

#关联对象: 使用objc_setAssociatedObject函数可以给某个对象关联其他的对象。
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
 
#获取关联的对象: 使用objc_getAssociatedObject函数可以通过键来取出某个对象的关联对象。
id objc_getAssociatedObject(id object, const void *key)
 
#移除关联的对象:使用objc_removeAssociatedObjects函数可以移除某个对象身上的所有关联的对象。
void objc_removeAssociatedObjects(id object)



扩展

一般用扩展做什么?

声明私有属性,声明方法(没什么意义),声明私有成员变量

扩展的特点

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

分类跟扩展的区别

扩展:在编写完分类文件后,直接把分类内容添加到相关的宿主类上。
分类:是在运行时使用runtime把分类的内容添加到宿主类上。


代理

代理是一种设计模式,以@protocol形式体现。
一般是一对一传递,一般以weak关键词以规避循环引用。
本质是获得对象,调用方法返回数据。


通知

使用观察者模式来实现的用于跨层传递信息的机制。
传递方式是一对多的。
原理是在通知中心维护一个map表,key为通知的名称,value为observer。本质为持有对象,所以在不用时,需要释放对象。


KVO

使用观察者设计模式,使用isa混写技术。
当A类实例被KVO监听时,Runtime会转建一个继承自A类的新类NSKVONotifying_A,并重写被观察属性的setter和getter方法。如果代码中有创建NSKVONotifying_A类,注册A类KVO时会崩溃。

何时触发:

使用setter方法改变值时,会触发KVO;
使用KVC setValue:forKey:改变值时,会触发KVO;
成员变量直接修改,不会触发KVO,需手动添加willChangeValueForKey和didChangeValueForKey方法才会触发;

- (void)setName:(NSString *)newName { 
      [self willChangeValueForKey:@"name"];    //KVO 在调用存取方法之前总调用 
      [super setValue:newName forKey:@"name"]; //调用父类的存取方法 
      [self didChangeValueForKey:@"name"];     //KVO 在调用存取方法之后总调用
}

~
~
~

KVC

KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性。KVC的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVC操作。

常用方法
获取值的方法
valueForKey:,传入NSString属性的名字。
valueForKeyPath:,传入NSString属性的路径,xx.xx形式。
valueForUndefinedKey它的默认实现是抛出异常,可以重写这个函数做错误处理。

修改值的方法
setValue:forKey:
setValue:forKeyPath:
setValue:forUndefinedKey:
setValue:forKey的搜索过程:
  1. 首先搜索set<Key>:方法
    如果成员用@property,@synthsize处理,因为@synthsize告诉编译器自动生成set<Key>:格式的setter方法,所以这种情况下会直接搜索到。
    注意:这里的<Key>是指成员名,而且首字母大写。
  2. 上面的setter方法没有找到,如果类方法accessInstanceVariablesDirectly返回YES(注:这是NSKeyValueCodingCatogery中实现的类方法,默认实现为返回YES)。
    那么按_<key>,_is<Key>,<key>,is<key>的顺序搜索成员名。
  3. 如果还是没有找到设置成员的值,就会调用setValue:forUndefinedKey:。
  4. 如果没有重写setValue:forUndefinedKey程序会马上崩溃。


属性关键字

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


1782258-7e7fdc564b4dff02.png

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

@property (nonatomic, copy) NSMutableArray * array;这样写有什么影响?
因为copy方法返回的都是不可变对象,所以array对象实际上是不可变的,如果对其进行可变操作如添加移除对象,则会造成程序crash。



参考:Objective_C语言特性:分类、扩展
参考: iOS开发-OC篇-KVC详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。