iOS面试旗开得胜之横扫千军篇(一)

本文问题来自iOS面试旗开得胜之问题篇 中的横扫千军之战胜篇

1.谈谈 tableview 的重用机制。

  • 为什么要“重用”?iPhone 重用机制是为了实现大量数据显示而采用的一种节省内存的机制。如果一个 tableview 有几百个 cell,这个内存消耗无疑是很恐怖的。再加上 cell 中还有 image 之类的资源。很容易出现 memory warning 甚至 crash !!!

  • 重用代码

static NSString *CellTableIdentifier = @"cell ";  
UITableViewCell *cell = [tableView  dequeueReusableCellWithIdentifier: CellTableIdentifier];  
if (cell == nil) {  
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellTableIdentifier];  
}

关键函数

- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
forIndexPath:(NSIndexPath *)indexPath;  
  • 文档说明如下: Returns a reusable table-view cell object for the specified reuse identifier and adds it to the table.
    它返回的是一个受 identifier 管理定位的可重用的 tableViewCell。

  • 那么它是如何重用的? 举例:假设系统启动的时候,tableView 可以显示 7 个 tableViewCell ,并且都有 tag 值,是 0 - 6。咱们把 tableView 向上滑动,那么 tag 为 0 的 Cell 将会移动到 tag 为 6 的 Cell 下面。重新设置属性。为 1 的 Cell 会移动到 0 的 Cell 下面。。。 这就是 “可重用”。 但是有的同学会问了:经常会有 cell 重叠的情况发生, 这个很容易理解,因为 Cell 滑出界面并被放入重用队列时, cell 中的内容不会消失,可以通过下面代码解决。

- (void)layoutSubviews {
[super layoutSubviews];
/*这里面对属性的值进行更改*/
}

2.静态库的原理是什么?你有没有自己写过静态编译库,遇到了哪些问题?

  • 库本质上讲是一种可执行的二进制格式,可以载入内存中执行。是程序代码的集合,共享代码的一种方式。
  • 静态库是闭源库,不公开源代码,都是编译后的二进制文件,不暴露具体实现。
  • 静态库 一般都是以 .a 或者 .framework 形式存在。
  • 静态库编译的文件比较大,因为整个函数库的数据都会被整合到代码中,这样的好处就是编译后的程序不需要外部的函数库支持,不好的一点就是如果改变静态函数库,就需要程序重新编译。多次使用就有多份冗余拷贝。
  • 使用静态库的好处:模块化分工合作、可重用、避免少量改动导致大量的重复编译链接。
  • 一般公司都会有核心开发团队和普通开发团队,然后公司的核心业务由核心开发团队写成静态库然后让普通开发团队调用,这样就算普通开发团队离职也带不走公司的核心业务代码。一般核心开发团队是不会离职的。
  • 有静态库自然就有动态库了。这里所谓的静态和动态是相对编译期和运行期的。静态库在程序编译时会被链接到代码中,程序运行时将不再需要改静态库,而动态库在编译时不会被链接到代码中,只有程序运行时才会被载入,所以 hook 别人程序或者说做插件都是运用了 runtime 机制,然后动态库注入修改的。
  • 制作 .a 文件时候,要注意 CPU 架构的支持,i386、X86_64、 armv7、armv7s。 查看可以通过命令 “ lipo -info 静态库名称” 。模拟器 .a 文件和真机 .a 文件合并可以通过 "lipo -create 模拟器静态库1名 真机静态库2名 -output 新静态库名称"
一些坑
  • 命名不要太随意,毕竟是被别人拿过去用的要能看懂。
  • framework中用到了NSClassFromString,但是转换出来的class 一直为nil。解决方法:在主工程的【Other Linker Flags】需要添加参数【-ObjC]即可。
  • 如果Xcode找不到框架的头文件,你可能是忘记将它们声明为public了。 解决方法:进入target的Build Phases页,展开Copy Headers项,把需要public的头文件从Project或Private部分拖拽到Public部分。
  • 尽量不要用 xib 。由于静态框架采用静态链接,linker会剔除所有它认为无用的代码。不幸的是,linker不会检查xib文件,因此如果类是在xib中引用,而没有在O-C代码中引用,linker将从最终的可执行文件中删除类。这是linker的问题,不是框架的问题(当你编译一个静态库时也会发生这个问题)。苹果内置框架不会发生这个问题,因为他们是运行时动态加载的,存在于iOS设备固件中的动态库是不可能被删除的。 有两个解决的办法:

