米可世界
1.长链的事件分发?例如收到礼物,直播间和个人中心模块都需要知道,数据可能很大,通知不合适
protocol buffer?
2.动画用的什么?
3.长连接
4.TCP和UDP的区别?
5.HTTP 为什么要三次握手、四次挥手?
6.HTTPS 的流程?确认加密算法是在哪一步?
7.class 和 struct 区别
8.runtime,+load和+initialize方法的执行顺序
9.automic和nonatomic,automic是否是线程安全?
10.Runloop,和对象释放的关系,Runloop在切换mode时会退出吗?
11.SDK体积优化?脚本用的什么?静态分析发现了哪些问题?
12.你觉得值得说的负责的项目
参考答案
1.长链的事件分发?例如收到礼物,直播间和个人中心模块都需要知道,数据可能很大,通知不合适
长链的事件分发,特别是在处理像收到礼物这样的复杂事件时,需要考虑事件的多点通知、数据大小以及实时性等因素。以下是对这个问题的一些详细解答和建议:
一、事件分发机制概述
事件分发机制主要指的是在系统中,当某个事件发生时,如何高效、准确地将该事件的信息传递给所有需要知道的模块或组件。在长链事件分发场景下,这种机制尤为重要,因为它需要确保多个模块(如直播间和个人中心模块)能够实时、准确地接收到事件信息。
二、长链事件分发的具体实现
事件定义与捕获
- 事件定义:首先,需要明确定义哪些事件需要被捕获并分发。在这个例子中,收到礼物是一个关键事件。
- 事件捕获:当用户在直播间送出礼物时,系统需要能够捕获到这个行为,并生成相应的事件信息。
事件信息的封装
- 由于数据可能很大,因此在封装事件信息时,需要考虑数据的压缩和精简。例如,可以使用Protobuf协议来序列化数据,以减少数据大小并提高效率。
- 同时,也可以考虑只封装关键信息,如礼物类型、数量、送礼者信息等,以减少数据传输量。
事件分发的设计与实现
- 分发策略:对于长链事件,可以采用多种分发策略,如发布订阅模式、消息队列等。这些策略可以确保事件信息能够可靠地传递给所有需要的模块。
- 异步处理:为了避免因事件处理而导致的系统阻塞,可以采用异步处理的方式。即当事件发生时,将事件信息放入消息队列中,由专门的消费者线程或进程进行异步处理。
- 并发控制:在并发环境下,需要确保事件分发的正确性和一致性。可以使用锁、事务等机制来控制并发访问和数据一致性。
模块间的通信
- API调用:直播间和个人中心模块可以通过API调用来实现事件信息的接收和处理。这些API可以定义为RESTful接口或WebSocket等实时通信协议。
- 消息推送:对于需要实时性较高的场景,可以使用消息推送服务(如MQTT、WebSocket)来实时将事件信息推送给相关模块。
数据校验与错误处理
- 在事件分发过程中,需要对接收到的数据进行校验,以确保数据的完整性和正确性。
- 如果在分发过程中出现错误或异常情况,需要有相应的错误处理机制来记录日志、报警或进行重试等操作。
三、优化与改进
- 性能优化:通过优化事件捕获、封装、分发和处理的各个环节,来提高系统的整体性能。例如,使用高效的序列化协议、优化消息队列的处理逻辑等。
- 可扩展性设计:在设计事件分发机制时,需要考虑系统的可扩展性。例如,可以通过增加消费者线程或进程的数量、扩展消息队列的容量等方式来提高系统的处理能力。
- 安全性考虑:在事件分发过程中,需要确保数据的安全性和隐私保护。例如,使用加密技术来保护敏感数据的传输和存储安全。
综上所述,长链的事件分发是一个复杂的过程,需要综合考虑事件的定义与捕获、信息封装、分发策略、模块间通信、数据校验与错误处理等多个方面。通过合理的设计和优化,可以确保事件分发的准确性和实时性,同时提高系统的整体性能和可扩展性。
2.动画用的什么?
3.长连接
4.TCP和UDP的区别?
TCP(传输控制协议)和UDP(用户数据报协议)是互联网协议族中两个重要的传输层协议。它们在多个方面存在显著差异,以下是对这些区别的详细解析:
一、连接性
- TCP:TCP是一种面向连接的协议。在数据传输之前,发送方和接收方需要先进行三次握手,建立连接后才能开始传输数据。这种连接保证了数据的可靠性和顺序性。
- UDP:UDP则是一种无连接的协议。发送方在发送数据时不需要提前与接收方建立连接,直接发送数据包。这种方式简化了通信过程,但牺牲了数据的可靠性和顺序性。
二、可靠性
- TCP:TCP对数据的可靠性要求非常严格。它使用确认和重传机制来确保数据的完整性和正确性。如果接收方没有收到数据或数据损坏,发送方会重新发送数据,直到接收方确认收到为止。
- UDP:UDP对数据的可靠性要求较低。它不提供确认、重传和流量控制机制。如果数据在传输过程中丢失或损坏,UDP不会进行重传,接收方可能会收到不完整的数据。
三、速度和效率
- TCP:由于TCP需要建立连接和使用确认重传机制,因此其速度和效率相对较低。特别是在网络拥塞时,TCP的拥塞控制机制会限制发送速率,进一步降低传输速度。
- UDP:UDP没有连接建立和确认重传的开销,也不受拥塞控制机制的限制。因此,其数据传输速度通常比TCP更快,尤其适用于对实时性要求较高的应用。
四、数据包大小
- TCP:TCP将数据划分为较小的数据包(称为TCP段)进行传输。它没有固定的数据包大小限制,而是根据网络状况动态调整数据包大小。
- UDP:UDP则允许发送方一次性将多个数据包打包成一个较大的数据报(称为UDP数据报)进行传输。数据报的大小通常由应用层决定,这使得UDP在传输大量数据时更为高效。
五、适用场景
- TCP:适用于对数据可靠性要求较高的应用场景,如文件传输、电子邮件、网页浏览等。这些应用需要确保数据的完整性和正确性,因此选择TCP作为传输层协议更为合适。
- UDP:适用于对数据实时性要求较高的应用场景,如音频和视频流传输、网络游戏、实时通信等。这些应用对数据的完整性和正确性要求相对较低,但对传输速度有较高要求,因此选择UDP作为传输层协议更为合适。
综上所述,TCP和UDP在连接性、可靠性、速度和效率、数据包大小以及适用场景等方面存在显著差异。在实际应用中,应根据具体需求选择合适的传输层协议以确保数据的安全、稳定和高效传输。
5.HTTP 为什么要三次握手、四次挥手?
HTTP之所以在TCP连接建立时需要三次握手,以及TCP连接断开时需要四次挥手,主要是为了确保数据传输的可靠性、确保连接的建立和断开均能够按照既定协议进行。以下是详细的解释:
三次握手
目的:
- 确认双方的接收与发送能力:通过三次握手,客户端和服务器能够确认彼此的接收和发送功能都是正常的,从而确保数据能够双向、可靠地传输。
- 同步初始序列号:TCP连接中,每个传输的TCP段都会带有一个序列号,用于标识数据的顺序。三次握手过程中,双方会协商并同步各自的初始序列号,以便后续的数据传输能够按照正确的顺序进行。
- 防止失效的连接请求:三次握手可以防止由于网络延迟或信号丢失导致的失效连接请求被服务器误认为是新的连接请求,从而减少资源浪费和潜在的安全风险。
过程:
- 第一次握手:客户端发送一个带有SYN(同步)标志的数据包给服务器,表示自己希望建立连接,并附带一个初始序列号(ISN)。
- 第二次握手:服务器收到客户端的SYN包后,会回复一个带有SYN/ACK(同步/确认)标志的数据包给客户端,表示自己已经收到了客户端的连接请求,并且也附带了自己的初始序列号(ISN)以及确认号为客户端的序列号+1,以此确认客户端的发送能力。
- 第三次握手:客户端收到服务器的SYN/ACK包后,会发送一个带有ACK(确认)标志的数据包给服务器,表示自己已经收到了服务器的回复,确认号为服务器的序列号+1,从而完成连接的建立。
四次挥手
目的:
- 确保数据完整传输:TCP连接是全双工的,即数据可以同时在两个方向上传输。四次挥手确保了双方都能在完成数据传输后再关闭连接,防止数据丢失。
- 释放连接资源:通过明确的断开连接过程,双方可以及时释放占用的资源,避免无效连接占用系统资源。
过程:
- 第一次挥手:客户端发送一个带有FIN(结束)标志的数据包给服务器,表示自己已经发送完所有数据,希望关闭连接。此时客户端进入FIN-WAIT-1状态。
- 第二次挥手:服务器收到客户端的FIN包后,会回复一个带有ACK标志的数据包给客户端,表示自己已经收到了客户端的关闭请求,确认号为客户端的序列号+1。此时服务器进入CLOSE-WAIT状态,等待自己这一方也发送完所有数据后再关闭连接。
- 第三次挥手:服务器发送一个带有FIN标志的数据包给客户端,表示自己也已经发送完所有数据,希望关闭连接。此时服务器进入LAST-ACK状态,等待客户端的确认。
- 第四次挥手:客户端收到服务器的FIN包后,回复一个带有ACK标志的数据包给服务器,表示自己已经收到了服务器的关闭请求,确认号为服务器的序列号+1。此时客户端进入TIME-WAIT状态,等待足够的时间以确保服务器收到自己的ACK包后顺利关闭连接。最后,客户端也从TIME-WAIT状态转换到CLOSED状态,完成连接的断开。
综上所述,HTTP的三次握手和四次挥手是为了确保数据传输的可靠性和连接的正确建立与断开而设计的机制。这些机制在TCP/IP协议栈中起着至关重要的作用,保证了网络通信的稳定性和高效性。
6.HTTPS 的流程?确认加密算法是在哪一步?
7.class 和 struct 区别
1. 类型属性
- class:是引用类型,意味着在堆上分配内存,栈中保存的是其引用(即指向堆上对象的指针)。这允许class对象可以被多个变量引用,且修改一个对象的属性可能会影响到其他引用该对象的变量。
- struct:是值类型,它在栈上直接分配内存,存储的是其实际的值。因此,每次复制struct变量时,都会创建该变量值的副本,修改一个struct变量的值不会影响其他副本。
2. 成员访问权限
- class:默认情况下,class中的成员(成员变量和成员函数)的访问权限是私有的(private),这意味着它们只能在class内部被访问。不过,可以显式地指定成员的访问权限,如public、protected或private。
- struct:默认情况下,struct中的成员是公开的(public),这意味着它们可以在struct外部被访问。但同样地,也可以显式地指定成员的访问权限。
3. 构造函数和析构函数
- class:可以定义构造函数来初始化对象的状态,也可以定义析构函数来清理资源。构造函数可以是隐式的或显式的,但析构函数总是显式的。
- struct:虽然可以定义构造函数,但它们的构造函数有一些限制,如必须初始化所有成员变量。struct不支持定义析构函数。
4. 继承和多态
- class:支持继承和多态。一个class可以继承另一个class的成员(包括方法和属性),并通过重写基类方法来支持多态。
- struct:不支持继承(除了接口)和多态。在.NET等环境中,虽然struct可以实现接口,但它不能作为基类被其他类型继承
8.runtime,+load和+initialize方法的执行顺序
+load方法
- 调用时机:
+load
方法会在runtime加载类、分类时调用。具体来说,当应用程序启动时,Objective-C runtime会加载所有的类和分类实现,并在此时调用每个类和分类的+load
方法。- 需要注意的是,
+load
方法在程序运行过程中只会被调用一次,且其调用顺序遵循特定的规则。
- 调用顺序:
- 首先,调用类的
+load
方法,按照编译先后顺序调用(先编译的类,其+load
方法会先被调用)。 - 调用子类的
+load
之前,会先调用其父类的+load
方法。这是为了确保父类被正确加载和初始化后,子类再进行加载。 - 然后,调用分类的
+load
方法,也是按照编译先后顺序调用。但分类的+load
方法会在所有类的+load
方法之后调用。
- 首先,调用类的
- 调用方式:
+load
方法是根据方法地址直接调用的,不走objc_msgSend消息发送机制。这意味着+load
方法的调用非常高效,但也需要注意不要在其中进行耗时的操作,以免影响程序启动速度。
+initialize方法
- 调用时机:
+initialize
方法会在类第一次接收到消息(即第一次使用该类时,如创建实例或调用类方法)时调用。每个类只会初始化一次。
- 调用顺序:
- 首先,调用父类的
+initialize
方法(如果子类没有实现自己的+initialize
方法,则会调用父类的)。 - 然后,调用子类的
+initialize
方法。这确保了类的初始化过程是从基类开始,逐层向下进行的。
- 首先,调用父类的
- 调用方式:
- 与
+load
方法不同,+initialize
方法是通过objc_msgSend消息发送机制调用的。这意味着它的调用更加灵活,但也可能会受到消息传递机制的影响。
- 与
- 注意事项:
- 如果子类实现了自己的
+initialize
方法,那么父类的+initialize
方法只有在子类没有调用[super initialize]
时才会被跳过。但在实际开发中,通常不需要(也不建议)在子类的+initialize
方法中显式调用[super initialize]
,因为系统已经保证了这一过程的正确性。
- 如果子类实现了自己的
9.automic和nonatomic,automic是否是线程安全?
在Objective-C中,atomic
和nonatomic
是属性(property)声明时的两个关键字,它们用于定义属性的线程安全性。然而,关于atomic
是否是线程安全的问题,需要更细致地理解这两个关键字的含义以及线程安全的概念。
atomic
- 定义:
atomic
属性表示属性的setter和getter方法是线程安全的。这通常是通过在访问属性时添加锁来实现的,以确保在任意给定时刻只有一个线程可以访问属性的值。 - 线程安全性:虽然
atomic
为setter和getter方法提供了基本的线程保护,但它并不保证整个对象的线程安全。具体来说,atomic
只能确保在读取或写入属性值时的原子性,但如果操作涉及到多个步骤或需要访问对象的其他部分,那么atomic
并不能提供足够的保护。 - 性能影响:由于
atomic
属性需要加锁,因此它们通常比nonatomic
属性执行得更慢。在某些情况下,这种性能开销可能是不可接受的。 - 重要说明:需要注意的是,即使在iOS开发中,苹果官方也推荐使用
nonatomic
属性,除非有明确的理由需要线程安全(如使用原子操作来保证数据的一致性)。这是因为atomic
属性虽然提供了一定的线程安全性,但它们的性能成本较高,并且在许多情况下,开发者可以通过其他方式(如使用锁或同步机制)来确保线程安全,而不必依赖atomic
属性。
nonatomic
- 定义:
nonatomic
属性表示属性的setter和getter方法不是线程安全的。这意味着在多个线程同时访问同一属性时,可能会导致数据竞争和不确定的行为。 - 性能优势:由于
nonatomic
属性不需要加锁,因此它们通常比atomic
属性执行得更快。 - 使用场景:
nonatomic
属性非常适合在不需要线程安全的情况下使用,例如在单线程环境中或当开发者可以通过其他方式(如加锁)来确保线程安全时。
结论
因此,atomic
属性虽然在一定程度上提供了线程安全,但它并不是完全线程安全的,并且性能成本较高。在iOS开发中,建议根据实际需要选择使用atomic
或nonatomic
属性,并在必要时通过其他同步机制来确保线程安全。
综上所述,atomic属性并不等同于线程安全,而只是为setter和getter方法提供了一定的保护。在复杂的多线程环境中,开发者需要结合其他同步机制来确保整个对象或应用的线程安全。
10.Runloop,和对象释放的关系,Runloop在切换mode时会退出吗?
一、基本概念
- Runloop:
- Runloop是一个事件处理循环,用于监听并处理输入事件(如触摸事件、定时器事件等)和系统事件(如内存警告)。
- 每个线程都可以有自己的Runloop,但默认情况下,除了主线程外,其他线程的Runloop不会自动启动。
- 自动释放池(AutoreleasePool):
- 自动释放池是iOS中的一种内存自动回收机制,用于延迟释放加入到池中的对象。
- 当对象被添加到自动释放池中时,它并不会立即被释放,而是在自动释放池销毁时(通常是Runloop的休眠或结束时)发送
release
消息,从而释放对象。
二、Runloop与自动释放池的关系
- 自动释放池的创建与销毁:
- 在主线程中,每次Runloop启动时(如用户交互导致的事件循环开始),系统会自动创建一个新的自动释放池。
- 在Runloop休眠或结束前,旧的自动释放池会被销毁,同时创建新的自动释放池以准备下一轮的事件处理。
- 在子线程中,如果Runloop被手动启动,同样会在Runloop的循环中创建和销毁自动释放池。不过,如果子线程没有启动Runloop,则需要手动管理自动释放池(例如,在每个任务开始前创建,任务结束后销毁)。
- 对象释放的时机:
- 加入到自动释放池中的对象,其释放时机受Runloop控制。具体来说,当Runloop进入休眠状态前或结束运行时,会自动销毁当前的自动释放池,从而释放池中的所有对象。
- 这种机制有助于在事件处理过程中有效地管理内存,避免了因对象生命周期管理不当而导致的内存泄漏问题。
三、注意事项
- 在使用子线程时,如果需要处理大量数据或执行耗时操作,建议手动启动Runloop,并利用自动释放池来管理内存。这可以避免因长时间占用内存而导致的性能问题。
- 需要注意的是,虽然自动释放池可以简化内存管理任务,但在某些情况下(如循环引用)仍然需要开发者手动介入以释放对象。
综上所述,Runloop与自动释放池在iOS开发中紧密相关。Runloop通过控制自动释放池的创建与销毁来间接管理对象的释放时机,从而帮助开发者有效地管理内存资源。
Runloop在切换Mode时并不会直接退出,而是会先退出当前Mode的Runloop循环,并重新进入新的Mode下的Runloop循环。
11.SDK体积优化?脚本用的什么?静态分析发现了哪些问题?
Xcode静态分析能够发现的问题主要包括以下几个方面:
1. 逻辑缺陷
- 访问未初始化的变量:这是常见的逻辑错误之一,尝试访问一个未被赋予初始值的变量,可能导致不确定的结果。
- 空指针的解引用:解引用一个空指针(即指向空地址的指针)会导致程序崩溃。静态分析能够识别出这类潜在的解引用操作。
2. 内存管理缺陷
- 内存泄漏:分配的内存没有被及时释放,导致内存占用不断增加,最终可能影响程序的性能和稳定性。Xcode静态分析能够检测出这类内存管理错误。
- 对象所有权问题:对于使用ARC(自动引用计数)管理的对象,静态分析可以检查是否存在潜在的强引用循环等问题,这些问题可能导致对象无法被正确释放。
3. 无用存储缺陷
- 未使用的变量:定义了变量但在代码中从未使用过,这不仅浪费了内存资源,还可能是代码冗余或逻辑错误的表现。
4. API使用缺陷
- 不遵循库或框架的使用规则:在调用API时,如果未按照库或框架的规范进行操作(如传递了错误的参数类型、遗漏了必要的参数等),静态分析能够识别出这类问题。
5. 声明错误
- 类、方法或变量的声明错误:包括类型不匹配、拼写错误等,这些都可能导致编译错误或运行时错误。静态分析可以在不执行代码的情况下发现这些错误。
12.你觉得值得说的负责的项目