腾讯-iOS面试题-答案

一面

1、介绍一下实习的项目,任务分工,做了哪些工作?介绍实习内容

2、网络相关的:项目里面使用到什么网络库,用过ASIHTTP库吗

在iOS开发中,常用的网络库包括:

  1. URLSession:苹果官方提供的网络库,支持多种网络请求类型和数据格式,适用于大多数网络请求场景。

  2. Alamofire:一个基于 Swift 的网络库,提供了一种方便的方式来进行网络请求和数据解析。

  3. AFNetworking:一个基于 Objective-C 的网络库,是 Alamofire 的前身,提供了类似的功能。

  4. Moya:一个基于 Swift 的网络库,将网络请求和数据解析分离,提供了一种类型安全的方式来定义网络请求。

  5. Reachability:一个用于检测网络连接状态的库,可用于判断设备是否联网。

        ASIHTTP是一个基于Objective-C的网络库,它提供了一组方便的API来进行网络请求和数据处理。它支持各种网络请求类型和数据格式,包括GET、POST、PUT、DELETE等请求类型,以及JSON、XML、二进制数据等数据格式。ASIHTTP还提供了一些高级功能,如网络队列、缓存、断点续传等,可以帮助开发者更方便地管理网络请求和处理网络数据。

不过,需要注意的是,ASIHTTP已经停止维护了。虽然ASIHTTP在过去很受欢迎,但现在已经被许多新的网络库所替代,如前面提到的 URLSession、Alamofire、AFNetworking、Moya等,它们使用更现代的编程语言和技术,提供了更好的性能和更丰富的功能。因此,如果你正在开发一个新的iOS应用,建议使用这些更新的网络库,而不是ASIHTTP。

3、断点续传怎么实现?需要设置什么?

断点续传是指在网络传输过程中,如果传输中断,可以从中断的地方继续传输,而不是重新开始传输。在iOS开发中,可以通过以下几个步骤来实现断点续传:

  1. 使用HTTP协议的Range头部字段:HTTP协议提供了Range头部字段,可以用于指定请求的字节范围。通过在请求头部添加Range字段,可以告诉服务器从哪个字节开始传输数据。

  2. 使用本地缓存:在客户端,可以使用本地缓存来保存已经下载的部分数据。如果传输中断,可以通过读取本地缓存来确定从哪个字节开始继续下载。

  3. 处理网络中断:在网络传输过程中,如果发生了网络中断,需要保存已经下载的部分数据,并在下次继续传输时从保存的位置开始传输。

  4. 处理传输错误:在传输过程中,可能会发生一些错误,如服务器返回错误代码、网络连接超时等。需要对这些错误进行处理,并在下次继续传输时从保存的位置开始传输。

在实现断点续传时,需要设置HTTP请求的Range头部字段,以及使用本地缓存保存已经下载的数据。具体来说,可以使用NSURLSession或Alamofire等网络库来实现断点续传。在NSURLSession中,可以通过设置NSURLSessionConfiguration对象的HTTPAdditionalHeaders属性来添加Range字段,通过设置NSURLSessionDownloadTask对象的resumeData属性来保存已经下载的数据。在Alamofire中,可以使用AlamofireDownloadRequest来实现断点续传,它提供了resumeData参数用于保存已经下载的数据。

需要注意的是,不是所有的服务器都支持断点续传,因此需要在客户端和服务器之间进行协商和适配。另外,断点续传可能会增加服务器的负担,因此在使用时需要谨慎考虑。

4、在杭州HTTP请求服务器响应快,可能离服务器距离近,而在深圳访问就很慢很慢,会是什么原因?如果用户投诉,怎么分析这个问题?

可能存在以下几个原因导致在深圳访问服务器响应缓慢:

  1. 网络带宽不足:深圳作为一个发达城市,网络带宽通常比较充足,但在一些特定的情况下,可能会存在网络带宽不足的情况,例如网络拥堵、带宽限制等。

  2. 网络延迟高:网络延迟是指从发出请求到接收到响应所需要的时间,通常用ping值来表示。如果网络延迟高,会导致请求和响应的时间变长。

  3. 服务器负载过高:服务器负载过高,可能会导致服务器响应变慢或者请求超时。

