承接上文,在2018年底,面试了许多家大公司,如某大型视频公司、电商平台、音频公司、教育公司等,具体名字就不一一列举了。面试这些公司最大的感受就是,这些公司对基础要求特别高,基础扎实的能过一轮二轮,三轮四轮有时候真的看领导对你的眼缘,很多时候靠运气的!所以,有了扎实的基础就是成功的关键。
承接上篇文章,分析一下各问题的答案,属于个人理解,有问题欢迎指正。
OC基础
1. OC对象,底层的结构是怎样的,类与类别,category为什么不能添加属性? 底层的实现原理,如果类别重写了类中的方法,调用顺序是怎样的?OC语言的动态特性,静态编译与动态类型识别,此处阿里曾考过?这些平时不注意的点有没有注意过?
分类一般用来动态的为已经存在的类扩展新的方法。
分类中可以添加实例方法、类方法、属性、协议。但是不能添加成员变量。
添加属性的话只会生成set、get方法的声明不会实现也不会生成下划线成员变量。
系统是在运行时把分类中的信息整合到了原来的类中
系统是在运行时将分类中对应的实例方法、类方法等插入到了原来类或元类的方法列表中,且是在列表的前边!所以,方法调用时通过isa去对应的类或元类的列表中查找对应的方法时先查到的是分类中的方法!查到后就直接调用不在继续查找。这即是’覆盖’的本质!
这个是与编译顺序有关,最后编译的分类中对应的信息会在整合在类或元类对应列表的最前边。所以是调用最后编译的分类中的方法!可以查看Build Phases ->Complie Source 中的编译顺序!category没有办法去代替子类,它不能像子类一样通过super去调用父类的方法实现。如果category中重写覆盖了当前类中的某个方法,那么这个当前类中的原始方法实现,将永远不会被执行,这在某些方法里是致命的。(ps:这里提一下,+(void)load方法是一个特例,它会在当前类执行完之后再在category中执行。)
同时,一个category也不能可靠的覆盖另一个category中相同的类的相同的方法。例如UIViewController+A与UIViewController+B,都重写了viewDidLoad,后编译的覆盖前编译的。
通过观察头文件我们可以发现,Cocoa框架中的许多类都是通过category来实现功能的,可能不经意间你就覆盖了这些方法中的其一,有时候就会产生一些无法排查的异常原因。
category的诞生只是为了让开发者更加方便的去拓展一个类,它的初衷并不是让你去改变一个类。
结论:
要重写方法,当然我们首推通过子类重写父类的方法,在一些不方便重写的情况下,我们也可以在category中用runtime进行method swizzling(方法的偷梁换柱)来实现。
2. OC的属性,assign,copy,strong,retain,weak.这些属性的应用场景,以及经常考的是可变字符串为什么不能用copy修饰?不可变字符串为什么不能用strong修饰,修饰了会怎样?代理如果用assign会怎样?
- 可变字符串用copy,就会copy成不可变字符串
- 不可变字符串用strong修饰,如果不可变字符串指向可变字符串,不可变字符串就会随着外部可变字符串改变而改变。
- 代理用assign,最后代理应该置为nil,如果不置为nil,会造成野指针。
3. OC的内存管理机制,从MRC到ARC内存管理做了什么?autoreleasePool 什么情况下会用,autoreleasePool 的实现原理?引用计数保存在哪里?了解其数据结构吗?
- 自动引用计数,不用手动释放对象内存,对象内存自动释放
- autoreleasePool实现原理:一个线程的autoreleasepool就是一个指针栈。
栈中存放的指针指向加入需要release的对象或者POOL_SENTINEL(哨兵对象,用于分隔autoreleasepool)。
栈中指向POOL_SENTINEL的指针就是autoreleasepool的一个标记。当autoreleasepool进行出栈操作,每一个比这个哨兵对象后进栈的对象都会release。
这个栈是由一个以page为节点双向链表组成,page根据需求进行增减。
autoreleasepool对应的线程存储了指向最新page(也就是最新添加autorelease对象的page)的指针。 - 引用计数保存地方:散列表,数据结构是散列表
4. 各种原理性问题,如dictionary的实现原理,KVC的实现原理以及为什么要派生出子类,KVO的实现原理,KVO为什么要移除观察者,timer会造成循环引用吗?怎么解决?weak的实现原理?
dictionary:使用hash表来实现key和value之间映射和存储的。hash函数设计的好坏影响着数据的查找访问效率。数据在hash表中分布的越均匀,其访问效率越高。而在Objective-C中,通常都是利用NSString来作为键值,其内部使用的hash函数也是通过使用 NSString对象作为键值来保证数据的各个节点在hash表中均匀分布。
见NSDictionary中最常用的一个方法原型:
[objc] view
plain copy
(void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;¬
从这个方法中可以知道, 要作为 Key 值,必须遵循 NSCopying 协议。也就是说在NSDictionary内部,会对 aKey 对象 copy 一份新的。而 anObject 对象在其内部是作为强引用(retain或strong)。所以在MRC下,向该方法发送消息之后,我们会向anObject发送 release 消息进行释放。
既然知道了作为 key 值,必须遵循 NSCopying 协议,说明除了 NSString 对象之外,我们还可以使用其他类型对象来作为 NSDictionary 的 key值。不过这还不够,作为 key 值,该类型还必须继承于 NSObject 并且要重载一下两个方法:
1 - (NSUInteger)hash;
(BOOL)isEqual:(id)objectKVC 底层实现原理:
当一个对象调用setValue:forKey:
方法时,方法内部会做以下操作: 1.判断有没有指定key的set方法,如果有set方法,就会调用set方法,给该属性赋值 2.如果没有set方法,判断有没有跟key值相同且带有下划线的成员属性(_key).如果有,直接给该成员属性进行赋值 3.如果没有成员属性_key,判断有没有跟key相同名称的属性.如果有,直接给该属性进行赋值 4.如果都没有,就会调用 valueforUndefinedKey 和setValue:forUndefinedKey:方法KVO 的底层实现原理:
(1)KVO 是基于 runtime 机制实现的
(2)当一个对象(假设是person对象,对应的类为 JLperson)的属性值age发生改变时,系统会自动生成一个继承自JLperson的类NSKVONotifying_JLPerson,在这个类的 setAge 方法里面调用 [super setAge:age]; [self willChangeValueForKey:@"age"]; [self didChangeValueForKey:@"age"];
三个方法,而后面两个方法内部会主动调用 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context方法,在该方法中可以拿到属性改变前后的值.观察者不会在 dealloc 的时候自动移除。因此最晚必须在观察者 dealloc 时移除它
因为通知中心对观察者有一个强引用,要及时移出去CADisplayLink、NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用
weak原理:Runtime维护了一个Weak表,用于存储指向某个对象的所有Weak指针。Weak表其实是一个哈希表,Key是所指对象的地址,Value是Weak指针的地址(这个地址的值是所指对象的地址)的数组。
一个对象A,里面有一个weak属性B, 首先会有一个hash表, 键为A的地址, 值为一个数组, 这个数组里包含了B指针的地址, 当销毁的时候回根据B指针的地址获取到B的指针, 然后置为nil
在对象被回收的时候,经过层层调用,会最终触发下面的方法将所有Weak指针的值设为nil。
简单来说,这个方法首先根据对象地址获取所以Weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从Weak表中删除。
1.初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2.添加引用时:objc_initWeak函数会调用 storeWeak() 函数, storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3.释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
5. block的实现原理,__block的作用以及底层实现原理,block的循环引用问题,下划线修饰的变量会造成循环引用吗?怎么处理?masonry链式编程怎么实现的?什么情况下不用__weak?(Masonry、AFNetWorking、动画等)
- block对象就是一个结构体,里面有isa指针指向自己的类(global malloc stack),有desc结构体描述block的信息,__forwarding指向自己或堆上自己的地址,如果block对象截获变量,这些变量也会出现在block结构体中。最重要的block结构体有一个函数指针,指向block代码块。block结构体的构造函数的参数,包括函数指针,描述block的结构体,自动截获的变量(全局变量不用截获),引用到的__block变量。(__block对象也会转变成结构体)
block代码块在编译的时候会生成一个函数,函数第一个参数是前面说到的block对象结构体指针。执行block,相当于执行block里面__forwarding里面的函数指针。 - __block:Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
- block循环引用,指针相互指向,用__weak增加弱环
- masonry链式编程:不会造成循环引用,constraintMaker只是局部变量,不会造成循环引用,链式编程:利用block, 代码块的返回类型是该类的实例变量,设置完相关的操作,并把实例对象返回。
- AFNetworking是因为人家大神自己封装了一个completionBlock,不管你传进来是啥,都给你把循环引用打破。
6. 平时有用到runtime吗?runtime干嘛的,方法交换。runloop的理解。
- 1.交换两个方法 Method Swizzling 2.获取属性列表以及成员变量 3.消息转发 4.动态关联属性 5.字典转模型应用 6.isa swizzling
7. 消息发送机制,消息转发的三步补救措施?objc向一个对象发送消息时,发送消息的整个过程?objc中向一个nil对象发送消息将会发生什么?这个题目很重要,一题可能会否定一个人。
- 消息发送 [object sendMassage]
1.通过obj的isa指针找到其所对应的类。2.通过SEL先去类的cache列表中找这个方法,如果就去找方法的实现,不存在,进入第3步 3. 去类的method列表中找,如果就去找方法的实现,没有找到,根据类中的superclass指针去父类中找,一直到NSObject. 4. 可以看到这个方法中的第二行代码imp,可以通过这个imp来查找这个方法的实现,要是没有找到,runtime给我们提供了三次机会让我们的程序不会崩溃,也就是下面要提到的动态方法解析和消息转发(消息重定向,消息转发)
动态方法解析:
runtime提供的第一次实现这个方法的机会,要实现resolveInstanceMethod/resolveClassMethod方法,给未实现的方法在运行时添加实现,返回no/不实现,进入消息转发。
消息转发一:消息重定向
消息重定向:返回一个实现了该方法的对象,要实现-(id)forwardingTargetForSelector:(SEL)aSelector函数,如果返会nil/self,则进入下面的消息转发二
消息转发二:消息转发
将未实现方法的相关信息打包成一个NSInvocation对象,然后交给一个类去实现。需要实现-(NSMethodSignature )methodSignatureForSelector:(SEL)aSelector和-(void)forwardInvocation:(NSInvocation )anInvocation方法
8. 一个对象的本质,一个对象的内存布局,类对象与元类对象?
- 一个对象的本质就是一个结构体,里面有isa指针,指向class,结构体是className_IMPL,里面有结构体,最终指向NSObject
- 1.instance对象在内存中存储的信息包括:isa指针以及其他成员变量,
2.类对象:
• 它们是同一个对象。每个类在内存中有且只有一个class对象
• class对象在内存中存储的信息主要包括
• isa指针
• superclass指针
• 类的属性信息(@property)、类的对象方法信息(instance method)
• 类的协议信息(protocol)、类的成员变量信息(ivar)- 元类对象:
• objectMetaClass是NSObject的meta-class对象(元类对象)
• 每个类在内存中有且只有一个meta-class对象
• meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括
• isa指针
• superclass指针
• 类的类方法信息(class method)
• 如果是instance对象,返回class对象
• 如果是class对象,返回meta-class对象
• 如果是meta-class对象,返回NSObject(基类)的meta-class对象
- 元类对象:
9. 深拷贝浅拷贝
- 对非容器类的浅拷贝就是拷贝对象的地址,对象里面存的内容仍然是一份,没有新的内存被分配。对非容器类的深拷贝就是重写分配一块内存,然后把另一个对象的内容原封不动的给我拿过来。
对容器类的深拷贝是对容器中的每个元素都进行拷贝,容器类的浅拷贝是对容器里的内容不进行拷贝,两个容器的地址是不同的,但容器里的所装的东西是一样的,在一个容器中修改值,则另一个浅拷贝的容器中的值也会变化。
iOS系统
1. main()之前的过程有哪些?
1)dyld开始将程序二进制文件初始化
2)交由ImageLoader读取Image,其中包含了我们的类、方法等各种符号(Class、Protocol、Selector、IMP)
3)由于runtime向dyld绑定了回调,当Image加载到内存后,dyld会通知runtime进行处理
4)runtime接手后调用map_images做解析和处理
5)接下来load_Images中调用call_load_methods,遍历所有加载进来的Class,按继承层次依次调用Class的+load和其他Category的+load方法
6)至此,所有的信息都被加载进了内存中
7)最后dyld调用真正的main函数
注意:dyld会缓存上一次把信息加载到内存的缓存,所以,第二次比第一次启动快一点。
dyld: 而动态库在加载的时候都需要用dyld(位于/usr/lib/dyld)程序进行链接
2. AF实现原理?SD实现原理?
- SDWebImage,里面有一个manager管理downloader以及缓存策略cache,downloader在后台开辟线程,实现下载,下载后的图片解码也在线程中,最后要回到主线程展示。缓存策略的话,先缓存到内存中,然后是硬盘中,
当SDWebImageManager向SDImageCache要资源时,先搜索内存层面的数据,如果有直接返回,没有的话去访问磁盘,将图片从磁盘读取出来,然后做Decoder,将图片对象放到内存层面做备份,再返回调用层
通知所有的downloadDelegates下载完成,回调给需要的地方展示图片。将图片保存到SDImageCache中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独NSInvocationOperation完成,避免拖慢主线程。
1.首先将placeholderImage进行展示,SDWebImageManager根据URL开始处理图片
2.SDImageCache从缓存中查找图片图片,如果有sdImageCacheDelegate回调image:didFindImage:forkey:useInfo:到SDWebImageManager .到前端展示图片
3.缓存中没有,生成NSInvocationOperation添加到队列中开始在硬盘中查找,如果找到会将图片添加到内存缓存中(如果空闲缓存不够,会先清理)然后SDImageCacheDelegate回调imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
4.如果硬盘中没有则共享或生成下载器SDWebImageDownLoader开始下载图片,图片下载有NSURLConnection来做
5.图片解码处理在一个NSOperationQueue完成,不会拖慢主线程UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
6.在主线程notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给SDWebImageDownloader。imageDownloader:didFinishWithImage: 回调给SDWebImageManager告知图片下载完成
7.通知所有的downloadDelegates下载完成,回调给需要的地方展示图片。将图片保存到SDImageCache中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独NSInvocationOperation完成,避免拖慢主线程。
8.SDImageCache在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片
系统级内存警告如何处理?
取消当前正在进行的所有下载操作
清除缓存数据(面试):
删除过期的文件数据,计算当前未过期的已经下载的文件数据的大小,如果发现该数据大小大于我们设置的最大缓存数据大小,那么程序内部会按照按文件数据缓存的时间从远到近删除,知道小于最大缓存数据为止。
3. 说一下UITableViewCell的卡顿你是怎么优化的?
4. 设计一套缓存策略。
1. 可以将数据缓存到本地磁盘。
2. 可以判断一个资源是否已经被缓存。如果已经被缓存,在请求相同的资源,先到本地磁盘搜索。
3. 可以判断文件缓存什么时候过期。这里为了简单起见这里,我们在请求url资源的时候,给每次请求的文件设定一个过期的时间。
4. 如果文件已经被缓存,而且没有过期,这将本地的数据返回,否则重新请求url
5. 如果文件下载不成功或者下载没有完成,下次打开程序的时候,移除这些没有成功或者没有下载完成的文件。
6. 开辟线程,同时请求或者下载多个资源。
5.有哪些锁?有什么用处?
- 同步锁,递归锁,读写锁,互斥锁,条件锁
1、@synchronized
2、NSLock 对象锁
3、NSRecursiveLock 递归锁
4、NSConditionLock 条件锁
5、pthread_mutex 互斥锁(C语言)
6、dispatch_semaphore 信号量实现加锁(GCD)
7、 OSSpinLock (暂不建议使用)
6. iOS启动优化和列表优化点
• APP的启动由dyld主导,将可执行文件加载到内存,顺便加载所有依赖的动态库
• 并由runtime负责加载成objc定义的结构
• 所有初始化工作结束后,dyld就会调用main函数
• 接下来就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法
- dyld
• 减少动态库、合并一些动态库(定期清理不必要的动态库)
• 减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类)
• 减少C++虚函数数量
• Swift尽量使用struct - runtime
• 用+initialize方法和dispatch_once取代所有的attribute((constructor))、C++静态构造器、ObjC的+load。 - main
• 在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在finishLaunching方法中
• 按需加载
7. iOS与h5的交互
8. 如何增加一条常驻线程?
- AFN 的做法是使用一个 runloop 来保证线程不死~然而频繁的创建线程并启动runloop肯定会造成内存泄露(runloop 无法停止.线程无法退出)
所以AFN就创建了一个单例线程,并且保证线程不退出。
9. 说一下响应链?
- 当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中
- UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。
- UIWindow将事件向下分发,即UIView。
- UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。
- 遍历子控件,重复以上两步。
- 如果没有找到,那么自己就是事件处理者。如果
- 如果自己不能处理,那么不做任何处理。
其中 UIView不接受事件处理的情况主要有以下三种
1)alpha <0.01
2)userInteractionEnabled = NO
3.hidden = YES.
这个从父控件到子控件寻找处理事件最合适的view的过程,如果父视图不接受事件处理(上面三种情况),则子视图也不能接收事件。事件只要触摸了就会产生,关键在于是否有最合适的view来处理和接收事件,如果遍历到最后都没有最合适的view来接收事件,则该事件被废弃。
10. 动画分为哪几类?如何终结一个动画?什么是转场动画?什么情况下动画会失败?
11. 有没有用过GCD?GCD的group会线程阻塞?下载一张图片,下载过程中UIImageView突然取消会怎样?NsOperation与GCD的不用应用场景
12. CoreText实现富文本?
• CTLine 可以看做Core Text绘制中的一行的对象 通过它可以获得当前行的line ascent,line descent ,line leading,还可以获得Line下的所有Glyph Runs
• CTRun 或者叫做 Glyph Run,是一组共享想相同attributes(属性)的字形的集合体
一个CTFrame有几个CTLine组成,有几行文字就有几行CTLine。一个CTLine有包含多个CTRun,一个CTRun是所有属性都相同的那部分富文本的绘制单元。所以CTRun是CTFrame的基本绘制单元。
13. ViewController的生命周期?
- 执行顺序:
- initWithCoder:通过nib文件初始化时触发。
- awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。
- loadView:开始加载视图控制器自带的view。
- viewDidLoad:视图控制器的view被加载完成。
- viewWillAppear:视图控制器的view将要显示在window上。
- updateViewConstraints:视图控制器的view开始更新AutoLayout约束。
- viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。
- viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。
- viewDidAppear:视图控制器的view已经展示到window上。
- viewWillDisappear:视图控制器的view将要从window上消失。
- viewDidDisappear:视图控制器的view已经从window上消失。
14. 设计一个检测主线和卡顿的方案。
- 我们启动一个worker线程,worker线程每隔一小段时间(delta)ping以下主线程(发送一个NSNotification),如果主线程此时有空,必然能接收到这个通知,并pong以下(发送另一个NSNotification),如果worker线程超过delta时间没有收到pong的回复,那么可以推测UI线程必然在处理其他任务了,此时我们执行第二步操作,暂停UI线程,并打印出当前UI线程的函数调用栈。
15. iOS应用安全,如何防止反编译?iOS平时的加密措施,密码一般保存在哪里?
16. 感觉项目中哪些地方做的比较好的?有哪些优化点值得推荐的?
17. 项目的整体架构是怎样的,项目层级如何划分?有什么熟悉的架构?项目架构如何做到细分?
18. 平时如何采集性能数据的?
19. 网络请求有没有做到深度优化?
iOS网络请求
1. http与HTTPS,每次请求都要建立证书吗?
- HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤:
(1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
(3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
(5)Web服务器利用自己的私钥解密出会话密钥。
(6)Web服务器利用会话密钥加密与客户端之间的通信
2. get与post区别,post请求能否用拼接参数的形式?get一定要拼接参数吗?两个最本质的区别?
- post也可以用拼参方式;get也不一定非得拼参。
GET产生一个TCP数据包;POST产生两个TCP数据包。
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。- GET与POST都有自己的语义,不能随便混用。
- 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
- 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
3. APNS整个请求过程
- APNs(Apple Push Notification service)是远程推送功能的核心,通过APNs客户端和苹果服务器建立一个长连接,推送也是通过这个长连接发送到客户端上
deviceToken是设备的一个标识符,属于你这款APP装在你这个设备上的标识符,即每个APP在每一个不同的设备上都有着不同的deviceToekn,通过注册远程推送服务,APNs会返回给你的APP的deviceToken。
1、首先是应用程序注册消息推送。
2、IOS跟APNS Server要deviceToken。应用程序接受deviceToken。
3、应用程序将deviceToken发送给PUSH服务端程序,保存deviceToken。
4、服务端程序向APNS服务发送消息。
5、APNS服务将消息发送给iPhone应用程序。
无论是iPhone客户端跟APNS,还是Provider和APNS都需要通过证书进行连接的。下面介绍一下所用到证书的制作。
服务器通过TLS验证和APNs连接,HTTPS中用的也是TLS协议,即四步握手,首先初始化TLS连接,即provider(服务器)发送请求给APNs,APNs服务器返回APNs证书(即公钥)给provider,然后服务端收到后生成provider证书后再返回给APNs,APNs收到后验证后即可以建立TLS连接,不过APNs用的不是HTTPS,而是HTTP2。
4. TCP与UDP的应用区别
5. socket的应用场景
6. 本地大量频繁的数据怎么处理?
数据库
1. 平时用的数据库有哪几种?如何联表查询,数据如何保证读取安全?
数据结构与算法
算法建议系统的看,有本书值得推荐《剑指offer》,可以系统的学习。毕竟数据结构与算法是基础,打好基础才能万丈大楼平地起!
1. 二分查找法
2. 反转链表
3. 两组有序数据,查找第k个数
当然,经典算法还有很多,此处就不一一列举了。
脚本
固有的iOS知识有时候并不能满足开发需求了,有时候需要脚本的编写。推荐Python,因为Mac本身就支持Python。能够利用Python处理一些事情,越来越成为一个程序员的基本要求。
1.自动化打包也越来越倾向于用脚本实现
2. 能够用脚本加载一些资源文件
3. 在程序加载之前能够用脚本处理一些事情
4. 许多第三方包依赖于脚本
5. 许多大公司,XCode只是一个代码编写工具,代码的编译运行交给脚本处理,能够用脚本处理许多事情
总结
本文总结了大部分问题的答案,很多都是日积月累总结的。如有不对的地方,欢迎大家指正。
大家在准备面试的时候,可以在本文基础上,形成思维导图,每个知识点连成一片,从而进行系统地学习。