总结下最近的iOS面试题[1]

自己能经常看看也是好的,查漏补缺
以下面试题只是简洁的回答,具体解析会有链接

一、AutoReleasePool,AutoRelease, AutoReleasePool与Runloop及GCD的关系

1.Autorelease对象什么时候释放

在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的。
这里能引出Runloop与AutoReleasePool的关系
程序启动后,在主线程 RunLoop 里注册了两个 Observer:
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用_objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入睡眠) 和 Exit(即将退出Loop),
BeforeWaiting(准备进入睡眠)时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;
Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后

2.Autorelease原理

@autoreleasepool{} 在使用时,编译器会改写成objc_autoreleasePoolPush()/Pop(),其实是对AutoreleasePoolPage的封装。
● AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)
● AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)
● AutoreleasePoolPage每个对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址
● 上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置
● 一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入

每次push时,会加一个哨兵,这样pop时就会给新加入的每个对象发送release消息,直到哨兵位置。
注:使用容器的block版本的枚举器时,内部会自动添加一个AutoreleasePool:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { // 这里被一个局部@autoreleasepool包围着}];
当然,在普通for循环和for in循环中没有,所以,新版的block版本枚举器更加方便。for循环中遍历产生大量autorelease变量时,就需要手加局部AutoreleasePool。

3.runloop与GCD的关系

在runloop中大量使用了GCD
1.RunLoop的超时时间使用的是GCD中的dispatch_source_t;
2.GCD MainQueue上的异步任务由runloop来执行。

黑幕背后的AutoRelease

二、内存泄漏的地方及场景

1.block

若有强强引用,记得__weak,修改局部变量时__block等修饰

2.代理

代理若用strong等强引用 ,会存在内存泄漏,一般用weak/assign修饰,weak相对于assign来说会将指针置为nil,防止野指针的存在。

3.timer

timer强引用target(self),一般self又会持有time作为属性,这样就造成了循环引用。

4.CF类型内存

注意以creat,copy作为关键的函数一定记得配对使用,释放

5.通知,观察者

成对存在,add和remove

三、copy是否会修改内存引用计数,与mutableCopy的区别,及与strong的区别

1.对不可变或者可变的对象进行mutableCopy操作,是一次深拷贝,返回的是可变的对象
2.对不可变的对象进行copy,是一次浅拷贝,返回一个不可变的对象
3.对可变对象进行copy,是一次深拷贝,产生了不可变的对象副本。
2.copy与strong主要区别就是深拷贝和浅拷贝,内存计数一样都会增加,只是针对mutableStr,copy是深拷贝,即不仅拷贝对象还拷贝指针,指的不是同一个对象,而strong浅拷贝,只是针对指针。

四、死锁的原因

产生死锁的4个必要条件
1.互斥条件:指进程对所分配到的资源进行排他性使用,即在一段时间内某资源只由一个进程使用
2.请求和保持条件:该进程已经保持至少一个资源,但还要求已被占领的其他资源
3.不剥夺条件:指进程已获得资源,在未使用完成时不能被剥夺,只能在使用完成时由自己释放
4.环路等待条件:指在发生死锁时,必然有进程等待下个被占有的资源,并形成环路
解决方案:打破其中一个条件皆可
了解ios中GCD经典死锁原理

五、OC的发送消息机制以及拦截调用,都做哪些修改判断,isa指针等内容

1.对象组成结构详解

结构详解

运行时,每个对象其实都是个结构体,会通过object的isa指针找到它的class

typedef struct objc_object {
    Class isa;
} *id;
typedef struct objc_class *Class
struct object_class{
    Class isa;
    Class super_class;     //父类
    const char* name;    //类名
    long version;             //版本信息
    long info;                  //类信息
    long instance_size;                                       //实例大小
    struct objc_ivar_list *ivars;                          //实例参数链表
    struct objc_method_list *methodLists;     //方法链表
    struct objc_cache *cache;                         //方法缓存
    struct objc_protocol_list *protocols;        //协议链表}
typedef struct objc_method{
    SEL method_name;    
    char *method_type;
    IMP method _imp;
};
2.OC的发送消息机制
总结下最近的iOS面试题[1]_第1张图片
isazhizhen.png

如图:1.调用一个对象方法时,实则是给对象发送了一条消息,以[object foo],在编译OC函数调用的语法时,会被编译成一个c的函数调用:objc_msgSend();
2.objc_msgSend()通过object的isa指针找到它的class,在class的缓存方法列表中找到foo,如果没找到,就去method_Lists中找,如果没找着,就去superclass和元组中找,找到就去实现(IMP),如果一直没找到动态方法决议与消息转发
Dynamic Method Resolution(动态方法决议)
3.如果调用的方法是实例方法,OC的运行时会调用- (BOOL)resolveInstanceMethod:(SEL)sel,如果是类方法,则会调用+ (BOOL)resolveClassMethod:(SEL)sel 让我们可以在程序运行时动态的为一个selector提供实现,如果我们添加了函数的实现,并返回YES,运行时系统会重启一次消息的发送过程,调用动态添加的方法
4.如果返回NO,就会进入消息转发
2、Message Forwarding(消息转发)
消息转发分为两步:

  1. 首先运行时系统会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法,如果这个方法中返回的不是nil或者self,运行时系统将把消息发送给返回的那个对象
  2. 如果- (id)forwardingTargetForSelector:(SEL)aSelector返回的是nil或者self,运行时系统首先会调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法来获得方法签名,方法签名记录了方法的参数和返回值的信息,如果-methodSignatureForSelector 返回的是nil, 运行时系统会抛出unrecognized selector exception,程序到这里就结束了。如果有返回,至forwardInvocation.
    总结下最近的iOS面试题[1]_第2张图片
    xiaoxi.png

    因此,可通过该体制能做很多事情,如多重代理转发,demo,提前防奔溃 ,JSPatch热更新也用到了该机制等等

六、内存的几大区域

总结下最近的iOS面试题[1]_第3张图片
neicunquyu.png

1.栈区:由编译器自动分配并释放,存放函数的参数值,局部变量等。栈是系统数据结构,对应线程/进程是唯一的。优点是快速高效,缺点是有限制,数据不灵活(先进后出) 分为静态分配和动态分配两种
2.堆区:由程序员分配和释放,如果没释放,程序结束时,可能会由操作系统回收,比如ios中alloc都存放于堆中的。灵活方便,数据适应面更广泛,但效率有一定的降低。堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。堆空间的分配总是动态的。
3.全局区(静态区static) 全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放
4.文字常量区 存放常量字符串,程序结束后由系统释放
5.代码区 存放函数的二进制代码

七、界面优化

1.页面间跳转的性能优化

页面间跳转性能优化
1.若是跳转页因加载数据太多,可将数据放到分线程中做
2.若是UI多,可将UI放到下个Runloop中去做,这样coreAnimation的动画并没有影响,而且结束了这个0.35秒时间

2.CPU和GPU综合考虑

ios保持界面流畅的技巧
在界面上市场上有各种各样的看法,可以多阅览,找适合自己项目的才是最好的,多实验

八、https原理,charles拦截原理

https原理
总结下最近的iOS面试题[1]_第4张图片
https yuanli.png

1.HTTP协议:超文本传输协议,定制传输数据的规范(客户端浏览器或其他程序与Web服务器之间的应用层通信协议),SOCKET实现,通过TCP经过三次握手连接。但很容易被冒充

  1. HTTPS协议:可以理解为HTTP+SSL/TLS, 即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL,用于安全的 HTTP 数据传输。
    步骤:
    如图:
    1).客户端发送一次握手
    2).服务端将发送一个ssl证书给客户端(证书包含了证书的发布机构、有效期、公钥、证书所有者、签名等)
    3).客户端收到ssl证书时,会对证书的真伪进行校验(除了校验所有者、有效期,还会校验是否是可信任证书,用公钥解密,检验hash值及签名是否被更改)
    4).证书通过后,就可以告诉服务端用对称密钥加密传输了
    5).客户端与服务对内容通过对称密钥加密传输
    HTTPS之所以较为安全,主要在2,3步,服务端难以被冒充

  2. HTTPS缺点
    1) SSL 证书费用很高,以及其在服务器上的部署、更新维护非常繁琐。貌似12306一直用的就是免费证书
    2)HTTPS 降低用户访问速度(多次握手)
    3.)网站改用HTTPS 以后,由HTTP 跳转到 HTTPS 的方式增加了用户访问耗时(多数网站采用302跳转)
    4) HTTPS 涉及到的安全算法会消耗 CPU 资源,需要增加大量机器(HTTPS访问过程需要加解密)

charles拦截原理

charles拦截
尝试下抓包就知道,中间需要自己手动设置代理,下载信任证书,打破了Https的2,3步,当然能拦截了,自找的哈
试了一下,内容果然一清二楚,吓的我赶紧把代理关掉,证书删掉,取消信任

篇幅较长,先整理到这里,会陆续整理出来,多多总结学习,找工作so easy

感谢各位贡献资料的大佬,学习永无止境

你可能感兴趣的:(总结下最近的iOS面试题[1])