OC对象
1.一个NSObject对象占用多少内存?
-
系统分配了_16_个字节给NSObject对象(通过malloc_size函数获得)(以此为准,也就是说一个NSObject对象占用了16字节的内存,在64位环境(iOS)中malloc分配的字节数必定是16的倍数(内存对齐))
-
但NSObject对象内部(Class isa)只使用了8个字节的空间(64bit环境下(iOS),可以通过class_getInstanceSize函数获得,getInstanceSize获得的字节数必定是8的倍数)
// Person @interface Person : NSObject {
int _age; } @property (nonatomic, assign) int height; @end @implementation Person @end// Student @interface Student : Person { int _no;
BOOL isTrue; } @end @implementation Student @endint main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
NSLog(@"person - %zd", class_getInstanceSize([Person class])); //16
NSLog(@"person - %zd", malloc_size((__bridge const void *)person)); //16Student *stu = [[Student alloc] init]; NSLog(@"stu - %zd", class_getInstanceSize([Student class])); //24 NSLog(@"stu - %zd", malloc_size((__bridge const void *)stu)); //32 } return 0;}
2. 对象的isa指针指向哪里
- instance对象的isa ---> class对象
- class对象的isa ---> meta-class对象
- meta-class对象的isa ---> 基类meta-class对象
3.OC的类信息存放在哪里
-
属性,对象方法,成员变量,协议信息 存放在 class对象中
-
类方法 存放在 meta-class对象中
-
成员变量的具体值 存放在 instance对象中
KVO
KVO的实现机制是isa-swizzling。
1.什么是KVO?
KVO是一种基于KVC实现的观察者模式。当指定的被观察的对象的属性更改了,KVO会以自动或手动方式通知观察者。
事例:监听 ScrollView 的 contentOffSet属性
[scrollview addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
2.KVO的底层实现?(KVO的本质?)(iOS用什么方式实现对一个对象的健值观察?)
1.当给A类addObserver的时候,系统利用RuntimeAPI动态生成一个A类的子类NSKVONotifying_A,让A类实例对象的isa指针指向NSKVONotifying_A类,重写class方法,隐藏对象真实类信息 ([A class]还是A,而不是NSKVONotifying_A)
2.重写监听属性的setter方法
a) 首先会调用 willChangeValueForKey
b) 然后给属性赋值(原来的setter实现)
c) 最后调用 didChangeValueForKey(didChangeValueForKey中调用 observer 的 observeValueForKeyPath 去告诉监听器属性值发生了改变 . )
4.重写了dealloc做一些 KVO 内存释放
(removeObserver方法将isa再指向原来的A类)
3.如何手动触发KVO?
手动调用willChangeValueForKey:和didChangeValueForKey:(两个方法缺一不可)
4.添加observer后,直接修改成员变量会触发KVO么?
不会触发KVO(a.xxx = 123 会触发,因为通过属性修改本质是调用的setter方法,而a->xxx = 123不会调用setter方法 所以不会触发KVO)
所以KVO的本质就是在setter方法的实现中加了个观察者
KVC
KVC取值和赋值的过程(原理)
先找setter方法(getter方法),如果没找到就会直接查找成员变量(优先找下划线开头,然后is开头),如果还没找到就抛出异常(UnderfineKey)
Category和Extension (分类和扩展)
Category的实现原理
Category编译之后的底层结构是struct category_t,里面存储着分类的对象方法、类方法、属性、协议信息
在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中)
Category和Extension的区别是什么?
他俩本质上就不是一个东西
-
extension在编译期将其内容合并入类,它就是类的一部分,但是category则完全不一样,它是在运行时把数据合入到类对象中的。
extension在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它、extension伴随类的产生而产生,亦随之一起消亡。
-
extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension,除非创建子类再添加extension。而category不需要有类的源码,我们可以给系统提供的类添加category。
-
extension可以添加实例变量,而category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。
-
extension和category都可以添加属性,但是category的属性不能生成成员变量和getter、setter方法的实现。
Category 的 load 和 initialize
load:
initialize:
Category能否添加成员变量?如果可以,如何给Category添加成员变量?
-
不能直接给Category添加成员变量,但是可以间接实现Category有成员变量的效果
-
默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中。但可以通过**关联对象(objc_setAssociatedObject)**来间接实现
-
关联对象并不是存储在被关联对象本身内存中
-
关联对象存储在全局的统一的一个AssociationsManager中
Block
1.block的本质是什么?
Block本质上是封装了函数调用及函数调用环境的OC对象
2.__block
3.block属性形式词是什么?为什么?
4.block循环引用,提前释放?
在和GCD一起使用的时候中间就有个很大的坑,因为block中对对象是弱引用,在block中使用GCD的话,在GCD中使用的对象就是已经弱化的对象,所以会导致这个对象还没开始使用就已经被释放,这时就可以引入__strong来解决。