高级面试

iOS 基础题

1、分类和扩展有什么区别?可以分别用来做什么?分类有哪些局限性?分类的结构体里面有哪些成员?

Category

可以减少单个文件的体积

把不同功能组织到不同的Category里

可以由多个开发者共同完成一个类

可以按需加载想要的category

声明私有方法

模拟多继承(一定要搞懂多继承概念)

把framework的私有方法公开(先了解framework)

Extension

Extension可以添加属性和方法,且方法必须实现

隐藏私有信息

区别

extension在编译器决议,属于类的一部分,category在运行时决议

extension必须在有一个类的源码时才能添加,category不需要有类的源码

extension可以添加实例变量,category不可以

分类的结构体里面的成员

分类结构体包含了分类名,绑定的类,实例与类方法列表,实例与类方法属性以及协议表。

2、讲一下atomic的实现机制;为什么不能保证绝对的线程安全(最好可以结合场景来说)?

自旋锁实现,只是读写的锁定。但是如果有个C也在写,D在读取,D会读到一些随机的值(ABC修改的值),这就不是线程安全的了。最好的方法是使用lock。

3、被weak修饰的对象在被释放的时候会发生什么?是如何实现的?知道sideTable么?里面的结构可以画出来么?

释放时,调用clearDeallocating函数。clearDeallocating 函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

4、关联对象有什么应用,系统如何管理关联对象?其被释放的时候需要手动将所有的关联对象的指针置空么?

分类添加成员变量,不需要手动置空,在对象走dealloc的系统内部实现帮助我们处理掉了关联对象的内存释放

5、KVO的底层实现?如何取消系统默认的KVO并手动触发(给KVO的触发设定条件:改变的值符合某个条件时再触发KVO)?

Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象A时,KVO机制动态创建一个新的名为:NSKVONotifying_A 的新类,该类继承自对象A的本类,且 KVO 为 NSKVONotifying_A 重写观察属性的 setter 方法,setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况。

```

+(BOOL)automaticallyNotifiesObserversForKey:(NSString*)key{

    if([key isEqualToString:@"name"]) {

        returnNO; 

 }else{

    return[superautomaticallyNotifiesObserversForKey:key]; }

}

-(void)setName:(NSString*)name{

    if(_name!=name) {

     [selfwillChangeValueForKey:@"name"];

     _name=name; 

     [selfdidChangeValueForKey:@"name"]; 

 }

 }

```

6、Autoreleasepool所使用的数据结构是什么?AutoreleasePoolPage结构体了解么?

    是以栈为节点通过双向链表的形式组合而成,是和线程一一对应的

7、讲一下对象,类对象,元类,跟元类结构体的组成以及他们是如何相关联的?为什么对象方法没有保存的对象结构体里,而是保存在类对象的结构体里?

    对象isa指向类对象,类对象的isa指向元类。元类isa指向根元类。根元类的isa指针指向自己,superclass指针指向NSObject类,实例对象结构体只有一个isa变量,指向实例对象所属的类。类对象有isa,superclass,方法,属性,协议列表,以及成员变量的描述。

    所有的对象调用方法都是一样的,没有必要存在对象中,对象可以有无数个,类对象就有一个所以只需存放在类对象中。

8、class_ro_t和class_rw_t的区别?

ObjC 类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t 中,其中还有一个指向常量的指针 ro,其中存储了当前类在编译期就已经确定的属性、方法以及遵循的协议。

9、iOS 中内省的几个方法?class方法和objc_getClass方法有什么区别?

内省是对象揭示自己作为一个运行时对象的详细信息的一种能力。这些详细信息包括对象在继承树上的位置,对象是否遵循特定的协议,以及是否可以响应特定的消息。

下面我们简单介绍几个NSObject的内省方法(项目中也是经常用到):

  1.isKindOfClass:

  检查对象是否是那个类或者其派生类实例化得对象

  2. isMemberOfClass:

    检查对象是否是那个类的实例化得对象

  3. respondToSelector:

  检查对象是否包含这个方法

  4. conformsToProtocol:

  检查对象是否符合协议,是否实现了协议中所有的必选方法