1、 让框架的最终用户关闭linker的优化选项,通过在他们的项目的Other Linker Flags中添加-ObjC和-all_load。

2、 在框架的另一个类中加一个该类的代码引用。例如,假设你有个MyTextField类,被linker剔除了。假设你还有一个MyViewController,它在xib中使用了MyTextField,MyViewController并没有被剔除。你应该这样做:
在MyTextField中: +(void)forceLinkerLoad_ {} 在MyViewController中: +(void)initialize { [MyTextField forceLinkerLoad]; } 他们仍然需要添加-ObjC到linker设置,但不需要强制allload了。 第2种方法需要你多做一点工作,但却让最终用户避免在使用你的框架时关闭linker优化(关闭linker优化会导致object文件膨胀)。

3.谈谈你对HTTP 、TCP、 IP、socket 协议的理解。

HTTP

  • 超文本传输协议(HTTP,HyperText Transfer Protocol) HTTP 协议对应应用层,HTTP 协议是基于 TCP 连接的。HTTP 链接就是所谓的短连接,即客户端向服务器发送一次请求,服务器端响应后连接即会断掉。
  • HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。 HTTP 1.1则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。
  • 因为 HTTP 是“短连接”,所以要保持客户端程序的在线状态,需要不断的向服务器发起连接请求。一般的做法就是不需要获取任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表示知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

TPC/IP

  • TPC/IP 协议是传输层协议,主要解决数据如何在网络中传输。“IP”代表网际协议,TCP和UDP使用该协议从一个网络传送数据包到另一个网络。把IP想像成一种高速公路,它允许其它协议在上面行驶并找到到其它电脑的出口。TCP和UDP是高速公路上的“卡车”,它们携带的货物就是像HTTP,文件传输协议FTP这样的协议等。 你应该能理解,TCP和UDP是FTP,HTTP和SMTP之类使用的传输层协议。虽然TCP和UDP都是用来传输其他协议的,它们却有一个显著的不同:TCP提供有保证的数据传输,而UDP不提供。这意味着TCP有一个特殊的机制来确保数据安全的不出错的从一个端点传到另一个端点,而UDP不提供任何这样的保证。 下面的图表试显示不同的TCP/IP和其他的协议在最初OSI模型中的位置: TCP:IP和其他的协议在OSI模型中的位置.jpg

  • TCP 标志位,有以下6种标示 1、SYN(synchronous建立联机)
    2、ACK(acknowledgement 确认)
    3、PSH(push传送)
    4、FIN(finish结束)
    5、RST(reset重置)
    6、URG(urgent紧急)
    Sequence number(顺序号码)
    Acknowledge number(确认号码)

  • 客户端 TCP 状态迁移: CLOSED->SYNSENT->ESTABLISHED->FINWAIT1->FINWAIT2->TIMEWAIT->CLOSED

  • 服务器TCP状态迁移: CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSEWAIT->LASTACK->CLOSED

  • 各个状态的意义如下: 1、LISTEN - 侦听来自远方TCP端口的连接请求;
    2、SYN-SENT -在发送连接请求后等待匹配的连接请求;
    3、SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;
    4、ESTABLISHED- 代表一个打开的连接,数据可以传送给用户;
    5、FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;
    6、FIN-WAIT-2 - 从远程TCP等待连接中断请求;
    7、CLOSE-WAIT - 等待从本地用户发来的连接中断请求;
    8、CLOSING -等待远程TCP对连接中断的确认;
    9、LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;
    10、TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认;
    11、CLOSED - 没有任何连接状态;

  • TCP/IP 三次握手 1、第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYNSEND状态,等待服务器B确认。
    2、第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN
    RECV状态。
    3、第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。 完成后,客户端和服务器开始传送数据。
    4、由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

