iOS面试题将来跳槽用收藏ios

iOS高级开发工程师面试题 重点!!!

2018-03-26  本文已影响1096人  司徒新新

总结了一堆面试题,希望能对大家有一些帮助!

  1. static的作用?
    (1)static修饰的函数是一个内部函数,只能在本文件中调用,其他文件不能调用。
    (2)static修饰的全部变量是一个内部变量,只能在本文件中使用,其他文件不能使用。
    (2)static修饰的局部变量只会初始化一次,并且在程序退出时才会回收内存。

  2. runtime介绍
    https://www.jianshu.com/p/8836dfac7f2c

  3. TCP等


    未命名文件.jpg

4.GET和POST的区别


2.jpg

5.tableView的重用机制
首先需要注册class,意思就是告诉tableView,首先去缓冲池中找有没有可重用的cell,如果有,则拿过来重用;如果没有,那么根据之前注册的UITableViewCell这个类,来自动生成一个cell,并且给它绑定上重用identifier。

6.减少cellForRowAtIndexPath代理中的计算量
首先要提前计算每个cell中需要的一些基本数据,代理调用的时候直接取出;
图片要异步加载,加载完成后再根据cell内部UIImageView的引用设置图片;
图片数量多时,图片的尺寸要跟据需要提前经过transform矩阵变换压缩好(直接设置图片的contentMode让其自行压缩仍然会影响滚动效率),必要的时候要准备好预览图和高清图,需要时再加载高清图。
图片的’懒加载’方法,即延迟加载,当滚动速度很快时避免频繁请求服务器数据。
尽量手动Drawing视图提升流畅性,而不是直接子类化UITableViewCell,然后覆盖drawRect方法,因为cell中不是只有一个contentview。绘制cell不建议使用UIView,建议使用CALayer。原因要参考UIView和CALayer的区别和联系。
减少heightForRowAtIndexPath代理中的计算量
由于每次TableView进行update更新都会对每一个cell调用heightForRowAtIndexPath代理取得最新的height,会大大增加计算时间。如果表格的所有cell高度都是固定的,那么去掉heightForRowAtIndexPath代理,直接设置TableView的rowHeight属性为固定的高度;

如果高度不固定,应尽量将cell的高度数据计算好并储存起来,代理调用的时候直接取,即将height的计算时间复杂度降到O(1)。例如:在异步请求服务器数据时,提前将cell高度计算好并作为dataSource的一个数据存到数据库供随时取用。
当cell为nil,需要创建新的cell的时候,使用的是initWithStyle:reuseIdentifier:方法,而不是init方法,这样做是因为创建新的cell的时候需要绑定一个identifier,这样在重用的时候才能找到可重用的相同类型。如果使用init方法则没有绑定identifier,这样在重用的时候无法成功找到对应的可重用的cell。

一般在if(!cell)中,也就是在新创建cell的时候,将一些只需要初始化一次的属性进行初始化,而不是在这个括号的外面。因为在括号外面会执行多次,而这些属性并不需要多次设置。同样,如果不同的cell需要设置不同属性或数据,那么需要在括号外执行,因为括号外面每次cell出现都会执行到,这样可以保证不用的cell对应不同的属性或数据。如果将本该设置不同cell对应不同属性的代码放在括号里面,在复用cell的时候不会重新覆盖这些数据,会出现不正确的结果,造成数据冗余的问题

7.weak的实现原理
http://www.cocoachina.com/ios/20171213/21520.html

8.nonatomic和atomic的区别
nonatomic:表示非原子,不安全,但是效率高。
atomic:表示原子行,安全,但是效率定。
atomic:不能绝对保证线程的安全,当多线程同时访问的时候,会造成线程不安全。可以使用线程锁来保证线程的安全。

9.进程与线程

进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
同步:阻塞当前线程操作,不能开辟线程。
异步:不阻碍线程继续操作,可以开辟线程来执行任务。
并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。
并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
区别:并发和并行是即相似又有区别的两个概念,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。倘若在计算机系统中有多个处理机,则这些可以并发执行的程序便可被分配到多个处理机上,实现并行执行,即利用每个处理机来处理一个可并发执行的程序,这样,多个程序便可以同时执行

10.block的本质
1)block:本质就是一个object-c对象.
block:存储位置,可能分为3个地方:代码去,堆区、栈区(ARC情况下会自动拷贝到堆区,因此ARC下只能有两个地方:代码去、堆区)
代码区:不访问栈区的变量(如局部变量),且不访问堆区的变量(alloc创建的对象),此时block存放在代码去。
堆区:访问了处于栈区的变量,或者堆区的变量,此时block存放在堆区。–需要注意实际是放在栈区,在ARC情况下会自动拷贝到堆区,如果不是ARC则存放在栈区,所在函数执行完毕就回释放,想再外面调用需要用copy指向它,这样就拷贝到了堆区,strong属性不会拷贝、会造成野指针错区。

