分类
1、你用分类都做了哪些事情?
-声明私有方法(比如定义一个分类,只有头文件放到对应宿主.m里,满足私有方法的声明和使用,不暴露具体实现。)
-分解体积庞大的类文件
-把Framework的私有方法公开
2、特点
-运行时决议,指的是在运行时,通过runtime将分类里面的内容添加到宿主类里面,当我们在编写或者编好分类文件后,它并没有把写好的内容添加到宿主类上,这个时候宿主类还没有分类中的方法,而是在运行时通过runtime把分类中的方法添加至宿主类上,这是分类的最大特点,也是分类和扩展的最大区别。
-可以为系统类添加分类,而扩展是不能给系统类添加扩展的。
3、分类中都可以添加哪些内容?
-实例方法
-类方法
-协议
-属性(只声明了get和set方法,并没有实现get和set方法,也没有生成实例变量)
4、加载调用栈
-分类添加的方法可以“覆盖”原类方法(当然不是真的覆盖)
-两个分类,里面有一个同名方法,最后编译的分类里面的方法被调用。
-名字相同的分类会引起编译报错
扩展
1、问题:一般用扩展做什么?
-声明私有属性
-声明私有方法
-声明私有成员变量
2、扩展的特点:
-编译时决议(分类是运行时决议)�
-只以声明的形式存在,多数情况下存在于宿主类的.m文件中(分类有声明有实现)
-不能为系统类添加扩展(系统类可以添加分类)
代理:
是一种软件设计模式(代理模式),并且可以添加属性
iOS中以@protocol的形式体现
代理和通知的区别是:代理是一对一的传递方式,而通知是一对多的传递方式
delegate一般声明为weak以规避循环引用,这样的结果是:
代理方----strong>委托方 同时 委托方-----weak>代理方
通知:
通知是使用 观察者模式 来实现的用于跨层传递消息的机制
1、问:如何实现通知机制?
在通知中心,有一个Notification_Map字典,里面存储着以通知名称为key,观察者组成的数组作为value
KVO
1、什么是KVO?
-KVO是Key-value observing的缩写
-KVO是Objective-C对观察者设计模式的一种实现
-Apple使用了isa混写(isa-swizzling)来实现KVO
当观察对象的某个属性时(当调用了addobserverforkeypath:),系统在运行时动态创建一个子类(这样是为了重写这个类的setter方法),并且将对象的isa指针指向新创建的这个类,修改isa的指向就是isa指针混写的一个标志,即isa混写技术
isa混写技术:
指的就是将isa指针动态重新指向新的类
setter方法重写的方法具体实现:
2、问:通过kvc设置value,kvo能否生效?
可以,原因是因为KVC改变属性值,会进入属性值的setter方法,从而触发KVO
3、问:通过成员变量直接赋值value,kvo能否生效?
不可以,但是可以通过手动修改setter方法,触发KVO
或者添加[self willChangeValueForKey:@"name"]; [self didChangeValueForKey:@"name"]; 这句话就可以了
总结:
使用setter方法改变值,KVO会生效
使用setValue:forKey:改变值,KVO会生效
成员变量直接修改需手动添加某些代码,KVO才会生效
KVC
1、KVC是Key-value coding的缩写。
里面有两个重要的方法:
- (id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
2、问:KVC是否有违面向对象思想
KVC只要知道某个对象的私有变量名称key,可以在外部将其value进行修改,也就是有违面向对象思想的。
valueForKey:的系统实现流程:
valueForKey:说白了就是通过一个key找到对应的value
找value可以是通过getter方法找,也可以通过直接找对应的实例变量
也就是,一个是方法,一个是实例变量,两种方法
图中
Accessor Method is exist?
就是通过getter方法找,有的话直接调用
Instance Var is exist?
就是有没有对应的实例变量,有的话调用
3、:Accessor Method(访问器方法)如何找方法呢?
通过getKey、key(正好是getter方法)、isKey三个方法,如果有则YES
这三个都是方法
4、:+(BOOL)accessInstanceVariablesDirectly的作用?
系统给我们提供了一个方法:
+(BOOL)accessInstanceVariablesDirectly,默认是返回YES
在返回YES的基础上,才会调用Instance Var is exist,判断是否存在相应的实例变量或者相似的实例变量
如果我们重写该方法,使其返回NO,那么直接报没有找到对应的值处理;不再进行实例变量的判断。
5:Instance Var is exist?判断规则是什么?
通过查找是否存在:_key、_isKey、key、isKey四个实例变量,如果有则YES
这四个都是实例变量
6、问:如果valueForKey:没有找到对应的value,会怎样?
如果valueForKey:没有找到对应的value,会调用valueForUndefinedKey:
从而造成崩溃
YZPerson *person1 = [[YZPerson alloc] init];
NSLog(@"person1.age = %@", [person1 valueForKey:@"age"]);
7、setValue:forKey:的系统实现流程:
属性关键字
1、问:属性关键字可以分为哪几类?
读写权限:readonly、readwrite(默认)
原子性:atomic(默认)、nonatomic
引用计数
setter\getter
atomic可以保证获取和赋值是线程安全的
但是,并不能保证在使用过程中是线程安全的(例如对一个使用atomic修饰的数组进行添加和删除元素并不能保证是线程安全的,只是保证在获取数组的值和赋值时是保证线程安全的)
引用计数:
retain(非ARC)\strong(ARC中)
assign(既可以修饰基本数据类型,也可以修修饰对象类型)\unsafe_unretain(只有在MRC中使用频繁,在ARC中基本不使用)
weak
copy
2、问:assign和weak的区别有哪些?
assign特点
可以修饰基本数据类型,如int、BOOL等
可以修饰对象类型,但不改变其引用计数器
当使用assign修饰对象,在对象释放的时候,指针仍然反向原对象地址,会产生悬垂指针,如果继续访问原对象可能导致
出现内存泄露或者程序异常。
3、weak
不可以修饰基本数据类型
修饰对象类型,但不改变其引用计数器
当使用weak修饰对象,在对象释放的时候,会自动置为nil
weak和assign的区别:assign既可以修饰基本类型,也可以修饰对象类型,weak只能修饰对象类型,第二个是assign修饰对象,在对象释放的时候,指针仍然反向原对象地址,而weak在释放以后被置为nil的。共同点是都不改变引用计数。
4、copy
浅拷贝
浅拷贝并没有新建一块内存
浅拷贝对原来的对象引用计数+1
深拷贝会新建一块内存,与原来的内存里面内容一样
深拷贝对原来的对象引用计数不变
深拷贝和浅拷贝的区别是否开辟了新的内存空间,是事影响了引用计数
对可变对象copy变为不可变,并且为深拷贝
对可变对象mutablecopy变为可变,深拷贝
总结:
可变对象的copy和mutableCopy均为深拷贝
不可变对象的mutableCopy是深拷贝,copy为浅拷贝。
copy返回的均是不可变对象
如果赋值过来是NSMutableArray,copy后其实是NSArray,如果对其进行添加或者删除操作,会造成程序崩溃
5、
问:为何会做 _obj != obj 的判断?不做可以吗?
如果不做,则先进行[_obj release];也就是原来的对象可能已经被释放掉了。再进行[obj retain]操作,会造成程序异常。