iOS面试题

1.堆和栈的区别

管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

申请大小:

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出

分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。

2.iOS与JS交互相互调用(利用iOS的系统框架JavaScriptCore)

1.凡事添加了JSExport协议的协议,所规定的方法变量等,就会对js开放,我们可以通过js调用到。

2.iOS调用js方法

方式一:- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

方式二:- (JSValue *)evaluateScript:(NSString *)script;

方式三:根据 .jsContext获取JSValue,执行- (JSValue *)callWithArguments:(NSArray *)arguments;

3.列举cocoa中常见对几种多线程的实现,并谈谈多线程安全的几种解决办法及多线程安全怎么控制?

1>只在主线程刷新访问UI

2>如果要防止资源抢夺,得用synchronized进行加锁保护

3>如果异步操作要保证线程安全等问题,尽量使用GCD(有些函数默认就是安全的)

4.GCD内部怎么实现的

1> iOS和OS X的核心是XNU内核,GCD是基于XNU内核实现的

2> GCD的API全部在libdispatch库中

3> GCD的底层实现主要有Dispatch Queue和Dispatch Source

Dispatch Queue:管理block(操作)

Dispatch Source:处理事件

5.你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和GCD的区别和类似的地方

1> GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装

2> GCD只支持FIFO的队列,NSOperationQueue可以很方便地调整执行顺序、设置最大并发数量

3> NSOperationQueue可以在轻松在Operation间设置依赖关系,而GCD需要写很多的代码才能实现

4> NSOperationQueue支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)

5> GCD的执行速度比NSOperationQueue快

任务之间不太互相依赖:GCD

任务之间有依赖\或者要监听任务的执行情况:NSOperationQueue

6.类别的作用?继承和类别在实现中有何区别?

category可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改。并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。

类别主要有3个作用:

(1)将类的实现分散到多个不同文件或多个不同框架中。

(2)创建对私有方法的前向引用。

(3)向对象添加非正式协议。

继承可以增加,修改或者删除方法,并且可以增加属性。

7.什么是TCP连接的三次握手

第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)

8.利用Socket建立网络连接的步骤

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket。

套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

1服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。

2客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

3连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

9.空指针 野指针

僵尸对象:已经被销毁的对象(不能再使用的对象)

野指针:指向僵尸对象(不可用内存)的指针,给野指针发消息会报EXC_BAD_ACCESS错误。

空指针:没有指向存储空间的指针(里面存的是nil,也就是0);给空指针发消息是没有任何反应的;

为了避免野指针错误的常见办法,在对象被销毁之后,将指向对象的指针变为空指针

Xcode开启僵尸对象的监控

iOS面试题_第1张图片

10.UIViewController 的创建加载过程总结

 1)initWithCoder、awakeFromNib

initWithCoder:反归档,如果对象是从文件解析来的就会调用。

awakeFromNib: 从xib或者storyboard加载完毕会调用。

新建UIView的子类并且想在load nib的时候做一些初始化工作的时候 可以重写awakeFromNib。bundle在load nib后会给每个view对象发送一个awakeFromNib消息。

a、用storyboard,调用方法顺序:

initialize -> initWithCoder -> awakeFromNib -> loadView

b、用Xib或者纯代码:

如果用[[VC alloc] init] 来初始化:

initialize -> init -> initWithNibName -> loadView

如果用[[VC alloc] initWithNibName:@“VC” bundle:nil] 来初始化:

initialize -> initWithNibName -> loadView

使用Xib来实现VC的时候,不要重写loadView方法。如果重写了loadView方法,则Xib中View就会消失,变成空View。

总结:

a.重写loadView方法,则会根据重写的loadView方法创建view

b.控制器通过storyboard加载,则根据storyboard的描述创建view

c.控制器view通过xib加载,则根据nibName对应的xib创建view

d.没有指定nibName,则根据与控制器同名的xib创建view

e.没有同名的xib,则根据与控制器名前缀相同不带controller的xib创建view

f.如果都没有,则创建一个空白的xib

g.awakeFromNib当控制器从nib加载的时候就会调用这个方法

h.storyboard加载的是控制器及控制器view,而xib加载的仅仅只是控制器的view

11.load、initialize方法对比

load、initialize是继承的NSObject的方法。

load和initialize方法都会在实例化对象之前调用,以main函数为分水岭,前者在main函数之前调用,后者在之后调用。这两个方法会被自动调用,不能手动调用它们。

load和initialize方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类。

load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。

load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。

12.afnetworking的内部实现原理,它是基于什么多线程写的?

1.AFURLConnectionOperation可以说是AFN最基础的类。继承自NSOperation类,将网络请求依附到一个operation上。从而让我们能够有效的控制并观察一个网络请求的创建、进行、取消、完成、暂停恢复、异常等问题及状态。

2.AFHTTPRequestOperation HTTP或HTTPS协议请求的AFURLConnectionOperation的子类。它封装的可接受状态码和内容的类型,判定一个请求结果是成功或失败

3.AFHTTPRequestOperationManager 这个类是AFN类库的核心类。它封装完成了一种通用的模式,可以帮助我们轻松友好的完成请求的创建、响应的系列化,网络状态的监控以及安全策略以及每一个请求operation的管理(operation的相互依赖或状态改变)

4.AFURLRequestSerialization 符合这个协议的对象用于处理请求,它将请求参数转换为 query string 或是 entity body 的形式,并设置必要的 header

5.AFURLResponseSerialization 遵循AFURLResponseSerialization协议的对象,用于验证、序列化响应及相关数据,转换为有用的形式,比如 JSON 对象、图像、甚至基于mantle的模型对象

6.AFNetworkReachabilityManager 网络的连通状态监控以及网络的类型。实际是将苹果官方提供的Reachability的类名和通知名更换了一下,防止和系统提供的类的通知名以及类名的冲突

你可能感兴趣的:(iOS面试题)