2018iOS最新面试题
1. 数组和链表有什么区别?
- 数组:数组元素在内存上连续存放,可以通过下标查找元素;插入、删除需要移动大量元素,比较适用于元素很少变化的情况
- 链表:链表中的元素在内存中不是顺序存储的,查找慢,插入、删除只需要对元素指针重新赋值,效率高
2. GCD 之线程死锁?
GCD 确实好用 ,很强大,相比NSOpretion 无法提供 取消任务的功能。
如此强大的工具用不好可能会出现线程死锁。 如下代码:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"=================4");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"=================5");
});
NSLog(@"=================6");
}
GCD Queue 分为三种:
1,The main queue :主队列,主线程就是在个队列中。
2,Global queues : 全局并发队列。
3,用户队列:是用函数 dispatch_queue_create 创建的自定义队列
dispatch_sync 和 dispatch_async 区别:
dispatch_async(queue,block) async 异步队列,dispatch_async 函数会立即返回, block会在后台异步执行。
dispatch_sync(queue,block) sync 同步队列,dispatch_sync 函数不会立即返回,及阻塞当前线程,等待 block同步执行完成。
分析上面代码:
viewDidLoad 在主线程中, 及在
dispatch_get_main_queue() 中,执行到sync 时 向
dispatch_get_main_queue()插入 同步 threed1.
sync 会等到 后面block 执行完成才返回, sync 又再 dispatch_get_main_queue() 队列中,
它是串行队列,sync 是后加入的,前一个是主线程,
所以 sync 想执行 block 必须等待主线程执行完成,主线程等待 sync 返回,去执行后续内容。
照成死锁,sync 等待mainThread 执行完成, mianThread 等待sync 函数返回。
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"=================1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"=================2");
});
NSLog(@"=================3");
});
}
程序会完成执行,为什么不会出现死锁。
首先: async 在主线程中 创建了一个异步线程 加入 全局并发队列,async 不会等待block 执行完成,立即返回,
1,async 立即返回, viewDidLoad 执行完毕,及主线程执行完毕。
2,同时,全局并发队列立即执行异步 block , 打印 1, 当执行到 sync 它会等待 block 执行完成才返回, 及等待
dispatch_get_main_queue() 队列中的 mianThread 执行完成, 然后才开始调用block 。
因为1 和 2 几乎同时执行,因为2 在全局并发队列上, 2 中执行到sync 时 1 可能已经执行完成或 等了一会,mainThread 很快退出, 2 等已执行后续内容。
如果阻塞了主线程,2 中的sync 就无法执行啦,mainThread 永远不会退出, sync 就永远等待着,
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"=================1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"=================2");
});
NSLog(@"=================3");
});
NSLog(@"==========阻塞主线程");
while (1) {
}
NSLog(@"========2==阻塞主线程");
}
打印如下:
2014-11-30 17:56:22.296 Test[6108:379350] =================1
2014-11-30 17:56:22.296 Test[6108:379231] ==========阻塞主线程
永远等着。。。。。
知道原理以后就会减少出错啦。
3.iOS程序执行顺序和UIViewController 的生命周期
** 具体执行流程 **
-
程序入口
进入main函数,设置AppDelegate称为函数的代理 -
程序完成加载
[AppDelegate application:didFinishLaunchingWithOptions:] -
创建
window窗口 -
程序被激活
[AppDelegate applicationDidBecomeActive:] -
当点击
command+H时(针对模拟器,手机是当点击home键)
程序取消激活状态
[AppDelegate applicationWillResignActive:];
程序进入后台
[AppDelegate applicationDidEnterBackground:]; -
点击进入工程
程序进入前台
[AppDelegate applicationWillEnterForeground:]
程序被激活
[AppDelegate applicationDidBecomeActive:];
3.1 视图的生命历程
[ViewController initWithCoder:]或[ViewController initWithNibName:Bundle]: 首先从归档文件中加载UIViewController对象。即使是纯代码,也会把nil作为参数传给后者。[UIView awakeFromNib]:作为第一个方法的助手,方法处理一些额外的设置。- **
[ViewController loadView]: 创建或加载一个view并把它赋值给UIViewController的view属性。
-[ViewController viewDidLoad]: ** 此时整个视图层次(view hierarchy)已经放到内存中,可以移除一些视图,修改约束,加载数据等。 - **
[ViewController viewWillAppear:]: ** 视图加载完成,并即将显示在屏幕上。还没设置动画,可以改变当前屏幕方向或状态栏的风格等。 -
[ViewController viewWillLayoutSubviews]:即将开始子视图位置布局 -
[ViewController viewDidLayoutSubviews]:用于通知视图的位置布局已经完成 -
[ViewController viewDidAppear:]:视图已经展示在屏幕上,可以对视图做一些关于展示效果方面的修改。 -
[ViewController viewWillDisappear:]:视图即将消失 -
[ViewController viewDidDisappear:]:视图已经消失
3.2 总结
-
只有
init系列的方法,如initWithNibName需要自己调用,其他方法如loadView和awakeFromNib则是系统自动调用。而viewWill/Did系列的方法则类似于回调和通知,也会被自动调用。 -
纯代码写视图布局时需要注意,要手动调用
loadView方法,而且不要调用父类的loadView方法。纯代码和用IB的区别仅存在于loadView方法及其之前,编程时需要注意的也就是loadView方法。 -
除了
initWithNibName和awakeFromNib方法是处理视图控制器外,其他方法都是处理视图。这两个方法在视图控制器的生命周期里只会调用一次。
4. 数组字符的所有排列组合的可能性?
- dataArr = @[@"1",@"2",@"3",@"4",@"5",@"6"];
- for (int i = 0; i < dataArr.count; i++)
- {
- [self combine:(int)dataArr.count index:i + 1 temp:@""];
- }
- //表示从n个元素中取出k个元素的取法数
- - (void)combine:(int)n index:(int)k temp:(NSString *)str
- {
- for(int i = n; i >= k; i--)
- {
- if(k > 1)
- {
- [self combine:i-1 index:k-1 temp:[NSString stringWithFormat:@"%@%@",str,[dataArr objectAtIndex:i-1]]];
- }
- else
- {
- NSLog(@"%@",[NSString stringWithFormat:@"%@%@",str,[dataArr objectAtIndex:i-1]]);
- }
- }
- }
5.属性关键字的使用
1、 assign###
- ‘基本数据类型’、‘枚举’、‘结构体’ 等非OC对象类型
2、 weak
- 一般应用:UI控件
- xib和纯代码布局的区别(xib布局默认是weak,苹果也强烈建议用weak,主要是防止在用的时候出现循环引用,因为控制器是被强指针应用的,所有说苹果建议UI控件用weak修饰,但是要是纯代码布局用weak修饰,控件不会显示,其实这个原因很明显,使用weak时,由于控件在创建时,没有任何强引用,因此就有可能提前释放,而xib却没有事情,因为xib创建的控件默认是强指针执行,你仔细想的话,苹果又会在之前加了一个IBOutlet这个关键词,在我们创建了IBOutlet之后,我们系统会有一个自动对它进行一个强引用,也就是又多了一条实线连接着它) 所以要是控制器中纯代码创建控件的时候,用strong修饰就行了,
3、 strong
- OC对象类型(NSArray、NSDate、NSNumber、模型类)
- 一个对象只要有强指针引用着,就不会被销毁
4、 copy
- 一般用在NSString*类型、block类型上
- copy语法的作用:
- 产生副本
- copy返回的是不可变的副本
- mutableCopy返回的是可变的副本
- 修改了副本并不会影响源对象,修改了源对象,并不会影响副本。
- 产生副本
- copy在属性声明中的使用,直接举例说明
- NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态
- UITrackingRunLoopMode:ScrollView滑动时会切换到该Mode
- UIInitializationRunLoopMode:run loop启动时,会切换到该mode
- NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
- NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
- NSRunLoopCommonModes(kCFRunLoopCommonModes)
- 在编程中:如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。当我们滚动的时候,也希望不调度,那就应该使用默认模式。但是,如果希望在滚动时,定时器也要回调,那就应该使用common mode。
- 当屏幕上滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元从屏幕外滑入屏幕内时,从重用队列中找看有没有可以重用的单元格,若有,就直接用,没有就重新创建一个。
- UITableView通过重用单元格来达到节省内存的目的,通过为每个单元格指定一个重用标示(reuseidentifier),即指定了单元格的种类,以及当单元格滚出屏幕时,允许恢复单元格以便复用。对于不同种类的单元格使用不同的ID,对于简单的表格,一个标示符就够了。
- 如一个TableView中有10个单元格,但屏幕最多显示4个,实际上iPhone只为其分配4个单元格的内存,没有分配10个,当滚动单元格时,屏幕内显示的单元格重复使用这4个内存。实际上分配的cell的个数为屏幕最大显示数,当有新的cell进入屏幕时,会随机调用已经滚出屏幕的Cell所占的内存,这就是Cell的重用。
- 对于多变的自定义Cell,这种重用机制会导致内容出错,为解决这种出错的方法,把原来的
|
1
2
|
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:defineString]
修改为:UITableViewCell *cell = [tableview cellForRowAtIndexPath:indexPath];
|
- 对于这四个异步请求,要判断都执行完成最简单的方式就是通过GCD的group来实现:
|
1
2
3
4
5
6
7
8
9
|
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*任务a */ });
dispatch_group_async(group, queue, ^{ /*任务b */ });
dispatch_group_async(group, queue, ^{ /*任务c */ });
dispatch_group_async(group, queue, ^{ /*任务d */ });
dispatch_group_notify(group,dispatch_get_main_queue(), ^{
// 在a、b、c、d异步执行完成后,会回调这里
});
|
- 当然,我们还可以使用非常老套的方法来处理,通过四个变量来标识a、b、c、d四个任务是否完成,然后在runloop中让其等待,当完成时才退出runloop。但是这样做会让后面的代码得不到执行,直到Run loop执行完毕。
- 解释:要求顺序执行,那么可以将任务放到串行队列中,自然就是按顺序来异步执行了。
- 代码紧凑,传值、回调都很方便,省去了写代理的很多代码。
- NSTimer封装成的block。
- 实现方法:
|
1
2
3
4
5
6
|
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
repeats:YES
callback:^() {
weakSelf.secondsLabel.text = ...
}
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
|
- 第一种:如果是点击某个按钮后,才会刷新它的值,其它不用修改,那么不用引用任何按钮,直接在回调时,就已经将接收响应的按钮给传过来了,直接通过它修改即可。
- 第二种:点击某个按钮后,所有与之同类型的按钮都要修改值,那么可以通过在创建按钮时将按钮存入到数组中,在需要的时候遍历查找。
- 一个程序至少要有进城,一个进程至少要有一个线程。
- 进程:资源分配的最小独立单元,进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
- 线程:进程下的一个分支,是进程的实体,是CPU调度和分派的基本单元,它是比进程更小的能独立运行的基本单位,线程自己基本不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器、一组寄存器、栈),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。
- 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
- 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
- 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
- NSThread:当需要进行一些耗时操作时会把耗时的操作放到线程中。线程同步:多个线程同时访问一个数据会出问题,NSlock、线程同步块、@synchronized(self){}。
- NSOperationQueue操作队列(不需考虑线程同步问题)。编程的重点都放在main里面,NSInvocationOperation、BSBlockOperation、自定义Operation。创建一个操作绑定相应的方法,当把操作添加到操作队列中时,操作绑定的方法就会自动执行了,当把操作添加到操作队列中时,默认会调用main方法。
- GCD(`Grand Central Dispatch)宏大的中央调度,串行队列、并发队列、主线程队列;
- 同步和异步:同步指第一个任务不执行完,不会开始第二个,异步是不管第一个有没有执行完,都开始第二个。
- 串行和并行:串行是多个任务按一定顺序执行,并行是多个任务同时执行;
- 代码是在分线程执行,在主线程嘟列中刷新UI。
- Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法;
- 一个NSOperationQueue操作队列,相当于一个线程管理器,而非一个线程,因为你可以设置这个线程管理器内可以并行运行的线程数量等。
- 多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径。
- iPhoneOS下的主线程的堆栈大小是1M。第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI的能力。
- 定时器;可以执行多次,默认在主线程中。
- 线程:只能执行一次。


- TCP为传输控制层协议,为面向连接、可靠的、点到点的通信;
- UDP为用户数据报协议,非连接的不可靠的点到多点的通信;
- TCP侧重可靠传输,UDP侧重快速传输。
- 第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
- 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN+RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次状态。
- HTTP协议是基于TCP连接的,是应用层协议,主要解决如何包装数据。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
- HTTP连接:短连接,客户端向服务器发送一次请求,服务器响应后连接断开,节省资源。服务器不能主动给客户端响应(除非采用HTTP长连接技术),iPhone主要使用类NSURLConnection。
- Socket连接:长连接,客户端跟服务器端直接使用Socket进行连接,没有规定连接后断开,因此客户端和服务器段保持连接通道,双方可以主动发送数据,一般多用于游戏.Socket默认连接超时时间是30秒,默认大小是8K(理解为一个数据包大小)。
- HTTP超文本传输协议,是短连接,是客户端主动发送请求,服务器做出响应,服务器响应之后,链接断开。HTTP是一个属于应用层面向对象的协议,HTTP有两类报文:请求报文和响应报文。
- HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4部分组成。
- HTTP响应报文:由三部分组成:状态行、消息报头、响应正文。
- GET请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度有限制,为1024个字节)。
- POST请求:参数在请求数据区放着,相对GET请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体中.
- GET提交的数据会在地址栏显示出来,而POST提交,地址栏不会改变。
- GET提交时,传输数据就会受到URL长度限制,POST由于不是通过URL传值,理论上书不受限。
- POST的安全性要比GET的安全性高;
- 通过GET提交数据,用户名和密码将明文出现在URL上,比如登陆界面有可能被浏览器缓存。
- HTTPS:安全超文本传输协议(Secure Hypertext Transfer Protocol),它是一个安全通信通道,基于HTTP开发,用于客户计算机和服务器之间交换信息,使用安全套结字层(SSI)进行信息交换,即HTTP的安全版。
- ASIHttpRequest功能强大,主要是在MRC下实现的,是对系统CFNetwork API进行了封装,支持HTTP协议的CFHTTP,配置比较复杂,并且ASIHttpRequest框架默认不会帮你监听网络改变,如果需要让ASIHttpRequest帮你监听网络状态改变,并且手动开始这个功能。
- AFNetWorking构建于NSURLConnection、NSOperation以及其他熟悉的Foundation技术之上。拥有良好的架构,丰富的API及模块构建方式,使用起来非常轻松。它基于NSOperation封装的,AFURLConnectionOperation子类。
- ASIHttpRequest是直接操作对象ASIHttpRequest是一个实现了NSCoding协议的NSOperation子类;AFNetWorking直接操作对象的AFHttpClient,是一个实现NSCoding和NSCopying协议的NSObject子类。
- 同步请求:ASIHttpRequest直接通过调用一个startSynchronous方法;AFNetWorking默认没有封装同步请求,如果开发者需要使用同步请求,则需要重写getPath:paraments:success:failures方法,对于AFHttpRequestOperation进行同步处理。
- 性能对比:AFNetworking请求优于ASIHttpRequest;
- XML数据解析的两种解析方式:DOM解析和SAX解析;
- DOM解析必须完成DOM树的构造,在处理规模较大的XML文档时就很耗内存,占用资源较多,读入整个XML文档并构建一个驻留内存的树结构(节点树),通过遍历树结构可以检索任意XML节点,读取它的属性和值,通常情况下,可以借助XPath查询XML节点;
- SAX与DOM不同,它是事件驱动模型,解析XML文档时每遇到一个开始或者结束标签、属性或者一条指令时,程序就产生一个事件进行相应的处理,一边读取XML文档一边处理,不必等整个文档加载完才采取措施,当在读取解析过程中遇到需要处理的对象,会发出通知进行处理。因此,SAX相对于DOM来说更适合操作大的XML文档。
- JSON解析:性能比较好的主要是第三方的JSONKIT和iOS自带的JSON解析类,其中自带的JSON解析性能最高,但只能用于iOS5之后。
- SVN=版本控制+备份服务器,可以把SVN当成备份服务器,并且可以帮助你记住每次上服务器的档案内容,并自动赋予每次变更的版本;
- SVN的版本控制:所有上传版本都会帮您记录下来,也有版本分支及合并等功能。SVN可以让不同的开发者存取同样的档案,并且利用SVN Server作为档案同步的机制,即您有档案更新时,无需将档案寄送给您的开发成员。SVN的存放档案方式是采用差异备份的方式,即会备份到不同的地方,节省硬盘空间,也可以对非文字文件进行差异备份。
- SVN的重要性:备份工作档案的重要性、版本控管的重要性、伙伴间的数据同步的重要性、备份不同版本是很耗费硬盘空间的;
- 防止冲突:
- 一种是Apple自己提供的通知服务(APNS服务器)、一种是用第三方推送机制。
- 首先应用发送通知,系统弹出提示框询问用户是否允许,当用户允许后向苹果服务器(APNS)请求deviceToken,并由苹果服务器发送给自己的应用,自己的应用将DeviceToken发送自己的服务器,自己服务器想要发送网络推送时将deviceToken以及想要推送的信息发送给苹果服务器,苹果服务器将信息发送给应用。
- 推送信息内容,总容量不超过256个字节;
- iOS SDK本身提供的APNS服务器推送,它可以直接推送给目标用户并根据您的方式弹出提示。
- 第三方推送机制,普遍使用Socket机制来实现,几乎可以达到即时的发送到目标用户手机端,适用于即时通讯类应用。

浙公网安备 33010602011771号