CP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。

(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。 (2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。 (3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。 (4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。 - 深入理解TCP连接的释放: 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。 TCP协议的连接是全双工连接,一个TCP连接存在双向的读写通道。
简单说来是 “先关读,后关写”,一共需要四个阶段。以客户机发起关闭连接为例: 1.服务器读通道关闭
2.客户机写通道关闭
3.客户机读通道关闭
4.服务器写通道关闭
关闭行为是在发起方数据发送完毕之后,给对方发出一个FIN(finish)数据段。直到接收到对方发送的FIN,且对方收到了接收确认ACK之后,双方的数据通信完全结束,过程中每次接收都需要返回确认数据段ACK。 详细过程: 第一阶段 客户机发送完数据之后,向服务器发送一个FIN数据段,序列号为i; 1.服务器收到FIN(i)后,返回确认段ACK,序列号为i+1,关闭服务器读通道;
2.客户机收到ACK(i+1)后,关闭客户机写通道;
(此时,客户机仍能通过读通道读取服务器的数据,服务器仍能通过写通道写数据) 第二阶段 服务器发送完数据之后,向客户机发送一个FIN数据段,序列号为j; 3.客户机收到FIN(j)后,返回确认段ACK,序列号为j+1,关闭客户机读通道;
4.服务器收到ACK(j+1)后,关闭服务器写通道。
这是标准的TCP关闭两个阶段,服务器和客户机都可以发起关闭,完全对称。 FIN标识是通过发送最后一块数据时设置的,标准的例子中,服务器还在发送数据,所以要等到发送完的时候,设置FIN(此时可称为TCP连接处于半关闭状态,因为数据仍可从被动关闭一方向主动关闭方传送)。如果在服务器收到FIN(i)时,已经没有数据需要发送,可以在返回ACK(i+1)的时候就设置FIN(j)标识,这样就相当于可以合并第二步和第三步。

  • TCP的 TIMEWAIT 和 CLOSEWAIT 状态 CLOSEWAIT:
    发起TCP连接关闭的一方称为client,被动关闭的一方称为server。被动关闭端未发出FIN的TCP状态是CLOASE
    WAIT。出现这种状况一般都是由于server端代码的问题,如果你的服务器上出现大量CLOSEWAIT,应该要考虑检查代码。 TIMEWAIT:
    根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIMEWAIT状态。TIMEWAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒。TIMEWAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIMEWAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务。

socket

  • socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。所以当一个socket连接中没有数据的传输,那么为了维持连接需要发送心跳消息~~具体心跳消息格式是开发者自己定义的。

  • 套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

  • 我们平时说的最多的socket是什么呢,实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。 实际上,Socket跟TCP/IP协议没有必然的联系。Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现 只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、 listen、connect、accept、send、read和write等等。网络有一段关于socket和TCP/IP协议关系的说法比较容易理解: “TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。” 实际上,传输层的TCP是基于网络层的IP协议的,而应用层的HTTP协议又是基于传输层的TCP协议的,而Socket本身不算是协议,就像上面所说,它只是提供了一个针对TCP或者UDP编程的接口。socket是对端口通信开发的工具,它要更底层一些.

  • Socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等。而TCP和UDP协议属于传输层 。 而http是个应用层的协议,它实际上也建立在TCP协议之上(HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力)。

Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口。

  • 短连接: 连接->传输数据->关闭连接 HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
    也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。

  • 长连接: 连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。 长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。

  • http 的长连接: HTTP也可以建立长连接的,使用Connection:keep-alive,HTTP 1.1默认进行持久连接。HTTP1.1和HTTP1.0相比较而言,最大的区别就是增加了持久连接支持(貌似最新的 http1.0 可以显示的指定 keep-alive),但还是无状态的,或者说是不可以信任的。

  • 长连接和短连接的使用环境: 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。 而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。 总之,长连接和短连接的选择要视情况而定。

  • 发送接收方式: 1、异步
    报文发送和接收是分开的,相互独立的,互不影响。这种方式又分两种情况: (1)异步双工:接收和发送在同一个程序中,由两个不同的子进程分别负责发送和接收 (2)异步单工:接收和发送是用两个不同的程序来完成。 2、同步
    报文发送和接收是同步进行,既报文发送后等待接收返回报文。 同步方式一般需要考虑超时问题,即报文发出去后不能无限等待,需要设定超时时间,超过该时间发送方不再等待读返回报文,直接通知超时返回。 在长连接中一般是没有条件能够判断读写什么时候结束,所以必须要加长度报文头。读函数先是读取报文头的长度,再根据这个长度去读相应长度的报文。

4.谈谈你对iOS中沙盒机制的理解。

  • iOS 的沙盒机制(sandbox) 每个iOS应用都有自己的应用沙盒,应用沙盒就是文件系统目录。 1.每个应用程序的活动范围都限定在自己的沙盒里
    2.不能随意跨越自己的沙盒去访问别的应用程序沙盒中的内容
    (iOS8已经部分开放访问extension) 3.在访问别人沙盒内的数据时需要访问权限。

  • 沙盒的组成: 1、Documents:
    Documents中一般保存应用程序本身产生文件数据,例如游戏进度,绘图软件的绘图等, iTunes备份和恢复的时候,会包括此目录。
    注意:在此目录下不要保存从网络上下载的文件,否则app无法上架!(曾试过 plist 存小文件扔进去 上架通过) 2、Library:
    (1)、Caches: 此目录用来保存应用程序运行时生成的需要持久化的数据,这些数据一般存储体积比较大,又不是十分重要,比如网络请求数据等。这些数据需要用户负责删除。iTunes同步设备时不会备份该目录。

(2)、Preferences: 此目录保存应用程序的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录 在Preferences/下不能直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好.

3、tmp:
此目录保存应用程序运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录

  • 手机越狱后跟沙盒的关系 不同的越狱方案对沙盒会有所不同的影响,iOS 越狱不代表沙盒(sandbox)的移除,但通常都会使其变得更“虛弱”。

越狱之后,原本的一些私密文件,比如短信资料(/var/mobile/Library/SMS/sms.db),AppStore安装的App仍旧没有访问权限。但是,越狱后App能获得应用文件夹(/var/mobile/Aplications/)的读写权限,就是说,我能够遍历找到你的支付宝、招行、QQ、微信的App里面的用户资料,这仍旧会导致很大的安全问题。即使在AppStore上线的App,也能轻易根据当前是否越狱来作出不同的动作,Apple审核检查不到这一点。

  • 越狱本身的方法就是透过iOS现有的漏洞进行越狱,代表越狱是有风险性的情况发生,不过不可就认为越狱不安全,在越狱后iOS沙盒并没有被破坏,使用者虽然获取了root权限,并不表示就可让App都能够拥有root权限,可自动将指令写入至系统资料夹中,不过越狱后,在iOS上发现的漏洞或BUG,越狱界都会有开发者在第一时间内发布修补漏洞,先前就有不少案例。

在越狱后我们能够享有各种不同的插件,可让iOS设备实现不同功能,这些插件我们都会透过Cydia一些公认安全的插件源(如Bigboss、ModMyi)进行下载安装,这些软体源就像是AppStore会有审核机制、会员注册与付费购买机制,在上面所安装的插件都有一定的高信任与安全,其中比较好用的插件也都必须付费才能够使用,毕竟这些都是由不同开发者所开发出来的产品,如开发者没有收入来源,为何还要继续维护与更新下去呢?有些插件会由作者自己架设插件源免费提供分享的也有,不过也延伸出了有些越狱用户贪图免费,透过底下几种方式来安装,导致造成插件功能不正常、Cydia不稳定或是造成出错问题发生。

QQ技术交流群:214541576

微信公众号:shavekevin

开发者头条:

热爱生活,分享快乐。好记性不如烂笔头。多写,多记,多实践,多思考。                                         

comments powered by Disqus