如果用户投诉服务器响应缓慢,可以通过以下几个步骤来分析和定位问题:

  1. 确认是否是局部问题:如果只有少数用户反映服务器响应缓慢,可能是用户本身网络环境或设备问题导致的。可以让用户尝试在其他网络环境或设备上访问,以确定是否是局部问题。

  2. 测试网络带宽和延迟:可以使用网络诊断工具测试网络带宽和延迟,以确定是否存在带宽限制或网络延迟高的情况。

  3. 监测服务器负载:可以通过监测服务器的负载情况,确定是否存在服务器负载过高的情况。

  4. 分析服务器日志:可以分析服务器日志,查看服务器响应时间、请求量等信息,以确定服务器是否存在性能问题。

  5. 联系运营商或服务器提供商:如果无法确定问题所在,可以联系运营商或服务器提供商,寻求技术支持和帮助解决问题。

分析和解决服务器响应缓慢问题需要多方面的技术知识和经验,建议在具备相关技能和资源的情况下进行。

5、HTTP请求的哪些方法用过?什么时候选择get、post、put?

  1. GET方法:用于获取资源,对服务器的数据不会产生任何影响。GET方法的特点是请求参数在URL中,适用于获取数据的场景。

  2. POST方法:用于提交数据,对服务器的数据进行新增、修改等操作。POST方法的特点是请求参数在请求体中,适用于提交数据的场景。

  3. PUT方法:用于更新数据,对服务器的数据进行修改操作。PUT方法的特点是请求参数在请求体中,且要求对所有数据进行修改,适用于更新整个资源的场景。

选择HTTP请求方法的原则是:根据实际需求选择最合适的方法。通常,使用GET方法获取数据,使用POST方法提交数据,使用PUT方法更新数据。

但是,实际场景中可能会有其他考虑因素,例如安全性、效率等。例如,使用GET方法时,请求参数会暴露在URL中,可能存在安全隐患;使用POST方法时,请求体中的数据量较大时,可能会影响效率。因此,在选择HTTP请求方法时,需要根据实际需求综合考虑各种因素,选择最合适的方法。

6、TCP建立连接的过程,断开连接的过程,为什么是四次握手?

TCP是一种面向连接的协议,建立连接和断开连接的过程都需要经过握手来确保通信的可靠性。TCP建立连接的过程是通过三次握手来完成的,而断开连接则是通过四次握手来完成的。

TCP建立连接的过程如下:

  1. 客户端向服务器发送SYN报文,表示请求建立连接,并等待服务器的响应。
  2. 服务器收到客户端的SYN报文后,向客户端发送SYN+ACK报文,表示同意建立连接。
  3. 客户端收到服务器的SYN+ACK报文后,向服务器发送ACK报文,表示连接建立成功。

TCP断开连接的过程如下:

  1. 客户端发送FIN报文,表示要关闭连接,但仍可接收数据。
  2. 服务器收到客户端的FIN报文后,向客户端发送ACK报文,表示收到关闭请求。
  3. 服务器向客户端发送FIN报文,表示服务器已经准备好关闭连接。
  4. 客户端收到服务器的FIN报文后,向服务器发送ACK报文,表示收到关闭请求,连接断开。

为什么TCP断开连接需要四次握手呢?这是因为在TCP断开连接时,客户端和服务器都可能还有未发送的数据,因此需要在关闭连接前先发送完数据。四次握手的过程中,第一次是客户端发送FIN报文,第二次是服务器发送ACK报文确认收到请求,第三次是服务器发送FIN报文,第四次是客户端发送ACK报文确认收到请求和响应。这样,客户端和服务器都可以确定对方已经收到了所有数据,确保连接的可靠性。如果只有三次握手,那么在服务器发送FIN报文后,客户端可能会误认为服务器已经没有数据需要发送了,这可能会导致数据丢失或者连接无法正常关闭

7、项目里面的数据存储都用了哪些?知道iOS里面有哪些数据存储方法?什么时候该用哪些方法存储?

常见的数据存储方法包括:

  1. 文件存储:将数据保存在本地文件中,如JSON、XML、Plist等格式。

  2. 关系型数据库:使用SQL语言进行数据的增加、删除、修改和查询,常见的关系型数据库包括MySQL、Oracle、SQL Server等。

  3. 非关系型数据库:使用键值对存储数据,常见的非关系型数据库包括Redis、MongoDB、Cassandra等。

在iOS中,可以使用以下数据存储方法:

  1. User Defaults:用于存储少量的数据,如应用的设置、用户的偏好等。使用NSUserDefault类进行操作。

  2. SQLite:基于关系型数据库的一种轻量级数据库,适用于存储结构化数据。使用FMDB或SQLite.swift等第三方库进行操作。

  3. Core Data:一种对象关系映射技术,适用于存储大量的结构化数据。使用Core Data框架进行操作。

  4. 文件存储:可以直接将数据保存到本地文件中,如Plist、JSON或者XML等格式。

  5. Keychain:用于存储敏感数据,如用户名、密码等。使用Keychain Services API进行操作。

