iOS 高级口试题--答案

手机游戏开发者 2024-10-1 17:39:39 79 0 来自 中国
目次

iOS 根本题

1. 分类和扩展有什么区别?可以分别用来做什么?分类有哪些范围性?分类的结构体内里有哪些成员?
2.讲一下atomic的实现机制;为什么不能包管绝对的线程安全(最好可以联合场景来说)?
3. 被weak修饰的对象在被开释的时间会发生什么?是怎样实现的?知道sideTable么?内里的结构可以画出来么?
4. 关联对象有什么应用,体系怎样管理关联对象?其被开释的时间须要手动将全部的关联对象的指针置空么?
5. KVO的底层实现?怎样取消体系默认的KVO并手动触发(给KVO的触发设定条件:改变的值符合某个条件时再触发KVO)?
6. Autoreleasepool所使用的数据结构是什么?AutoreleasePoolPage结构体相识么?
7. 讲一下对象,类对象,元类,跟元类结构体的构成以及他们是怎样相关联的?为什么对象方法没有生存的对象结构体里,而是生存在类对象的结构体里?
8. class_ro_t和class_rw_t的区别?
9. iOS中内省的几个方法?class方法和objc_getClass方法有什么区别?
10. 在运行时创建类的方法objc_allocateClassPair的方法名尾部为什么是pair(成对的意思)?
11. 一个int变量被__block修饰与否的区别?
12. 为什么在block外部使用__weak修饰的同时须要在内部使用__strong修饰?
13. RunLoop的作用是什么?它的内部工作机制相识么?(最好联合线程和内存管理来说)
14. 哪些场景可以触发离屏渲染?(知道多少说多少)
iOS实战题

15. AppDelegate怎样瘦身?
16. 反射是什么?可以举出几个应用场景么?(知道多少说多少)
17. 有哪些场景是NSOperation比GCD更容易实现的?(或是NSOperation优于GCD的几点,知道多少说多少)
18. App 启动优化战略?最好联合启动流程来说(main()函数的实行前后都分别说一下,知道多少说多少)
19. App 无痕埋点的思绪相识么?你以为理想的无痕埋点体系应该具备哪些特点?(知道多少说多少)
20. 你知道有哪些环境会导致app崩溃,分别可以用什么方法拦截并化解?(知道多少说多少)
21. 你知道有哪些环境会导致app卡顿,分别可以用什么方法来避免?(知道多少说多少)
网络题

22. App 网络层有哪些优化战略?
23. TCP为什么要三次握手,四次挥手?
24. 对称加密和非对称加密的区别?分别有哪些算法的实现?
25. HTTPS的握手流程?为什么密钥的转达须要使用非对称加密?双向认证相识么?
26. HTTPS是怎样实现验证身份和验证完备性的?
27. 怎样用Charles抓HTTPS的包?此中原理和流程是什么?
28. 什么是中心人攻击?怎样避免?
盘算机体系题

29. 相识编译的过程么?分为哪几个步调?
30. 静态链接相识么?静态库和动态库的区别?
31. 内存的几大地区,各自的职能分别是什么?
32. static和const有什么区别?
33. 相识内联函数么?
34. 什么时间会出现死锁?怎样避免?
35. 说一说你对线程安全的明白?
36. 枚举你知道的线程同步战略?
37. 有哪几种锁?各自的原理?它们之间的区别是什么?最好可以联合使用场景来说
数据结构&算法题

38. 链表和数组的区别是什么?插入和查询的时间复杂度分别是多少?
39. 哈希表是怎样实现的?怎样办理所在冲突?
40. 排序题:冒泡排序,选择排序,插入排序,快速排序(二路,三路)能写出那些?
41. 链表题:怎样检测链表中是否有环?怎样删除链表中即是某个值的全部节点?
42. 数组题:如安在有序数组中找出和即是给定值的两个元素?怎样合并两个有序的数组之后保持有序?
43. 二叉树题:怎样反转二叉树?怎样验证两个二叉树是完全相称的?
1.分类和扩展有什么区别?可以分别用来做什么?分类有哪些范围性?分类的结构体内里有哪些成员?


  • 分类重要用来为某个类添加方法,属性,协议(我一般用来为体系的类扩展方法大概把某个复杂的类的按照功能拆到差别的文件里)
  • 扩展重要用来为某个类原来没有的成员变量、属性、方法。注:方法只是声明(我一般用扩展来声明私有属性,大概把.h的只读属性重写成可读写的)
分类和扩展的区别:

  • 分类是在运行时把分类信息合并到类信息中,而扩展是在编译时,就把信息合并到类中的
  • 分类声明的属性,只会天生getter/setter方法的声明,不会自动天生成员变量和getter/setter方法的实现,而扩展会
  • 分类不可用为类添加实例变量,而扩展可以
  • 分类可以为类添加方法的实现,而扩展只能声明方法,而不能实现
分类的范围性:

  • 无法为类添加实例变量,但可通过关联对象举行实现,注:关联对象中内存管理没有weak,用时须要注意野指针的题目,可通过其他办法来实现,详细可参考iOS weak 关键字漫谈
  • 分类的方法若和类中本来的实现重名,会覆盖本来方法的实现,注:并不是真正的覆盖
  • 多个分类的方法重名,会调用末了编译的谁人分类的实现
分类的结构体里有哪些成员
struct category_t {    const char *name; //名字    classref_t cls; //类的引用    struct method_list_t *instanceMethods;//实例方法列表    struct method_list_t *classMethods;//类方法列表    struct protocol_list_t *protocols;//协议列表    struct property_list_t *instanceProperties;//实例属性列表    // 此属性不肯定真正的存在    struct property_list_t *_classProperties;//类属性列表};复制代码2.讲一下atomic的实现机制;为什么不能包管绝对的线程安全(最好可以联合场景来说)?


