iOS面试总结

一、 GCD和NSOperation的比较

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

2.GCD仅仅支持FIFO队列,只可以设置队列的优先级,而NSOperationQueue中的每一个任务都可以被重新设置优先级(setQueuePriority:),从而实现不同操作的执行顺序调整

3.GCD不支持异步操作之间的依赖关系设置。如果某个操作的依赖另一个操作的数据,使用NSOperationQueue能够设置依赖按照正确的顺序执行操作(addDependency:)。GCD则没有内建的依赖关系支持(只能通过Barrior和同步任务手动实现)。

4. NSOperationQueue方便停止队列中的任务(cancelAllOperations, suspended),GCD不方便停止队列中的任务.

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

6.GCD的执行速度比NSOperationQueue快

7. NSOperationQueue可设置最大并发数量(节电),GCD具有dispatch_once(只执行一次,单例)和dispatch_after(延迟执行)功能

NSThread 一般主要用它来查看线程

二、 Runtime

运行时,它是一种机制

1、它可以动态的创建一个类,比如KVO的实现原理

2、可以动态的修改或创建一个类的属性、方法

3、可以遍历一个类的所有属性和方法

4、可以将动态的生成一个方法,并将原本的方法替换掉,比如AFN发送NSURLSession请求时,我们需要resume,当这个时候,runtime会生成一个方法将这个方法替换掉,并发出通知,使菊花转动,当获取到网络数据的时候在发出通知,取消菊花转动

1> 什么是runtime

runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。

在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者,objc_msgSend

2> runtime干什么用,使用场景

runtime是属于OC的底层, 可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)

在程序运行过程中, 动态创建一个类(比如KVO的底层实现) objc_allocateClassPair,class_addIvar,objc_registerClassPair

在程序运行过程中, 动态地为某个类添加属性\方法, 修改属性值\方法(修改封装的框架)  objc_setAssociatedObject  object_setIvar

遍历一个类的所有成员变量(属性)\所有方法(字典转模型,归解档)  class_copyIvarList class_copyPropertyList class_copyMethodList

三、Runloop

运行循环,运行循环的存在依赖多线程,当我们运行一个进程时,默认会创建一条线程,这个线程就是主线程,主线程的运行循环默认是开启的,子线程的运行循环是关闭的,需要我们输欧东开启,运行循环有两个源 inputSource和 timerSource

inputSource,输入源,是用来与子线程进行交互的

timerSource 定时源,是用来处理主线程操作的,如更新UI等

四、单例

保证程序运行过程中,永远只有一个实例对象

它的目的是:全局共享一份资源,节省不必要的内存开销,同时它能够保证获取到的数据的相同,起到一定的安全性

像我们平时封装的一些工具类都是使用了单例,单例的实现是通过GCD中的一次性代码来完成的(可以接着说GCD,NSOpertion一些多线程的问题)


五、 响应者链条

当触摸事件发生时,压力转为电信号,iOS系统将产生UIEvent对象,记录事件产生的时间和类型,然后系统将事件加入到一个由UIApplication管理的事件队列中。

UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)

主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件(从父到子,从后到前),这也是整个事件处理过程的第一步

找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理


六、Json/XML数据解析

Json数据的解析直接通过苹果源生的方法就可解析(序列化、反序列化)

XML解析方式有两种:DOM解析和SAX解析

DOM解析一般在MAC上使用,手机身很少使用,因为它会将要解析的文件一次性加载进内存,这对手机来讲,负担过重,手机我们一般采用SAX解析

它的解析步骤为:

1.         创建一个解析器(parser)

2.         设置解析器代理

3.         开始解析

之后XML的解析就在代理方法中进行,有5步

1.         准备文档

2.         发现开始节点

3.         发现节点内容

4.         发现结束节点

5.         解析结束

这5步中其中2、3、4这三步是重复进行的

七、Sdwebimage实现原理

这是一个重量级的框架,它最终发送网络请求的方式是NSURLConnection它的执行步骤是这样的:

当我们获取一张图片时,它首先会去内存缓存(缓存是通过字典存数据的,key是url地址,value是图片)中取,根据url地址,如果有图片,就取出显示,如果没有会磁盘上找,如果有,则显示,没有就说明这张图片需要下载,首先会设置占位图片,然后根据key值看是否有这个操作在,如果有,说明图片正在下载,如果没有,需要创建操作,当图片下载完成之后,将图片显示出来,并将图片添加到内存缓存中,同时删除操作。


八、数据存储方式

1.属性列表

2.Preference(NSUserDefaults)

3.键值归档(NSKeyedArchiver、NSCoding)

4.SQLite数据库

5.Core Data

九、 内存管理

一、oc是如何管理内存的

这个分在ARC和非ARC环境下

在非ARC环境下:

1.  每个对象都有一个引用计数器,当我们使用alloc、copy、new创建一个新对象时,它的引用计数器为一,当引用计数器为0时,这个对象就会被销毁,在销毁前会调用一个dealloc方法,相当于这个对象死之前的“遗言”。

2.  当我们reatain一个对象时,会使该对象的引用计数器加1,使用realease或者autorelease,会使引用计数器减1