在选择数据存储方法时,应该根据具体的需求来选择合适的方法。如果数据量较小,可以使用User Defaults或者文件存储;如果数据量较大,可以使用SQLite或Core Data;如果需要存储敏感数据,可以使用Keychain。需要注意的是,不同的数据存储方法有不同的特点和适用场景,需要根据实际情况进行选择。

8、MVVM如何实现绑定

在iOS开发中,可以使用以下几种方式实现数据绑定:

  1. KVO(Key-Value Observing):KVO是一种观察者模式,可以用来观察对象的属性值的变化。当视图模型中的数据发生变化时,通过KVO通知视图进行更新。需要注意的是,KVO只能观察对象的属性值的变化,不能观察数据集合的变化。

  2. Notification:可以使用NSNotification通知机制来实现数据绑定。当视图模型中的数据发生变化时,通过NSNotification通知视图进行更新。

  3. RxSwift:RxSwift是一个函数式响应式编程框架,可以用来实现数据绑定。通过使用RxSwift中提供的Observable、Observer等组件,可以实现视图与视图模型之间的数据绑定。

  4. SwiftUI:SwiftUI是一种声明式UI框架,可以实现数据绑定。在SwiftUI中,可以使用@State、@Binding、@ObservedObject等属性包装器来实现视图与视图模型之间的数据绑定。

需要注意的是,无论使用哪种方式实现数据绑定,都需要遵循以下几个原则:

  1. 视图模型中的数据应该是可观察的,当数据发生变化时,应该能够通知到视图进行更新。

  2. 视图应该是被动的,不应该主动去访问视图模型中的数据。

  3. 视图模型应该是独立于视图的,不应该包含任何与视图相关的代码,从而实现了视图和数据的解耦。

9、block和通知的区别,分别适用什么场景

Block是一种将代码块作为对象传递的机制,由于它的语法简洁、易于理解,同时又能够直接访问函数所在的作用域中的变量,因此在以下场景中比较适用:

  1. 回调函数的实现:当需要在一个函数执行完成后,通知调用者进行相应的操作时,可以使用Block作为回调函数的实现方式。

  2. 异步任务的实现:当需要在异步任务执行完成后,通知调用者进行相应的操作时,可以使用Block作为异步任务的回调函数。

  3. 闭包的实现:当需要将一段代码作为一个整体进行传递时,可以使用Block作为闭包的实现方式。

通知是一种基于消息传递的机制,它允许模块之间进行松耦合的通信。通知机制具有以下特点:

  1. 通知的发送者和接收者之间没有直接的联系,它们只是通过通知中心进行通信。

  2. 通知可以被多个接收者同时接收,从而实现了多个模块之间的松耦合。

  3. 通知可以携带数据,从而让接收者能够了解发送者的状态。

通知适用于以下场景:

  1. 多个模块之间进行松耦合的通信。

  2. 模块之间需要传递数据的场景。

  3. 模块之间需要进行广播的场景,如系统通知、网络状态变化等。

需要注意的是,由于通知机制是基于消息传递的,因此它的效率相对较低,而且由于通知的广播范围较大,容易引发不必要的性能问题。因此,在使用通知机制时,需要谨慎使用,避免出现不必要的问题。

10、算法。连续问了好几个,都是数组,层层递进的,但是我忘了,只记得最后是找出数组中重复的数字

方法一:哈希表

  1. 创建一个哈希表,遍历数组中的每个元素,如果该元素已经在哈希表中出现过,则说明它是重复的元素,直接返回即可。

  2. 如果遍历完整个数组都没有找到重复的元素,则说明数组中没有重复的元素。

方法二:快慢指针

  1. 定义两个指针slow和fast,初始值都指向数组的第一个元素。

  2. slow指针每次移动一步,fast指针每次移动两步。如果存在重复的元素,那么两个指针最终一定会相遇。

  3. 当两个指针相遇时,将slow指针重新指向数组的第一个元素,然后两个指针每次都移动一步,直到它们再次相遇。此时的相遇点就是重复的元素。

需要注意的是,以上两种方法的时间复杂度都是O(n),空间复杂度也都是O(n)。如果要求空间复杂度为O(1),可以使用数组本身来实现,具体做法是将每个元素放到它应该在的位置上,如果发现某个位置上已经有了相同的元素,则说明存在重复的元素。

