1.为什么不能给类别category 添加成员变量?extension呢?
2.isKindOfClass: 和 -isMemberOfClas区别?
3.weak的实现原理
4.理解 [self class] 与 [super class] ?
5.ios中的内存管理机制
6.Block如何访问外部变量? 下划线__block的作用? 如何防止循环引用?
7.Block循环引用问题
(1).为什么Msonry不会循环引用?
(2).weakSelf、strongSelf结合使用
8.深拷贝与浅拷贝
9.KVO实现原理
10.Runtime的原理及实际使用场景
讲解
1.为什么不能给类别category 添加成员变量?extension呢?
分类并不会改变原有类的内存分布的情况,它是在运行期间决定的,此时内存的分布已经确定,若此时再添加实例会改变内存的分布情况,这对编译性语言是灾难,是不允许的。
category 是基于运行时的:
typedef struct category_t {
const char *name; //类的名字
classref_t cls; //类
struct method_list_t *instanceMethods; //category中所有给类添加的实例方法的列表
struct method_list_t *classMethods; //category中所有添加的类方法的列表
struct protocol_list_t *protocols; //category实现的所有协议的列表
struct property_list_t *instanceProperties; //category中添加的所有属性
} category_t;
结构体中并没有成员变量这个list,所以无法在category添加成员变量,添加属性是可以的,但是不会生成添加属性的getter和setter方法,所以,尽管添加了属性,也无法使用点语法调用getter和setter方法,但你可以使用运行时实现关联对象并可以引用。
反观扩展(extension),作用是为一个已知的类添加一些私有的信息,必须有这个类的源码,才能扩展,它是在编译器生效的,所以能直接为类添加属性或者实例变量。
category跟extension最大的区别在于生效时间不一样,category在运行时生效,而extension在编译时生效
2.isKindOfClass: 和 -isMemberOfClas区别?
isKindOfClass来确定一个对象是否是一个类的成员,或者是派生自该类的成员。
isMemberOfClass只能确定一个对象是否是当前类的成员。
3.weak的实现原理
weak是Runtime维护了一个hash(哈希)表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。
对象准备释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
A.x = B;
__weak B.y = A;
当A释放时,会根据A的地址获取所有弱引用它的指针的地址(如B.y),把它置为nil。
4.理解 [self class] 与 [super class] ?
我们知道实际上在iOS中,对方法的调用是通过发送消息来完成的。也就是说使用 [self class] 时,会使用obj_msgSend(id theReceiver, SEL selector, ...)函数向Receiver来发送消息。而使用 [super class] 时,会使用obj_msgsendSuper(...)函数向Receiver来发送消息。
————————————————
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@”%@”, NSStringFromClass([self class]));
NSLog(@”%@”, NSStringFromClass([super class]));
}
return self;
}
@end
上边代码会输出什么,为什么
简单来说,self和super都是指向当前实例的,不同的是,[self class]会在当前类的方法列表中去找class这个方法,[super class]会直接开始在当前类的父类中去找calss这个方法,两者在找不到的时候,都会继续向祖先类查询class方法,最终到NSObject类。那么问题来了,由于我们在Father和Son中都没有去重写class这个方法,最终自然都会去执行NSObject中的class方法,结果也自然应该是一样的。
至于为什么是Son,我们可以看看NSObject中class的实现:
-(Class)class {
return object_getClass(self);
}
5.ios中的内存管理机制
为了管理所有对象的引用计数和weak指针,苹果创建了一个全局的SideTables,虽然名字后面有个"s"不过他其实是一个全局的Hash表,里面的内容装的都是SideTable结构体而已。它使用对象的内存地址当它的key。管理引用计数和weak指针就靠它了。
6.Block如何访问外部变量? 下划线__block的作用? 如何防止循环引用?
-
block在访问外部变量(局部变量)时,会在block内部创建(浅拷贝)一个新的变量来保存这个外部变量,修改的是内部新变量的值,外部的变量不受影响;
-
当外部变量加了__block修饰,block保存(深拷贝)了其指针引用,对指针变量的修改直接影响外部变量的值;
另外,静态变量、全局变量和全局静态变量,传入的就是地址值,可以直接被block修改 -
为了避免循环引用,最好的是对block内部引用的self对象进行弱引用。一般使用一个弱指针来指向该对象,然后在block内使用该弱引用指针来进行操作,这样就避免了block对该对象的强引用。
7.Block循环引用问题
(1).为什么Msonry不会循环引用?
查看masonry源码可以看到究竟:msonry中设置布局的方法中的block对象并没有被View所引用,而是直接在方法内部同步执行,执行完以后block将释放,其中捕捉的外部变量的引用计数也将还原到之前。
(2).weakSelf、strongSelf结合使用
使用weakSelf结合strongSelf的情况下,能够避免循环引用,也不会造成提前释放导致block内部代码无效(野指针问题)。
_person1 = [[Person alloc] init];
_person2 = [[Person alloc] init];
_person2.name = @"张三";
__weak __typeof(self) weakSelf = self;
_person1.block = ^{
__typeof(&*weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.person2.name);
});
};
8.深拷贝与浅拷贝
浅拷贝就是拷贝之后,并没有真正的复制,而是复制对象和原对象都指向同一个地址
深拷贝是真正的复制了一份,复制的对象只想新的地址
- copy:对于可变对象为深拷贝,对于不可变对象为浅拷贝
- mutablecopy:始终为深拷贝
注:
oc中只有遵循<NSCopying>才支持copy,只有遵循<NSMutableCopying>才支持mutablecopy,如果没有遵循,拷贝时会直接crash。
9.KVO实现原理
当某个类的对象属性第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法。派生类在被重写的setter方法内实现真正的通知机制。
当观察对象A时,KVO机制动态创建一个新的名为NSKVONotifying_A的新类,该类集成字对象A的本类,且KVO为NSKVONotifying_A重写观察属性的setter方法,setter方法会负责在调用元setter方法之前和之后,通知所有观察对象属性值的更改情况。
被观察属性发生改变之前,willChangeValueForkey:被调用,通知系统该keyPath的属性值即将变更;当改变发生后,didChangeValueForkey:被调用,通知系统该keyPath的属性值已经变更;之后,observeValueForKey:ofObject:context:也会被调用。且重写观察属性的setter方法这种继承方式的注入是在运行时而不是编译时实现的。
10.Runtime的原理及实际使用场景
主动使用
1.字典转模型
2.给分类属性添加get,set方法
3.方法交换swizzling
4.设置UITextField占位文字的颜色
···
隐式调用
1.KVO与KVC的实现。
2.内存管理,weak表的维护。
···
网友评论