2)为什么在默认情况下无法修改被block捕获的变量? __block都做了什么?
默认情况下,block里面的变量,拷贝进去的是变量的值,而不是指向变量的内存的指针。
当使用__block修饰后的变量,拷贝到block里面的就是指向变量的指针,所以我们就可以修改变量的值。

11.app开启之后,在执行main之前都干了什么事?

1、APP启动时间
1)main之前的系统dylib(动态链接库)和自身App可执行文件的加载的时间
2)main之后执行didFinishLaunchingWithOptions:结束前的时间

2、main之前的加载过程
1)首先加载可执行文件(自身app的所有.o文件集合)
2)然后加载动态链接库dyld,dyld是一个专门用来加载动态链接库的库
3)执行从dyld开始,dyld从可执行文件的依赖开始,递归加载所有的依赖动态链接库
4)动态链接库包括:iOS中用到的所有系统的framework,加载OC runtime方法的libobjec,系统级别的libSystem,例如libdispatch(GCD)he libsystem_blocks(Block)
5)dyld:the dynamic link editor,所有动态链接库和我们App的静态库.a和所有类文件编译后.o文件,最终都由dyld加载到内存的

3、动态链接库库是相对于系统来讲的

4、可执行文件是相对于App本身来讲的

5、每个app 都是以镜像为单位进行加载的
1)镜像(Mirroring)是冗余的一种类型,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本即为镜像。
2)镜像是一种文件存储形式,可以把许多文件做成一个镜像文件。
3)每个镜像又都有个ImageLoader类来负责加载,一一对应的关系

6、framework 是动态链接库和相应资源包含在一起的一个文件结构

7、系统使用动态链接库的好处
1)代码共用:很多程序都动态链接了这些lib,但它们在内存和磁盘中只有一份
2)易于维护:由于被依赖的lib是程序执行时才链接的,所以这些很容易更新,只要保证在程序执行之前,获取最新lib即可
3)减少可执行文件体积:相比静态链接,动态链接在编译时不需要打进去,所有可执行文件的体积要小很多

8、动态链接库的加载步骤具体分为5步
1)load dylibs image 读取库镜像文件
2)Rebase image 重定位镜像
3)Bind image 组装镜像
4)Objc setup 设置对象
5)initializers 初始化

9、第一步又分为下面6个过程
1)分析所依赖的动态库
2)找到动态库的mach-o 文件(我们知道Windows下的文件都是PE文件,同样在OS X和iOS中可执行文件是Mach-o格式的。)
3)打开文件
4)验证文件
5)在系统核心注册文件签名
6)对动态库的每一个segment调用mmap()

由于ASLR(address apace layout randomization)的存在,可执行文件和动态链接库在虚拟内存中的加载地址每次启动都不固定,所以需要这两步俩修复镜像中的资源地址,来纸箱正确的地址
1)rabase 修复的是指当前镜像内存的资源指针;bind指向的是镜像外部的资源指针
2)rebase步骤先进行,需要把镜像读入内存,并以page为单位进行加密验证,保证不会被篡改;bind 在其后进行,由于要查询表符号表,来指向镜像的资源;

  1. 注册objc类
    2)把category的定义插入方法列表
    3)保证每个selector唯一

0)以上三步属于静态调整(fix up),都是在修改__DATA segment中内容,从这里开始动态调整,开始在堆和堆栈写入内容
1)objc 的 + load 函数
2)C++的构造函数属性函数 形如 attribute((contructor))void DoSomeInitializationWork()
3)非基本类型的C++静态全局变量的创建(通常是类或结构体)(non-trivial initializer)重大初始化
13、再次回顾整个调用顺序
1)dyld 开始将程序二进制文件初始化
2)交由ImageLoader 读取 image,其中包含了我们的类,方法等各种符号(Class、Protocol 、Selector、 IMP)
3)由于runtime 向dyld 绑定了回调,当image加载到内存后,dyld会通知runtime进行处理
4)runtime 接手后调用map_images做解析和处理
5)接下来load_images 中调用call_load_methods方法,遍历所有加载进来的Class,按继承层次依次调用Class的+load和其他Category的+load方法
6)至此 所有的信息都被加载到内存中
7)最后dyld调用真正的main函数
8)注意:dyld会缓存上一次把信息加载内存的缓存,所以第二次比第一次启动快一点

同时在网上找了一个这个,
https://blog.csdn.net/demondev/article/details/52347136

12.iOS开发中BitCode功能
https://www.jianshu.com/p/8eff48e5c010

10.另一篇很好的面试题,我从头看到尾的. 推荐大家一定要看看这个...因为很多我想写的,他这里都有.我要在写一遍,总有一种照人抄的感觉..哈哈!
http://www.jianshu.com/p/f9eb6b315c08

11.简单看看这个也行,也是一个面试题,可能是哪个公司的面试题吧...
https://www.jianshu.com/p/2aef7e5aef15

上一篇下一篇

猜你喜欢

热点阅读