11、进程和线程的区别

  1. 资源分配:进程是操作系统中进行资源分配的基本单位,每个进程拥有独立的地址空间、文件描述符、网络连接等资源。而线程是进程中的执行单元,多个线程共享进程的地址空间和其他资源,因此线程的创建、销毁和切换的开销通常比进程小。

  2. 并发性:由于每个进程都有独立的资源,因此进程之间的并发性相对较低,进程之间的通信和同步需要复杂的机制来保证正确性。而线程之间共享进程的资源,因此线程之间的同步和通信相对较容易实现,同时也可以充分利用多核CPU的优势,提高程序的并发性。

  3. 调度:操作系统负责为进程分配CPU时间片,并进行进程的调度。而线程的调度则是在进程内部进行的,由进程的线程调度器负责调度。

  4. 安全性:由于每个进程拥有独立的地址空间和其他资源,因此进程之间的安全性相对较高,受到攻击的风险较小。而线程之间共享进程的资源,因此线程之间可能存在数据共享和竞争的问题,需要采取相应的措施来保证线程的安全性。

总的来说,进程和线程都是操作系统中实现并发的重要手段,各自有其优缺点和适用场景。在编写程序时,需要根据具体的需求选择合适的并发模型来实现程序的并发性。

12、程序在运行时操作系统除了分配内存空间还有什么

  1. 分配CPU时间片:操作系统负责为程序分配CPU时间片,以便程序能够在CPU上执行。

  2. 进程调度:如果多个程序同时在运行,操作系统需要进行进程调度,以便为每个程序分配适当的CPU时间片。进程调度算法的目标是在保证公平性的同时,尽可能提高系统的吞吐量和响应速度。

  3. 系统调用:程序运行时需要访问操作系统提供的各种服务和资源,例如文件、网络、设备等。为了方便程序访问这些资源,操作系统提供了系统调用接口,程序可以通过调用系统调用接口来请求操作系统提供相应的服务。

  4. 进程间通信:多个程序之间可能需要进行通信和数据共享,为了实现这些功能,操作系统提供了多种进程间通信机制,例如管道、消息队列、共享内存等。

  5. 安全保护:操作系统负责保护程序和系统的安全,例如防止程序访问未授权的资源、保护系统文件和配置等。

  6. 错误处理:如果程序发生错误或异常,操作系统会负责捕获和处理这些错误,例如向用户显示错误信息、记录日志等。

总的来说,操作系统在程序运行时扮演了重要的角色,负责提供各种服务和保障程序的安全和稳定性。

13、进程间通信的方式

  1. 管道(Pipe):管道是一种半双工的通信机制,允许在两个进程之间传递数据。管道通常是由一个进程创建,然后该进程可以通过管道向另一个进程发送数据。管道可以是匿名的,也可以是有名字的,有名管道通常用于不相关的进程之间的通信。

  2. 消息队列(Message Queue):消息队列是一种异步通信机制,允许多个进程之间发送和接收消息。消息队列通常具有优先级和持久化功能,可以在进程之间传递多种类型的消息。

  3. 共享内存(Shared Memory):共享内存是一种高效的进程间通信方式,允许多个进程共享同一块物理内存。共享内存允许进程直接访问共享内存区域,因此比管道和消息队列更快速和灵活,但需要注意进程之间的同步和数据一致性问题。

  4. 信号量(Semaphore):信号量是一种用于进程同步和互斥的机制,允许多个进程对共享资源进行访问控制。信号量可以用于进程间同步、互斥、资源分配等问题,通常由操作系统提供支持。

  5. 套接字(Socket):套接字是一种网络通信机制,允许多个进程通过网络进行通信。套接字可以在同一主机上的进程之间进行通信,也可以在不同主机上的进程之间进行通信。套接字通常使用TCP或UDP协议进行通信。

  6. 文件(File):文件也可以用于进程间通信,例如通过共享文件进行数据交换。文件通常需要进行同步和锁定,以避免多个进程同时访问同一个文件时出现数据不一致的情况。

总的来说,不同的进程间通信方式各有优缺点,需要根据具体的应用场景选择合适的方式来实现进程间的数据交换和协作。