3.  我们也可以将对象放在autorealeasepool中来管理内存

在ARC环境下:

编译器会自动生成管理内存的代码,不需要我们在考虑对象什么时候销毁,销毁的时候是否要对其他对象进行release

二、内存管理的原则是什么?

这个也要分在ARC和非ARC环境讨论

当在非ARC环境下:

1、当我们使用alloc、copy、new方法创建了一个新的对象,都必须在最后做一次release或者autorelease

2、 只要调用了retain,都必须在最后做一次release或者autorelease

3、 @property如果使用了retain或者copy,就需要对不再使用的对象做一次release或autorelease

如果在ARC环境下:

编译器会自动生成管理内存的代码。不需要我们关心


十、 KVC、KVO

KVC:Key-Value-Coding内部的实现:一个对象在调用setValue的时候,

(1)首先根据方法名找到运行方法的时候所需要的环境参数。

(2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。

(3)再直接查找得来的具体的方法实现。

KV0:键值监听,当一个类的某个属性第一次被监听时,系统运行过程中会动态的创建一个派生类(通过runtime实现),在这个派生类中会重写基类的set方法,并在该方法中发出通知给监听者,实现监听机制

在使用KVO监听时要注意移除监听,否则程序会崩

另外使用KVO监听,性能不好,因为会运行过程中会动态的创建类。

kvo使用场景

①实现上下拉刷新控件contentoffset

②webview混合排版contentsize

③监听模型属性实时更新UI


十一、堆和栈的区别

管理方式:

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

申请大小:

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

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

碎片问题:

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

分配方式:

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

分配效率:

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


十二、分类的作用

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

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

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

十三、 视图的生命周期

loadView - 默认调用super方法,根据控制器创建方式加载视图,重写后将根据重写方法创建视图

viewDidLoad-视图加载完成

viewWillAppear-UIViewController对象的视图即将加入窗口时调用;

viewDidApper-UIViewController对象的视图已经加入到窗口时调用;

viewWillDisappear-UIViewController对象的视图即将消失、被覆盖或是隐藏时调用;

viewDidDisappear-UIViewController对象的视图已经消失、被覆盖或是隐藏时调用;

viewVillUnload-当内存过低时,需要释放一些不需要使用的视图时,即将释放时调用;

viewDidUnload-当内存过低,释放一些不需要的视图时调用。


十四、三次握手

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

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

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


十五、 创建控制器的方式

1.通过代码的方式加载viewController

UIViewController *controller = [[UIViewController alloc] init];

2.通过stroyboard来加载viewController

2.1加载storyboard中箭头指向的viewController

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main"bundle:nil]; //加载箭头指向的viewController

CZViewController *controller = [storyboardinstantiateInitialViewController];

2.2 加载storyboard中特定标示的viewController(storyboard可以有多个controller)

UIStoryboard *storyboard = [UIStoryboardstoryboardWithName:@"Main" bundle:nil];

CZViewController *controller = [storyboardinstantiateViewControllerWithIdentifier:@"two"];

3通过xib加载viewController

 3.1) 传统方法

3.1.1)创建Xib,并指定xib的files owner为自定义控制器类(为了能连线关联管理IB的内容)

3.1.2)xib中要有内容,且xib中描述的控制器类的view属性要与xib的view控件完成关联(关联方法两种,一种是control+files owner拖线到xib中搭建的指定view控件,另一种是指定xib中的view拖线到@interface)

3.1.3)从xib加载viewController

CZViewController *controller = [[CZViewController alloc]initWithNibName:@"CZOneView" bundle:nil];

3.2)bundle中取出xib内容

CZViewController *vc = [[NSBundle mainBundle]loadNibNamed:@"Two" owner:nil options:nil].lastObject;


十六、创建视图的方式

 

1.用系统的loadView方法创建控制器的视图

2.如果指定加载某个storyboard文件做控制器的视图,就会加载storyboard里面的描述去创建view

3.如果指定读取某个xib文件做控制器的视图,就根据指定的xib文件去加载创建

4.如果有xib文件名和控制器的类名前缀(也就是去掉controller)的名字一样的   xib文件就会用这个xib文件来创建控件器的视图例:控件器的名为 MJViewController  xib文件名为 MJView.xib  如果xib文件名后有一个字不一样就不会去根据它去创建如:MJView8.xib

5.找和控制器同名的xib文件去创建

6.如果以上都没有就创建一个空的控制器的视图;


十七、View和layer的区别

图层不会直接渲染到屏幕上,UIView是iOS系统中界面元素的基础,所有的界面元素都是继承自它。它本身完全是由CoreAnimation来实现的。它真正的绘图部分,是由一个CALayer类来管理。UIView本身更像是一个CALayer的管理器。一个UIView上可以有n个CALayer,每个layer显示一种东西,增强UIView的展现能力。

1.都可以显示屏幕效果

2. 如果需要用户交互就要用UIVIew,其可接收触摸事件(继承UIResponder),而CALayer不能接收触摸事件

3.如果没有用户交互可选用CALayer,因为其所在库较小,占用的资源较少



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值