object_getClass:获得的是isa的指向 

self.class:当self是实例对象的时候,返回的是类对象,否则则返回自身。



10、在运行时创建类的方法objc_allocateClassPair的方法名尾部为什么是pair(成对的意思)?

meta-class是类对象的类,每个类都有自己单独的meta-class。所有的类对象并不会属于同一个meta-class。meta-class要保证类对象具有继承体系中基类的所有实例和类方法,以及继承体系中的所有中间类方法。对于所有NSObject继承体系下的类,NSObject的实例方法和协议方法对他们和他们meta-class的对象都要有效。

所有的meta-class使用基类的meta-class作为自己的基类,对于顶层基类的meta-class也是一样,只是它指向自己而已。

11、一个int变量被__block修饰与否的区别?

    从中可以看出被__block修饰的变量a,在block的定义后,不管是block内部,还是block后面,都不再是原来定义的a了,而是一个新的变量,只是值和原来的a相同,并且以后不管什么地方调用此block,都是用的此一个变量,不会是生成多个,作用相当于是static变量。

12、为什么在block外部使用__weak修饰的同时需要在内部使用__strong修饰?

    ARC环境中使用weak 的修饰符来修饰一个变量,防止其在block中被循环引用,而有些特殊情况下,我们在block中又使用__strong 来修饰这个在block外刚刚用__weak修饰的变量,这是因为在block中调用self会引起循环引用,而在block中需要对weakSelf进行__strong,保证代码在执行到block中,self不会被释放,当block执行完后,会自动释放该strongSelf;

13、RunLoop的作用是什么?它的内部工作机制了解么?(最好结合线程和内存管理来说)

字面意思是“消息循环、运行循环”,runloop内部实际上就是一个do-while循环,它在循环监听着各种事件源、消息,对他们进行管理并分发给线程来执行。

1.通知观察者将要进入运行循环。

线程和 RunLoop 之间是一一对应的

2.通知观察者将要处理计时器。

3.通知观察者任何非基于端口的输入源即将触发。

4.触发任何准备触发的基于非端口的输入源。

5.如果基于端口的输入源准备就绪并等待触发,请立即处理该事件。转到第9步。

6.通知观察者线程即将睡眠。

7.将线程置于睡眠状态,直到发生以下事件之一:

事件到达基于端口的输入源。

计时器运行。

为运行循环设置的超时值到期。

运行循环被明确唤醒。

8.通知观察者线程被唤醒。

9.处理待处理事件。

如果触发了用户定义的计时器,则处理计时器事件并重新启动循环。转到第2步。

如果输入源被触发,则传递事件。

如果运行循环被明确唤醒但尚未超时,请重新启动循环。转到第2步。

10.通知观察者运行循环已退出。

14、哪些场景可以触发离屏渲染?(知道多少说多少)

离屏渲染Off-Screen Rendering 指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。

以下方式会触发离屏幕渲染

1:使用系统提供的圆角效果也会触发离屏渲染.(masksToBounds = true&&cornerRadius>0才会引发离屏渲染)

2:重写drawRect

3:layer.shadow(Shawdow 可以通过指定路径来取消离屏渲染)

4:layer.mask(Mask 效果无法取消离屏渲染,使用混合图层的方法来模拟 mask 效果,性能各方面都是和无效果持平。)

5:layer.allowsGroupOpacity(GroupOpacity 是指 CALayer 的allowsGroupOpacity属性,UIView 的alpha属性等同于 CALayer opacity属性,

开启离屏渲染的条件是:layer.opacity != 1.0并且有子 layer 或者背景图。

layer.allowsEdgeAntialiasing(该属性用于消除锯齿,离屏渲染条件旋转视图并且设置layer.allowsEdgeAntialiasing = true)

6:layer.shouldRasterize(光栅化会触发离屏渲染,开启 Rasterization=true 后,GPU 只合成一次内容,然后复用合成的结果;合成的内容超过 100ms 没有使用会从缓存里移除,在更新内容时还会产生更多的离屏渲染。对于内容不发生变化的视图,原本拖后腿的离屏渲染就成为了助力)

你可能感兴趣的:(高级面试)