  • atomic的实现机制
    atomic是property的修饰词之一,体现是原子性的,使用方式为@property(atomic)int age;,此时编译器会自动天生getter/setter方法,终极会调用objc_getProperty和objc_setProperty方法来举行存取属性。若此时属性用atomic修饰的话,在这两个方法内部使用os_unfair_lock来举行加锁,来包管读写的原子性。锁都在PropertyLocks中生存着(在iOS平台会初始化8个,mac平台64个),在用之前,会把锁都初始化好,在须要用到时,用对象的所在加上成员变量的偏移量为key,去PropertyLocks中去取。因此存取时用的是同一个锁,以是atomic能包管属性的存取时是线程安全的。注:由于锁是有限的,不消对象,差别属性的读取用的也大概是同一个锁
  • atomic为什么不能包管绝对的线程安全?

    • atomic在getter/setter方法中加锁,仅包管了存取时的线程安全,假设我们的属性是@property(atomic)NSMutableArray *array;可变的容器时,无法包管对容器的修改是线程安全的
    • 在编译器自动生产的getter/setter方法,终极会调用objc_getProperty和objc_setProperty方法存取属性,在此方法内部包管了读写时的线程安全的,当我们重写getter/setter方法时,就只能依靠本身在getter/setter中包管线程安全

3. 被weak修饰的对象在被开释的时间会发生什么?是怎样实现的?知道sideTable么?内里的结构可以画出来么?


  • 被weak修饰的对象在被开释的时间会发生什么?
    被weak修饰的对象在被开释的时间,会把weak指针自动置位nil
  • weak是怎样实现的?
    runTime会把对weak修饰的对象放到一个全局的哈希表中,用weak修饰的对象的内存所在为key,weak指针为值,在对象举行烧毁时,用通过自身所在去哈希表中查找到全部指向此对象的weak指针,并把全部的weak指针置位nil
  • sideTable的结构
struct SideTable {    spinlock_t slock;//操作SideTable时用到的锁    RefcountMap refcnts;//引用计数器的值    weak_table_t weak_table;//存放weak指针的哈希表};复制代码4. 关联对象有什么应用,体系怎样管理关联对象?其被开释的时间须要手动将全部的关联对象的指针置空么?


  • 关联对象有什么应用? 一般用于在分类中给类添加实例变量
  • 体系怎样管理关联对象?
    起首体系中有一个全局AssociationsManager,内里有个AssociationsHashMap哈希表,哈希表中的key是对象的内存所在,value是ObjectAssociationMap,也是一个哈希表,此中key是我们设置关联对象所设置的key,value是ObjcAssociation,内里存放着关联对象设置的值和内存管理的战略。 已void objc_setAssociatedObject(id object, const void * key,id value, objc_AssociationPolicy policy)为例,起首会通过AssociationsManager获取AssociationsHashMap,然后以object的内存所在为key,从AssociationsHashMap中取出ObjectAssociationMap,若没有,则新创建一个ObjectAssociationMap,然后通过key获取旧值,以及通过key和policy天生新值ObjcAssociation(policy, new_value),把新值存放到ObjectAssociationMap中,若新值不为nil,而且内存管理战略为retain,则会对新值举行一次retain,若新值为nil,则会删除旧值,若旧值不为空而且内存管理的战略是retain,则对旧值举行一次release
  • 其被开释的时间须要手动将全部的关联对象的指针置空么? 注:对这个题目我的明白是:当对象被开释时,须要手动移除该对象所设置的关联对象吗? 不须要,由于在对象的dealloc中,若发现对象有关联对象时,会调用_object_remove_assocations方法来移除全部的关联对象,并根据内存战略,来判断是否须要对关联对象的值举行release
5. KVO的底层实现?怎样取消体系默认的KVO并手动触发(给KVO的触发设定条件:改变的值符合某个条件时再触发KVO)?


  • KVO的底层实现?

    • 当某个类的属性被观察时,体系会在运行时动态的创建一个该类的子类。而且把改对象的isa指向这个子类
    • 假设被观察的属性名是name,若父类里有setName:或这_setName:,那么在子类里重写这2个方法,若2个方法同时存在,则只会重写setName:一个(这里和KVCset时的搜刮顺序是一样的)
    • 若被观察的范例是NSString,那么重写的方法的实现会指向_NSSetObjectValueAndNotify这个函数,假如Bool范例,那么重写的方法的实现会指向_NSSetBoolValueAndNotify这个函数,这个函数里会调用willChangeValueForKey:和didChangevlueForKey:,而且会在这2个方法调用之间,调用父类set方法的实现
    • 体系会在willChangeValueForKey:对observe里的change[old]赋值,取值是用valueForKey:取值的,didChangevlueForKey:对observe里的change[new]赋值,然后调用observe的这个方法- (void)observeValueForKeyPathnullable NSString *)keyPath ofObjectnullable id)object changenullable NSDictionary<NSKeyValueChangeKey, id> *)change contextnullable void *)context;
    • 当使用KVC赋值的时间,在NSObject里的setValue:forKey:方法里,若父类不存在setName:或这_setName:这些方法,会调用_NSSetValueAndNotifyForKeyInIvar这个函数,这个函数里同样也会调用willChangeValueForKey:和didChangevlueForKey:,若存在则调用

  • 怎样取消体系默认的KVO并手动触发(给KVO的触发设定条件:改变的值符合某个条件时再触发KVO)?
    举例:取消Person类age属性的默认KVO,设置age大于18时,手动触发KVO