14、如何检测应用是否卡顿

  1. 监控应用程序的响应时间:可以使用专业的性能监控工具来监控应用程序的响应时间。这些工具可以记录应用程序的各种指标,例如请求响应时间、CPU和内存使用率等,并生成报告和警报,以便及时发现潜在的性能问题。

  2. 模拟负载测试:可以使用负载测试工具来模拟用户同时访问应用程序的情况,以便测试应用程序在高负载情况下的性能表现。负载测试可以帮助确定应用程序是否能够承受大量的并发请求,并发现潜在的性能瓶颈。

  3. 监控系统资源使用情况:可以监控应用程序所在的操作系统的资源使用情况,例如CPU、内存和磁盘等。如果应用程序的资源使用过高,可能会导致应用程序卡顿和响应变慢。

  4. 监控应用程序日志:可以监控应用程序的日志,以便发现潜在的性能问题和错误。应用程序的日志可以记录各种信息,例如请求处理时间、数据库查询时间等,可以帮助识别潜在的性能瓶颈和错误。

  5. 用户反馈:可以收集用户反馈,以了解用户是否遇到过应用程序卡顿等性能问题。用户反馈可以帮助识别应用程序的性能问题,并及时采取措施进行优化。

总的来说,检测应用程序是否卡顿需要综合考虑多个因素,包括性能指标、系统资源使用情况、应用程序日志和用户反馈等。及时发现和解决应用程序的性能问题可以提高用户体验和应用程序的可靠性。

15、发布出去的版本,怎么收集crash日志?不使用bugly等第三方平台或者这些第三方平台是怎么收集crash日志的?

  1. Android平台:在Android平台上,可以通过设置UncaughtExceptionHandler来捕获应用程序的异常信息。在应用程序发生Crash时,UncaughtExceptionHandler会将异常信息保存到本地文件中。开发人员可以在应用程序的启动阶段进行初始化,并将Crash日志上传到服务器或者发送到指定的邮箱。

  2. iOS平台:在iOS平台上,可以使用Xcode的Crash日志收集功能来捕获应用程序的Crash信息。开发人员可以在Xcode中打开Devices窗口,选择对应的设备和应用程序,然后查看Crash日志。同时,也可以将Crash日志从设备中导出,并上传到服务器或者发送到指定的邮箱。

  3. Windows平台:在Windows平台上,可以使用Windows的内置工具来收集应用程序的Crash日志。开发人员可以使用Windows的Event Viewer来查看应用程序的事件日志,并查找与应用程序Crash相关的信息。同时,也可以使用Windows的Debug Diagnostic Tool来生成Crash日志,并上传到服务器或者发送到指定的邮箱。

对于第三方平台如Bugly等,它们通常会在应用程序中集成SDK,并提供相应的API来捕获Crash日志。当应用程序发生Crash时,SDK会收集Crash信息,并上传到Bugly等平台的服务器。开发人员可以登录相应的平台,查看Crash日志和异常信息,以便及时发现和解决应用程序的问题。

总的来说,收集应用程序的Crash日志需要针对不同的平台选择合适的方法和工具,并将Crash日志上传到服务器或者发送到指定的邮箱,以便开发人员及时发现和解决应用程序的问题。

16、在block里面使用_property会造成循环引用吗?怎么解决?除了使用self->_property,可以使用valueforkey来访问吗 在block里面可以修改它的值吗setvalueforkey?可以修改它的值,可以用valueforkey来解决,显式的的使用self,block外先持有self的弱引用。

在block中使用@property可能会导致循环引用问题,因为block会捕获其所在的上下文,如果该上下文中包含对当前对象的强引用,则会导致循环引用。这种情况下,如果在block中访问该属性,就会导致循环引用的问题。

为了解决这个问题,可以使用弱引用来避免循环引用。可以在block外面使用弱引用持有当前对象,然后在block中使用弱引用来访问该属性。例如:

__weak typeof(self) weakSelf = self;
self.block = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    NSString *propertyValue = strongSelf.property;
};

在这个例子中,首先使用弱引用weakSelf来持有当前对象self,然后在block中使用强引用strongSelf来访问属性property,以避免循环引用的问题。

另外,也可以使用[self valueForKey:@"property"]来访问属性,但是这种方式可能会影响性能,因为它需要进行运行时的动态查找,而且无法进行编译时的类型检查。

在block中修改属性的值也可能会导致循环引用问题。如果需要在block中修改属性的值,同样需要使用弱引用来避免循环引用的问题。可以使用弱引用weakSelf来持有当前对象self,然后在block中使用弱引用来修改属性的值,例如:

__weak typeof(self) weakSelf = self;
self.block = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    strongSelf->_property = @"new value";
};

在这个例子中,首先使用弱引用weakSelf来持有当前对象self,然后在block中使用弱引用来修改属性_property的值。

总的来说,在block中使用@property可能会导致循环引用问题,需要使用弱引用来避免循环引用。可以使用弱引用来访问和修改属性的值,或者使用[self valueForKey:@"property"]来访问属性的值,但需要注意性能问题。

你可能感兴趣的:(iOS面试题总结,ios,swift,开发语言)