iOS面试iOS 开发每天分享优质文章iOS

iOS面试题总结(一)

2020-05-10  本文已影响0人  文小猿666

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指针就靠它了。

图片.png

6.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.深拷贝与浅拷贝
浅拷贝就是拷贝之后,并没有真正的复制,而是复制对象和原对象都指向同一个地址
深拷贝是真正的复制了一份,复制的对象只想新的地址

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表的维护。
···

上一篇下一篇

猜你喜欢

热点阅读