+ (BOOL)automaticallyNotifiesObserversForKeyNSString *)key {    if ([key isEqualToString"age"]) {        return NO;    }    return [super automaticallyNotifiesObserversForKey:key];}- (void)setAgeNSInteger)age {    if (age > 18 ) {        [self willChangeValueForKey"age"];        _age = age;        [self didChangeValueForKey"age"];    }else {        _age = age;    }}复制代码6. Autoreleasepool所使用的数据结构是什么?AutoreleasePoolPage结构体相识么?

Autoreleasepool是由多个AutoreleasePoolPage以双向链表的情势毗连起来的, Autoreleasepool的根本原理:在每个自动开释池创建的时间,会在当前的AutoreleasePoolPage中设置一个标记位,在此期间,当有对象调用autorelsease时,会把对象添加到AutoreleasePoolPage中,若当前页添加满了,会初始化一个新页,然后用双向量表链接起来,并把新初始化的这一页设置为hotPage,当自动开释池pop时,从最下面依次往上pop,调用每个对象的release方法,直到碰到标记位。 AutoreleasePoolPage结构如下
class AutoreleasePoolPage {    magic_t const magic;    id *next;//下一个存放autorelease对象的所在    pthread_t const thread; //AutoreleasePoolPage 所在的线程    AutoreleasePoolPage * const parent;//父节点    AutoreleasePoolPage *child;//子节点    uint32_t const depth;//深度,也可以明白为当前page在链表中的位置    uint32_t hiwat;}复制代码7. 讲一下对象,类对象,元类,跟元类结构体的构成以及他们是怎样相关联的?为什么对象方法没有生存的对象结构体里,而是生存在类对象的结构体里?


  • 讲一下对象,类对象,元类,跟元类结构体的构成以及他们是怎样相关联的?


    对象的结构体里存放着isa和成员变量,isa指向类对象。 类对象的isa指向元类,元类的isa指向NSObject的元类。 类对象和元类的结构体有isa、superclass、cache、bits,bits里存放着class_rw_t的指针。 放一张经典的图 1.jpg <figcaption></figcaption>
  • 为什么对象方法没有生存的对象结构体里,而是生存在类对象的结构体里?
    方法是每个对象相互可以共用的,假如每个对象都存储一份方法列表太浪费内存,由于对象的isa是指向类对象的,当调用的时间,直接去类对象中查找就行了。可以节流许多内存空间的
8. class_ro_t和class_rw_t的区别?

class_rw_t提供了运行时对类拓展的本领,而class_ro_t存储的大多是类在编译时就已经确定的信息。二者都存有类的方法、属性(成员变量)、协议等信息,不外存储它们的列表实现方式差别。简朴的说class_rw_t存储列表使用的二维数组,class_ro_t使用的一维数组。 class_ro_t存储于class_rw_t结构体中,是不可改变的。生存着类的在编译时就已经确定的信息。而运行时修改类的方法,属性,协议等都存储于class_rw_t中
9. iOS中内省的几个方法?class方法和objc_getClass方法有什么区别?


  • 什么是内省?
在盘算机科学中,内省是指盘算机步调在运行时(Run time)查抄对象(Object)范例的一种本领,通常也可以称作运行时范例查抄。 不应该将内省和反射肴杂。相对于内省,反射更进一步,是指盘算机步调在运行时(Run time)可以访问、检测和修改它本身状态或活动的一种本领。

  • iOS中内省的几个方法?

    • isMemberOfClass //对象是否是某个范例的对象
    • isKindOfClass //对象是否是某个范例或某个范例子类的对象
    • isSubclassOfClass //某个类对象是否是另一个范例的子类
    • isAncestorOfObject //某个类对象是否是另一个范例的父类
    • respondsToSelector //是否能相应某个方法
    • conformsToProtocol //是否遵照某个协议

  • class方法和object_getClass方法有什么区别?
    实例class方法就直接返回object_getClass(self),类class方法直接返回self,而object_getClass(类对象),则返回的是元类
10. 在运行时创建类的方法objc_allocateClassPair的方法名尾部为什么是pair(成对的意思)?

由于此方法会创建一个类对象以及元类,恰恰构成一队
Class objc_allocateClassPair(Class superclass, const char *name,                              size_t extraBytes){    ...省略了部门代码    //天生一个类对象    cls  = alloc_class_for_subclass(superclass, extraBytes);    //天生一个类对象元类对象    meta = alloc_class_for_subclass(superclass, extraBytes);    objc_initializeClassPair_internal(superclass, name, cls, meta);    return cls;}复制代码11. 一个int变量被__block修饰与否的区别?

int变量被__block修饰之后会被包装成一个对象,如__block int age会被包装成下面如许
struct __Block_byref_age_0 {  void *__isa;__Block_byref_age_0 *__forwarding; //指向本身 int __flags; int __size; int age;//包装的详细的值};// age = 20;会被编译成下面如许(age.__forwarding->age) = 20;复制代码12. 为什么在block外部使用__weak修饰的同时须要在内部使用__strong修饰?

用__weak修饰之后block不会对该对象举行retain,只是持有了weak指针,在block实行之前或实行的过程时,随时都有大概被开释,将weak指针置位nil,产生一些未知的错误。在内部用__strong修饰,会在block实行时,对该对象举行一次retain,包管在实行时若该指针不指向nil,则在实行过程中不会指向nil。但有大概在实行实行之前已经为nil了
13. RunLoop的作用是什么?它的内部工作机制相识么?(最好联合线程和内存管理来说)


  • 什么是RunLoop 一般来讲,一个线程一次只能实行一个使命,实行完成后线程就会退出。假如我们须要一个机制,让线程能随时处置处罚变乱但并不退出。这种模型通常被称作 Event Loop。 Event Loop 在许多体系和框架里都有实现,比如 Node.js 的变乱处置处罚,比如 Windows 步调的消息循环,再比如 OSX/iOS 里的 RunLoop。实现这种模型的关键点在于:怎样管理变乱/消息,怎样让线程在没有处置处罚消息时休眠以避免资源占用、在有消息到来时立刻被叫醒。
  • RunLoop的作用是什么?(由于水平有限,不是很明白作者的本意,我对标题的明白是,使用RunLoop可以做哪些变乱?)

    • 保持步调的连续运行,在iOS线程中,会在main方法给主线程创建一个RunLoop,包管主线程不被烧毁
    • 处置处罚APP中的各种变乱(如touch,timer,performSelector等)
    • 界面更新
    • 手势辨认
    • AutoreleasePool

      • 体系在主线程RunLoop注册了2个observer
      • 第一个observe监听即将进入RunLoop,调用_objc_autoreleasePoolPush()创建自动开释池
      • 第二个observe监听两个变乱,进入休眠之前和即将退出RunLoop
      • 在进入休眠之前的回调里,会先开释自动开释池,然后在创建一个自动开释池
      • 在即将退出的回调里,会开释自动开释池

    • 线程保活
    • 监测卡顿

  • RunLoop的内部逻辑(下图来自于YY大神的博客)
<figcaption></figcaption>
14. 哪些场景可以触发离屏渲染?(知道多少说多少)


  • 添加遮罩mask
  • 添加阴影shadow
  • 设置圆角而且设置masksToBounds为true
  • 设置allowsGroupOpacity为true而且layer.opacity小于1.0和有子layer大概配景不为空
  • 开启光栅化shouldRasterize=true
15.AppDelegate怎样瘦身?


  • AppDelegate为什么会那么痴肥? AppDelegate是一个项目的入口,负担了太多的功能,如初始化根控制器,管理应用的状态,管理推送,和其他APP交互,初始化第三方SDK,获取权限等等
  • 怎样瘦身 瘦身的方案有许多,比如说把某些方法放到swift扩展大概OC的分类中,抽取中心类,使用关照监听等等,不外我比力喜好的是使用命令操持模式举行瘦身。 命令模式是形貌对象被称作命令相称于是一个简朴的方法大概变乱。由于对象封装了触发自身所需的全部参数,因此命令的调用者不知道该命令做了什么以及相应者是谁 可以为APPdelegate的每一个职责定义一个命令,这个命令的名字有他们本身指定
protocol Command {    func execute()}struct InitializeThirdPartiesCommand: Command {    func execute() {        // 初始化第三方库    }}struct InitialViewControllerCommand: Command {    let keyWindow: UIWindow    func execute() {        // 设置根控制器        keyWindow.rootViewController = UIViewController()    }}struct RegisterToRemoteNotificationsCommand: Command {    func execute() {        // 注册长途推送    }}复制代码然后我们定义StartupCommandsBuilder来封装怎样创建命令的详细信息。APPdelegate调用这个builder去初始化命令并实行这些命令
// MARK: - Builderfinal class StartupCommandsBuilder {    private var window: UIWindow!    func setKeyWindow(_ window: UIWindow) -> StartupCommandsBuilder {        self.window = window        return self    }    func build() -> [Command] {        return [            InitializeThirdPartiesCommand(),            InitialViewControllerCommand(keyWindow: window),            RegisterToRemoteNotificationsCommand()        ]    }}// MARK: - App Delegate@UIApplicationMainclass AppDelegate: UIResponder, UIApplicationDelegate {    var window: UIWindow?    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {        StartupCommandsBuilder()            .setKeyWindow(window!)            .build()            .forEach { $0.execute() }        return true    }}复制代码假如APPdelegate须要添加新的职责,则可以创建新的命令,然后把命令添加到builder里去而无需去改变APPdelegate。而且使用命令模式有以下利益

  • 每个命令都有单一的职责
  • 无需更改APPdelegate就可以很容易的添加新的命令
  • 每个命令可以很容易的被单独测试
16. 反射是什么?可以举出几个应用场景么?(知道多少说多少)


  • 什么是反射
反射是指盘算机步调在运行时(runtime)可以访问、检测和修改它本身状态或活动的一种本领。用比喻来说,反射就是步调在运行的时间可以大概“观察”而且修改本身的活动。
在OC中,反射是指步调在运行时,获取和修改类的信息。 2. 应用场景

  • JSON与模型之间的相互转换
  • Method Swizzling
  • KVO的实现原理
  • 实现NSCoding的自动归档和自动解档
  • 探索体系某些类的详细实现
17. 有哪些场景是NSOperation比GCD更容易实现的?(或是NSOperation优于GCD的几点,知道多少说多少)


  • NSOperation可以设置依靠
  • NSOperation可以举行停息,继续等操作
  • NSOperation可以监测当前队列运行的状态
  • NSOperationQueue可以取消队列里的全部操作
  • NSOperationQueue很方便的设置最大并发数
18. App启动优化战略?最好联合启动流程来说(main()函数的实行前后都分别说一下,知道多少说多少)


  • iOS的启动流程

    • 根据 info.plist 里的设置加载闪屏,创建沙箱,对权限举行查抄等
    • 加载可实行文件
    • 加载动态链接库,举行 rebase 指针调解和 bind 符号绑定
    • Objc 运行时的初始处置处罚,包罗 Objc 相关类的注册、category 注册、selector 唯一性查抄等;
    • 初始化,包罗了实行 +load() 方法、attribute((constructor)) 修饰的函数的调用、创建 C++ 静态全局变量。
    • 实行 main 函数
    • Application 初始化,到 applicationDidFinishLaunchingWithOptions 实行完
    • 初始化帧渲染,到 viewDidAppear 实行完,用户可见可操作。

  • 启动优化

    • 淘汰动态库的加载
    • 去撤消无用的类和C++全局变量的数目
    • 只管让load方法中的内容放到首屏渲染之后再去实行,大概使用initialize更换
    • 去除在首屏展现之前非须要的功能
    • 查抄首屏展现之前主线程的耗时方法,将没须要的耗时方法滞后大概延长实行

19. App无痕埋点的思绪相识么?你以为理想的无痕埋点体系应该具备哪些特点?(知道多少说多少))

App无痕埋点的思绪是使用AOP来拦截用户的操作并举行标记记载然后举行上传
我以为理想的无痕埋点体系应该具备以下特点

  • 不侵入业务代码
  • 统计尽大概多的变乱
  • 自动天生唯一标识
  • 要能统计到控件在但差别状态意义差别的环境
  • 须要某些机制可以大概提供业务数据
  • 在合适的时机去上传数据
20. 你知道有哪些环境会导致app崩溃,分别可以用什么方法拦截并化解?(知道多少说多少))


  • unrecognized selector sent to instance 方法找不到
  • 数组越界,插入空值
  • [NSDictionary initWithObjects:forKeys:]使用此方法初始化字典时,objects和keys的数目不划一时
  • NSMutableDictionary,setObject:forKey:大概removeObjectForKey:时,key为nil
  • setValue:forUndefinedKey:,使用KVC对对象举行存取值时传入错误的key大概对不可变字典举行赋值
  • NSUserDefaults 存储时key为nil
  • 对字符串操作时,转达的下标超出范围,判断是否存在前缀,后缀子串时,子串为空
  • 使用C字符串初始化字符串时,传入null
  • 对可变聚集或字符串使用copy修饰并举行修改操作
  • 在空间未添加到父元素上之前,就使用autoLayout举行结构
  • KVO在对象烧毁时,没有移除KVO大概多次移除KVO
  • 野指针访问
  • 死锁
  • 除0
1-9都可以使用Runtime举行拦截,然后举行一些逻辑处置处罚,防止crash
21. 你知道有哪些环境会导致app卡顿,分别可以用什么方法来避免?(知道多少说多少))


  • 主线程中进化IO或其他耗时操作,办理:把耗时操作放到子线程中操作
  • GCD并发队列短时间内创建大量使命,办理:使用线程池
  • 文本盘算,办理:把盘算放在子线程中避免壅闭主线程
  • 大量图像的绘制,办理:在子线程中对图片举行解码之后再展示
  • 高清图片的展示,解法:可在子线程中举行下采样处置处罚之后再展示
22. App网络层有哪些优化战略?


  • 优化DNS解析和缓存
  • 对传输的数据举行压缩,淘汰传输的数据
  • 使用缓存本领淘汰哀求的发起次数
  • 使用战略来淘汰哀求的发起次数,比如在上一个哀求未着地之前,不举行新的哀求
  • 避免网络抖动,提供重发机制
23. TCP为什么要三次握手,四次挥手?

三次握手:

  • 客户端向服务端发起哀求链接,起首发送SYN报文,SYN=1,seq=x,而且客户端进入SYN_SENT状态
  • 服务端收到哀求链接,服务端向客户端举行复兴,并发送相应报文,SYN=1,seq=y,ACK=1,ack=x+1,而且服务端进入到SYN_RCVD状态
  • 客户端收到确认报文后,向服务端发送确认报文,ACK=1,ack=y+1,此时客户端进入到ESTABLISHED,服务端收到用户端发送过来简直认报文后,也进入到ESTABLISHED状态,此时链接创建乐成
四次挥手:

  • 客户端向服务端发起关闭链接,并制止发送数据
  • 服务端收到关闭链接的哀求时,向客户端发送回应,我知道了,然后制止吸收数据
  • 当服务端发送数据竣事之后,向客户端发起关闭链接,并制止发送数据
  • 客户端收到关闭链接的哀求时,向服务端发送回应,我知道了,然后制止吸收数据
为什么须要三次握手: 为了防止已失效的毗连哀求报文段突然又传送到了服务端,因而产生错误,假设这是一个早已失效的报文段。但server收到此失效的毗连哀求报文段后,就误以为是client再次发出的一个新的毗连哀求。于是就向client发出确认报文段,同意创建毗连。假设不接纳“三次握手”,那么只要server发出确认,新的毗连就创建了。由于现在client并没有发出创建毗连的哀求,因此不会答应server简直认,也不会向server发送数据。但server却以为新的运输毗连已经创建,并不停期待client发来数据。如许,server的许多资源就白白浪费掉了。
为什么须要四次挥手: 由于TCP是全双工通讯的,在吸收到客户端的关闭哀求时,还大概在向客户端发送着数据,因此不能再回应关闭链接的哀求时,同时发送关闭链接的哀求
24. 对称加密和非对称加密的区别?分别有哪些算法的实现?

对称加密,加密的加密息争密使用同一密钥。
非对称加密,使用一对密钥用于加密息争密,分别为公开密钥和私有密钥。公开密钥全部人都可以得到,通讯发送方得到吸收方的公开密钥之后,就可以使用公开密钥举行加密,吸收方收到通讯内容后使用私有密钥解密。
对称加密常用的算法实现有AES,ChaCha20,DES,不外DES被以为是不安全的;非对称加密用的算法实现有RSA,ECC
25. HTTPS的握手流程?为什么密钥的转达须要使用非对称加密?双向认证相识么?

HTTPS的握手流程,如下图,摘自图解HTTP
[图片上传失败...(image-782287-1671505185177)]
<figcaption></figcaption>

  • 客户端发送Client Hello 报文开始SSL通讯。报文中包罗客户端支持的SSL的版本,加密组件列表。
  • 服务器收到之后,会以Server Hello 报文作为应答。和客户端一样,报文中包罗客户端支持的SSL的版本,加密组件列表。服务器的加密组件内容是从吸收到的客户端加密组件内筛选出来的
  • 服务器发送Certificate报文。报文中包罗公开密钥证书。
  • 然后服务器发送Server Hello Done报文关照客户端,最初阶段的SSL握手协商部门竣事
  • SSL第一次握手竣事之后,客户端以Client Key Exchange报文作为会议。报文中包罗通讯加密中使用的一种被称为Pre-master secret的随秘暗码串
  • 接着客户端发送Change Cipher Space报文。该报文会提示服务器,在次报文之后的通讯会接纳Pre-master secret密钥加密
  • 客户端发送Finished 报文。该报文包罗链接至今全部报文的团体校验值。这次握手协商是否可以大概乐成,要以服务器是否可以大概精确揭秘该报文作为判断尺度
  • 服务器同样发送Change Cipher Space报文。
  • 服务器同样发送Finished报文。
  • 服务器和客户端的Finished报文交换完毕之后,SSL毗连创建完成,以后开始HTTP通讯,通讯的内容都使用Pre-master secret加密。然后开始发送HTTP哀求
  • 应用层收到HTTP哀求之后,发送HTTP相应
  • 末了有客户端断开毗连
为什么密钥的转达须要使用非对称加密?

答:使用非对称加密是为了后面客户端天生的Pre-master secret密钥的安全,通过上面的步调能得知,服务器向客户端发送公钥证书这一步是有大概被别人拦截的,假如使用对称加密的话,在客户端向服务端发送Pre-master secret密钥的时间,被黑客拦截的话,就可以大概使用公钥举行解码,就无法包管Pre-master secret密钥的安全了
双向认证相识么?(这里我真想说一句不相识)

答:上面的HTTPS的通讯流程只验证了服务端的身份,而服务端没有验证客户端的身份,双向认证是服务端也要确保客户端的身份,大概流程是客户端在校验完服务器的证书之后,会向服务器发送本身的公钥,然后服务端用公钥加密产生一个新的密钥,传给客户端,客户端再用私钥解密,以后就用此密钥举行对称加密的通讯
26. HTTPS是怎样实现验证身份和验证完备性的?

使用数字证书和CA来验证身份,起首服务端先向CA机构去申请证书,CA考核之后会给一个数字证书,内里包裹公钥、署名、有用期,用户信息等各种信息,在客户端发送哀求时,服务端会把数字证书发给客户端,然后客户端会通过信托链来验证数字证书是否是有用的,来验证服务端的身份。
使用择要算法来验证完备性,也就是说在发送消息时,会对消息的内容通过择要算法天生一段择要,在收到收到消息时也使用同样的算法天生择要,来判断择要是否划一。
27. 怎样用Charles抓HTTPS的包?此中原理和流程是什么?

流程:

  • 起首在手机上安装Charles证书
  • 在署理设置中开启Enable SSL Proxying
  • 之后添加须要抓取服务端的所在
原理:
Charles作为中心人,对客户端伪装成服务端,对服务端伪装成客户端。简朴来说:

  • 截获客户端的HTTPS哀求,伪装成中心人客户端行止服务端发送HTTPS哀求
  • 担当服务端返回,用本身的证书伪装成中心人服务端向客户端发送数据内容。
详细流程如下图,图片来自扯一扯HTTPS单向认证、双向认证、抓包原理、反抓包战略
<figcaption></figcaption>
28. 什么是中心人攻击?怎样避免?

中心人攻击就是截获到客户端的哀求以及服务器的相应,比如Charles抓取HTTPS的包就属于中心人攻击。
避免的方式:客户端可以预埋证书在当地,然后举行证书的比力是否是匹配的
29. 相识编译的过程么?分为哪几个步调?


  • 预编译:重要处置处罚以“#”开始的预编译指令。
  • 编译:

    • 词法分析:将字符序列分割成一系列的暗号。
    • 语法分析:根据产生的暗号举行语法分析天生语法树。
    • 语义分析:分析语法树的语义,举行范例的匹配、转换、标识等。
    • 中心代码天生:源码级优化器将语法树转换成中心代码,然后举行源码级优化,比如把 1+2 优化为 3。中心代码使得编译器被分为前端和后端,差别的平台可以使用差别的编译器后端将中心代码转换为呆板代码,实现跨平台。
    • 目的代码天生:以后的过程属于编译器后端,代码天生器将中心代码转换成目的代码(汇编代码),其后目的代码优化器对目的代码举行优化,比如调解寻址方式、使用位移代替乘法、删除多余指令、调解指令顺序等。

  • 汇编:汇编器将汇编代码变化成呆板指令。
  • 静态链接:链接器将各个已经编译成呆板指令的目的文件链接起来,颠末重定位过后输出一个可实行文件。
  • 装载:装载可实行文件、装载其依靠的共享对象。
  • 动态链接:动态链接器将可实行文件和共享对象中须要重定位的位置举行修正。
  • 末了,进程的控制权转交给步调入口,步调终于运行起来了。
30. 静态链接相识么?静态库和动态库的区别?

静态链接是指将多个目的文件合并为一个可实行文件,直观感觉就是将全部目的文件的段合并。须要注意的是可实行文件与目的文件的结构根本划一,差别的是是否“可实行”。 静态库:链接时完备地拷贝至可实行文件中,被多次使用就有多份冗余拷贝。 动态库:链接时不复制,步调运行时由体系动态加载到内存,供步调调用,体系只加载一次,多个步调共用,节流内存。
31. 内存的几大地区,各自的职能分别是什么?


  • 栈区:有体系自动分配并开释,一般存放函数的参数值,局部变量等
  • 堆区:有步调员分配和开释,若步调员未开释,则在步调竣事时有体系开释,在iOS里创建出来的对象会放在堆区
  • 数据段:字符串常量,全局变量,静态变量
  • 代码段:编译之后的代码
32. static和const有什么区别?

const是指声明一个常量 static修饰全局变量时,体现此全局变量只在当前文件可见 static修饰局部变量时,体现每次调用的初始值为上一次调用的值,调用竣过后存储空间不开释
33. 相识内联函数么?

内联函数是为了淘汰函数调用的开销,编译器在编译阶段把函数体内的代码复制到函数调用处
34. 什么时间会出现死锁?怎样避免?

死锁是指两个或两个以上的线程在实行过程中,由于竞争资源大概由于相互通讯而造成的一种壅闭的征象,若无外力作用,它们都将无法推进下去。 发存亡锁的四个须要条件:

  • 互斥条件:一个资源每次只能被一个线程使用。
  • 哀求与保持条件:一个线程因哀求资源而壅闭时,对已得到的资源保持不放。
  • 不剥夺条件:线程已得到的资源,在未使用完之前,不能强行剥夺。
  • 循环期待条件:多少线程之间形成一种头尾相接的循环期待资源关系。
只要上面四个条件有一个条件不被满足就能避免死锁
35. 说一说你对线程安全的明白?

在并发实行的环境中,对于共享数据通过同步机制包管各个线程都可以精确的实行,不会出现数据污染的环境,大概对于某个资源,在被多个线程访问时,不管运行时实行这些线程有什么样的顺序大概交错,不会出现错误的活动,就以为这个资源是线程安全的,一般来说,对于某个资源假如只有读操作,则这个资源无需同步就是线程安全的,如有多个线程举行读写操作,则须要线程同步来包管线程安全。
36. 枚举你知道的线程同步战略?


  • OSSpinLock 自旋锁,已不再安全,除了这个锁之外,下面写的锁,在期待时,都会进入线程休眠状态,而非忙等
  • os_unfair_lock atomic就是使用此锁来包管原子性的
  • pthread_mutex_t 互斥锁,而且支持递归实现和条件实现
  • NSLock,NSRecursiveLock,根本的互斥锁,NSRecursiveLock支持递归调用,都是对pthread_mutex_t的封装
  • NSCondition,NSConditionLock,条件锁,也都是对pthread_mutex_t的封装
  • dispatch_semaphore_t 信号量
  • @synchronized 也是pthread_mutex_t的封装
37. 有哪几种锁?各自的原理?它们之间的区别是什么?最好可以联合使用场景来说


  • 自旋锁:自旋锁在无法举行加锁时,会不绝的举行实验,一般用于临界区的实行时间较短的场景,不外iOS的自旋锁OSSpinLock不再安全,重要缘故因由发生在低优先级线程拿到锁时,高优先级线程进入忙等(busy-wait)状态,斲丧大量 CPU 时间,从而导致低优先级线程拿不到 CPU 时间,也就无法完成使命并开释锁。这种题目被称为优先级反转。
  • 互斥锁:对于某一资源同时只答应有一个访问,无论读写,平常使用的NSLock就属于互斥锁
  • 读写锁:对于某一资源同时只答应有一个写访问大概多个读访问,iOS中pthread_rwlock就是读写锁
  • 条件锁:在满足某个条件的时间举行加锁大概解锁,iOS中可使用NSConditionLock来实现
  • 递归锁:可以被一个线程多次得到,而不会引起死锁。它记载了乐成得到锁的次数,每一次乐成的得到锁,必须有一个配套的开释锁和其对应,如许才不会引起死锁。只有当全部的锁被开释之后,其他线程才可以得到锁,iOS可使用NSRecursiveLock来实现
38. 链表和数组的区别是什么?插入和查询的时间复杂度分别是多少?

链表和数组都是一个有序的聚集,数组须要连续的内存空间,而链表不须要,链表的插入删除的时间复杂度是O(1),数组是O(n),根据下标查询的时间复杂度数组是O(1),链表是O(n),根据值查询的时间复杂度,链表和数组都是O(n)
39. 哈希表是怎样实现的?怎样办理所在冲突?

哈希表是也是通过数组来实现的,起首对key值举行哈希化得到一个整数,然后对整数举行盘算,得到一个数组中的下标,然后举行存取,办理所在冲突常用方法有开放定址法和链表法。runtime源码的存放weak指针哈希表使用的就是开放定址法,Java里的HashMap使用的是链表法。
40. 排序题:冒泡排序,选择排序,插入排序,快速排序(二路,三路)能写出那些?

这里简朴的说下几种快速排序的差别之处,随机快排,是为相识决在近似有序的环境下,时间复杂度会退化为O(n2),双路快排是为相识决快速排序在大量数据重复的环境下,时间复杂度会退化为O(n2),三路快排是在大量数据重复的环境下,对双路快排的一种优化。

  • 冒泡排序
extension Array where Element : Comparable{    public mutating func bubbleSort() {        let count = self.count        for i in 0..<count {            for j in 0..<(count - 1 - i) {                if self[j] > self[j + 1] {                    (self[j], self[j + 1]) = (self[j + 1], self[j])                }            }        }    }}复制代码

  • 选择排序
extension Array where Element : Comparable{    public mutating func selectionSort() {        let count = self.count        for i in 0..<count {            var minIndex = I            for j in (i+1)..<count {                if self[j] < self[minIndex] {                    minIndex = j                }            }            (self, self[minIndex]) = (self[minIndex], self[I])        }    }}复制代码

  • 插入排序
extension Array where Element : Comparable{    public mutating func insertionSort() {        let count = self.count        guard count > 1 else { return }        for i in 1..<count {            var preIndex = i - 1            let currentValue = self[I]            while preIndex >= 0 && currentValue < self[preIndex] {                self[preIndex + 1] = self[preIndex]                preIndex -= 1            }            self[preIndex + 1] = currentValue        }    }}复制代码

  • 快速排序
extension Array where Element : Comparable{    public mutating func quickSort() {        func quickSort(left:Int, right:Int) {            guard left < right else { return }            var i = left + 1,j = left            let key = self[left]            while i <= right {                if self < key {                    j += 1                    (self, self[j]) = (self[j], self[I])                }                i += 1            }            (self[left], self[j]) = (self[j], self[left])            quickSort(left: j + 1, right: right)            quickSort(left: left, right: j - 1)        }        quickSort(left: 0, right: self.count - 1)    }}复制代码

  • 随机快排
extension Array where Element : Comparable{    public mutating func quickSort1() {        func quickSort(left:Int, right:Int) {            guard left < right else { return }            let randomIndex = Int.random(in: left...right)            (self[left], self[randomIndex]) = (self[randomIndex], self[left])            var i = left + 1,j = left            let key = self[left]            while i <= right {                if self < key {                    j += 1                    (self, self[j]) = (self[j], self[I])                }                i += 1            }            (self[left], self[j]) = (self[j], self[left])            quickSort(left: j + 1, right: right)            quickSort(left: left, right: j - 1)        }        quickSort(left: 0, right: self.count - 1)    }}复制代码

  • 双路快排
extension Array where Element : Comparable{    public mutating func quickSort2() {        func quickSort(left:Int, right:Int) {            guard left < right else { return }            let randomIndex = Int.random(in: left...right)            (self[left], self[randomIndex]) = (self[randomIndex], self[left])            var l = left + 1, r = right            let key = self[left]            while true {                while l <= r && self[l] < key {                    l += 1                }                while l < r && key < self[r]{                    r -= 1                }                if l > r { break }                (self[l], self[r]) = (self[r], self[l])                l += 1                r -= 1            }            (self[r], self[left]) = (self[left], self[r])            quickSort(left: r + 1, right: right)            quickSort(left: left, right: r - 1)        }        quickSort(left: 0, right: self.count - 1)    }}复制代码

  • 三路快排
// 三路快排extension Array where Element : Comparable{    public mutating func quickSort3() {        func quickSort(left:Int, right:Int) {            guard left < right else { return }            let randomIndex = Int.random(in: left...right)            (self[left], self[randomIndex]) = (self[randomIndex], self[left])            var lt = left, gt = right            var i = left + 1            let key = self[left]            while i <= gt {                if self == key {                    i += 1                }else if self < key{                    (self, self[lt + 1]) = (self[lt + 1], self[I])                    lt += 1                    i += 1                }else {                    (self, self[gt]) = (self[gt], self[I])                    gt -= 1                }            }            (self[left], self[lt]) = (self[lt], self[left])            quickSort(left: gt + 1, right: right)            quickSort(left: left, right: lt - 1)        }        quickSort(left: 0, right: self.count - 1)    }}复制代码41. 链表题:怎样检测链表中是否有环?怎样删除链表中即是某个值的全部节点?


  • 怎样检测链表中是否有环?
public class ListNode {    public var val: Int    public var next: ListNode?    public init(_ val: Int) {        self.val = val        self.next = nil    }}extension ListNode {    var hasCycle: Bool {        var slowistNode? = self        var fast = self.next        while fast != nil {            if slow! === fast! {                return true            }            slow = slow?.next            fast = fast?.next?.next        }        return false    }}复制代码

  • 怎样删除链表中即是某个值的全部节点?
func remove(with value:Int, from listNodeistNode?) -> ListNode? {    let tmpNode = ListNode(0)    tmpNode.next = listNode    var currentNode = tmpNode.next    var persiousNodeistNode? = tmpNode    while currentNode != nil {        if let nodeValue = currentNode?.val, nodeValue == value {            persiousNode?.next = currentNode?.next        }else {            persiousNode = currentNode        }        currentNode = currentNode?.next    }    return tmpNode.next}复制代码42. 数组题:如安在有序数组中找出和即是给定值的两个元素?怎样合并两个有序的数组之后保持有序?


  • 如安在有序数组中找出和即是给定值的两个元素?LeetCode第167题
func twoSum(_ numbers: [Int], _ target: Int) -> [Int] {    var i = 0, j = numbers.count - 1    while i < j {        let sum = numbers + numbers[j]        if sum == target {            return [i + 1, j + 1]        }else if sum > target {            j -= 1        }else {            i += 1        }    }    return []}复制代码

  • 怎样合并两个有序的数组之后保持有?LeetCode第88题
func merge(_ nums1: inout [Int], _ m: Int, _ nums2: [Int], _ n: Int) {    for i in stride(from: n + m - 1, to: n - 1, by: -1) {        nums1 = nums1[i - n]    }    var i = 0, j = 0    while i < m && j < n {        if nums1[n + i] > nums2[j] {            nums1[i + j] = nums2[j]            j += 1        }else {            nums1[i + j] = nums1[n + I]            i += 1        }    }    while i < m {        nums1[i + j] = nums1[n + I]        i += 1    }    while j < n {        nums1[i + j] = nums2[j]        j += 1    }}复制代码43. 二叉树题:怎样反转二叉树?怎样验证两个二叉树是完全相称的?


  • 怎样翻转二叉树?LeetCode第226题
func invertTree(_ root: TreeNode?) -> TreeNode? {    guard let root = root else { return nil }    (root.left, root.right) = (root.right, root.left)    invertTree(root.left)    invertTree(root.right)    return root}复制代码

  • 怎样验证两个二叉树是完全相称的?
func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool {    guard let pNode = p ,let qNode = q else { return q == nil && p == nil }    return pNode.val == qNode.val && isSameTree(pNode.left, qNode.left) && isSameTree(pNode.right, qNode.right)}复制代码
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 16:47, Processed in 0.194070 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表