1.上传一组图片的时候可使用dispatch_group_t,将每一个图片的上传添加到组中,后续可以汇总所有图片上传结束dispatch_group_wait。
dispatch_group_enter(self.imageGroup);
dispatch_group_wait(self.imageGroup, DISPATCH_TIME_FOREVER);
dispatch_group_leave(self.imageGroup);
2.定义字符串常量:
.h文件:FOUNDATION_EXPORT NSString *const LKREmotionCodeKey;
.m文件:NSString * const LKREmotionCodeKey = @"LKREmotionCodeKey";
3.加锁解锁移除锁:
pthread_mutex_lock(&_lock);
pthread_mutex_unlock(&_lock);
4.对NSMutableArray进行KVO:
改变数组
[[self mutableArrayValueForKey:@"myItems"] addObject:item];
使用kvc获取数组
[self mutableArrayValueForKey:@"mutableItems"];
5.可以遍历数组的同时删除特定的元素:
reverseObjectEnumerator按照索引号从大到小访问数组的元素
for (LKRAudioQueueItem *item in self.mutableOriginalItems.reverseObjectEnumerator) { // 本地播放列表,需要过滤出本地歌曲,不改变原有列表顺序
LKRMediaPlayableResult result = [LKRMediaPlayableChecker playableFlowForMedia:item.audio option:LKRMediaPlayableSceneOptionOfflinePlay];
if (result != LKRMediaPlayableResultOK) {
[self.mutableOriginalItems removeObject:item];
}
}
6.音频播放器系统控制中心:
MPRemoteCommandCenter即锁屏状态下的显示
7.LKR的缓存主要类解析:
LKRKVStorageManager:数据库文件操作管理类,用于载入数据库文件类,此处已经对不同的数据进行了文件分类,例如某些需要itunes自动备份的会放在Document子级目录下,有些是放在Cache目录下
LKRKVStorage:使用FMDatabaseQueue生成数据库操作队列,数据库实体文件,具备生成数据表,以及对表中的数据进行增删改查的能力
Cache:LKRCommonCache,LKRItemCache,LKRUserCache按照功能用途分成多类缓存
其实就是通过数据库的数据操作,将本地的缓存载入到内存缓存中,通过表中不同的key拿到对应的数据保存下来,用于后续的操作。
SubCache:将Cache进行细分,典型的有LKRLocalMediaCache,LKRMediaDownloadCache。LKRLocalMediaCache存储了本地的所有媒体文件,LKRMediaDownloadCache保存了媒体文件的下载信息(下载状态、下载路径等等)
8.LKR的音乐播放器主要类/类别/协议解析:
LKRAudioManager:持有播放引擎(LKRAudioEngine),控制播放相关的操作如切换播放模式、播放、暂停、跳转到秒数、恢复等;播放列表的控制,添加音频、播放列表中的某一项;
控制FM私人电台,加载音频、播放音频、播放歌曲、播放单集、播放全部等操作
LKRAudioEngine:播放器实际操纵者,持有音频内核类(LKRAudioAVPlayerCore),可用于设置音频相关的一些属性如播放的媒体类型、打点类型、音频质量、设置的暂停播放时间等。
可设置session(音频初始化必须操作)为活跃、切换播放类型、播放秒数的回调监听。
FM相关:向外暴露FMTask,当前播放项是否正在加载的标识
LKRAudioAVPlayerCore:音频播放器的核心,这里将core需要实现的一些行为方法定义到协议中,即核心必须实现LKRAudioCoreProtocol协议中描述的方法;core是系统音频的实际操纵者,它包装了与播放相关的系统类AVPlayer、AVPlayerItem,除了实现协议相关方法之外,需要对某些事件做监听如AVPlayerItemDidPlayToEndTimeNotification(播放结束)、AVPlayerItemPlaybackStalledNotification、AVPlayerItem对应的属性变化(status、loadedTimeRanges、playbackBufferEmpty、playbackLikelyToKeepUp)
LKRAudioEngine+PlaybackControl:通过类别抽取出跟播放相关的操作方法
LKRAudioEngine+Queue:这里是把跟媒体列表相关的播放方法单独抽出去放到一个类别中,类别包含媒体列表的播放方法,其实就是LKRAudioManager中对Queue操作的实现。
LKRAudioEngine+PrivateFM:FM相关的播放方法抽取。
LKRAudioCoreCallback:LKRAudioCore(LKRAudioCoreProtocol的实现者)的回调,这里的回调是通过代理的方式回传给engine。需要回调出去的信息有:更新锁屏信息、当前媒体播放当结束、播放item的变化、播放进度每秒回调处理业务。
LKRAudioCoreProtocol:成为音频播放器的核心需要遵循的协议,能够获取媒体相关的一些属性如播放时长、是否正在缓存、总时长、当前播放时间等;需要提供播放相关的方法(播放、暂停、拖动进度条、清楚播放等)
LKRAudioLogger:音频打点
LKRAudioStatusInfo:音频状态信息
9.HTTP1和HTTP2的区别
Http1.x的缺陷:线程阻塞,在同一时间,同一域名的请求有一定数量限制,超过限制数目的请求会被阻塞
Http1.0(短连接)的缺陷:与服务器保持短暂的连接,每次请求都需要建立一个TCP连接(耗时成本高),服务器请求处理后立即断开TCP。
Http1.1改进点:持久连接,TCP默认不关闭,在同一个TCP连接中发送多个请求,分块传输数据。
缺点:同一个TCP连接里数据通信是按次序进行的,如果前面的处理特别慢,后面就会有许多请求排队等着,导致堵塞。
Http/2.0的特点:
二进制格式;
多路复用,而非有序并阻塞的、只需一个连接即可实现并行;(解决了线头阻塞的问题,与Http1最重要的区别)
使用报头压缩,降低开销;
服务器推送
10.TCP如何保证可靠性
检验和;
序列号;
确认应答机制;三次握手,四次挥手
超时重传机制;
连接管理机制 ;
流量控制;
拥塞控制 ;
11.weak和assign的区别
weak只能作用于对象,不能作用于基本数据类型
assign不但能作用于对象还能作用于基本数据类型
weak所指向的对象销毁时会将当前指向对象的指针指向nil,防止野指针的生成
assign所指向的对象销毁时不会将当前指向对象的指针指向nil,有野指针的生成
12.对象的⽅法是存放到哪个地⽅,调⽤的流程是怎样的
对象方法存放在类中,可以通过runtime获取类的方法列表
调用流程:
1、先根据对象的 isa 指针找到该对象的类对象,即该对象所属的类;
2、在该类对象的缓存方法列表里面查找该方法,找到即执行响应方法;
3、如果没找到,向该类对象的方法列表里面查找该方法,找到即执行响应方法;
4、如果没找到,根据该类对象的父对象指针,去父对象里去执行2、3步骤;
5、如果没找到,转向拦截调用,走消息转发机制;
消息转发机制:
1、是否动态的去添加这个方法去处理:+(BOOL)resolveInstanceMethod:(SEL)sel或者+(BOOL)resolveClassMethod:(SEL)sel 。返回值为No时,进入第二步;
2、转发给其他对象,是否能实现这个方法:- (id)forwardingTargetForSelector:(SEL)aSelector 。返回值为nil时,进入下一步;
3、返回一个方法签名:- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 。
方法签名返回正确的话,执行 - (void)forwardInvocation:(NSInvocation *)anInvocation ,这个方法可以转发到多个对象中;
4、再没找到就会crash
如果找到了:
在方法中找到IMP指针,执行具体实现
13.load 和 initialize的区别
加载时机:
load是类被加载的时候调用(main函数之前),会优先执行父类的load,如果子类没有实现load那么父类的也不会调用,最后执行扩展中的load,存在多个扩展时执行顺序由文件的添加顺序决定。多用于方法交换
initialize是在类或者子类的第一个方法被调用前调用,即使类文件被引用进项目但是没有使用initialize也不会被调用,调用的时候会优先调用父类的initialize,如果子类没有实现initialize,子类会继承父类的initialize调用一遍,在此之前父类方法会被优先调用一次。当有多个扩展实现了initialize时会覆盖类中的initialize,只会执行一个(编译文件中的最后一个)
14.SDWebImage有什么亮眼的地⽅
https://www.jianshu.com/p/5baeff2bc9d4
SDWebImage的大致流程:
给UIImageView设置图片-取消对应的UIImageview的图片相关操作-预加载的图片URL是否为空(为空则出错处理)-生成图片URL对应的key-使用key读取内存和自盘缓存-读取图片设置到对应空间(没有缓存:下载图片图片处理-图片保存到缓存-设置对应的UIImageview)
亮眼的地方:异步下载模式、内存+磁盘缓存、自动缓存过期处理机制、同一个URL不会重复下载、耗时操作都在子线程、不会阻塞主线程、保存了一个无效url的数组不会被无限重试
15.⽆重复字符的最⻓⼦串“abcdddabcdefffcde”
大概思路:可以通过一次遍历,记录maxstr、tmpstr,tmpstr在遇到重复字符串之前一直在增长,遇到相同字符时临时存储到maxstr中,tmpstr继续从最后一个重复的字符开始重新计算,继续得到新的不重复子串,每次得到一个新的则与maxstr比较,直至字符串遍历至最后。
16.copy和strong的区别,使⽤copy修饰NSMutableArray有什么问题
copy修饰NSMutableArray会导致通过点属性得到的新数组是不可变的,在其他地方使用NSMutableArray进行数组内容变更时会导致崩溃
17.APNS的原理
registerDeviceToken-service-sendMsg-appleservice-iphone-client
18.KVO的原理
可修改automaticallyNotifiesObserversForKey的返回值进行手动触发kvo
注册了observer时对象的isa指针会指向一个新的中间类,中间类会重写setter方法,在方法前后加上willchange、didchange
19.AutoReleasepool是怎么管理内存的
什么时候需要自己创建pool:
1、你写的程序不是基于UI framework, 例如命令行项目
2、你写的循环创建了大量临时对象 -> 你需要在循环体内创建一个autorelease pool block并且在每次循环结束之前处理那些autoreleased对象. 在循环中使用autorelease pool block可以降低内存峰值
3、你创建了一个新线程
当线程开始执行的时候你必须立马创建一个autorelease pool block, 否则你的应用会造成内存泄露.
添加了AutoReleasepool的block之后,内部执行产生的变量会立即释放掉,减少瞬间的内存消耗
20.FMDB是怎么做到线程安全
FMDatabaseQueue
队列里面的是同步方法,这样可以避免多线程锁死的情况(多个线程访问同一块数据)。但是这样会阻塞主线程,所以需要在外面套个异步执行,即数据库的操作全都是在子线程中执行,执行的方式是同步
21.内存结果分多少种
bss段:存放未初始化的全局变量和静态变量
数据段:存放已经初始化的全局、静态变量、例如一些字符串常量等
代码段:存放程序执行代码
堆:存放进程运行时被动态分配的内存段,大小不固定
栈:用户存放程序临时创建的局部变量,不包括static申明的变量(数据段中存放)
22.CocoaPods原理
CocoaPods 的原理是将所有的依赖库都放到另一个名为Pods的项目中, 然而让主项目依赖Pods项目,
这样,源码管理工作任务从主项目移到了Pods项目中
1、Pods项目最终会编译成一个名为liLKRods.a的文件, 主项目只要依赖这个.a文件即可.
2、对于资源文件, CocoaPods提供了一个名为Pods-resources.sh的bash脚步, 该脚本在每次项目
编译的时候都会执行,将第三方库的各种资源文件复制到目标目录中.
3、CocoaPods通过一个名为Pods.xcconfig的文件在编译设置所有的依赖和参数
23.深拷贝浅拷贝
对一个可变数组的copy操作是深拷贝还是浅拷贝:浅拷贝,只会拷贝一份指针
24.自动释放池的原理
http://blog.sunnyxx.com/2014/10/15/behind-autorelease/
AutoreleasePoolPage、autoreleaseFast
自动释放池是由 AutoreleasePoolPage 以双向链表的方式实现的
释放时机是其所在的runloop迭代结束的时候
当对象调用 autorelease 方法时,会将对象加入 AutoreleasePoolPage 的栈中
调用 AutoreleasePoolPage::pop 方法会向栈中的对象发送 release 消息
25.block原理
https://juejin.cn/post/6844903893176958983
copy操作会将stackblock转换成mallocblock
什么情况会自动将block进行copy操作?
1.block作为返回值
2.将block赋值给__strong指针(ARC下赋值操作默认都是strong)
3.block作为Cocoa API中方法名含有usingBlock的方法参数时
4.block作为GCD API的方法参数时
26.weak的原理
https://juejin.cn/post/6867360481322434567
1、weak的原理在于底层维护了一张weak_table_t结构的hash表,key是所指对象的地址,value是weak指针的地址数组。
2、weak 关键字的作用是弱引用,所引用对象的计数器不会加1,并在引用对象被释放的时候自动被设置为 nil。
3、对象释放时,调用clearDeallocating函数根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
(当第一次创建某个对象的弱引用时,会以该对象的指针和弱引用的地址创建一个 weak_entry_t,并放在该对象所处的 SideTable 的 weak_table_t 中,然后以后所有指向该对象的弱引用的地址都会保存在该对象的 weak_entry_t 的哈希数组中,当该对象要析构时,遍历 weak_entry_t 中保存的弱引用的地址,将弱引用指向 nil,最后将 weak_entry_t 从 weak_table 中移除。)
27.hittest的原理
https://juejin.cn/post/6844903460706451463#comment
触摸事件的传递是由底层向上层传递,而事件的响应是由上层向底层传递的,找到合适的对象进行处理
28.isKindOfClass与isMemberOfClass的区别
主要看源码实现
object_getClass类方法的调用,得到的是metaClass元类
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superClass)
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superClass)
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
29.排序
冒泡排序:每一次外层循环确定一个最大的数放在顶部,起始的位置在往前挪动
for i in 0..count {
for j in 0..<(count - i - 1) {
if (self[j] > self[j + 1]) {
swap(self[j],self[j+1]) //前面的数大于后面的,那么就把前面的数往后放,类似大的气泡往上面冒出
}
}
}
选择排序:每次外层循环确定一个最小的数放在底部,起始的位置在往后挪动
for i in 0..count {
var minIndex = i;
for j in (i + 1)..
minIndex = j
}
swap(self[i],self[minIndex]) 初始值与最小值交换
}
30.setvalue和setobject的区别
1.setobject:ForKey:是NSMutableDictionary特有的
2.setobject中的key和value不能为nil
3.setValue:ForKey:是KVC的主要方法
4.setValue中的key只能为字符串,value可以为nil也可以为空对象[NSNull null]以及其他所有对象
31.GCD与NSOperation区别
1.GCD仅仅支持先进先出FIFO队列,不支持异步操作之间的依赖关系设置
2.GCD更接近底层,GCD在追求性能的底层操作来说,速度是最快的
3.GCD需要自己写更多的代码来处理异步操作之间的事务性、顺序性、依赖关系
4.NSOperation内建了异步操作之间的事务性、顺序性、依赖关系
5.NSOperation可以设置优先级、最大并行数等
6.NSOperation是基于GCD封装的
32.http协议的一个感知和理解
https://www.jianshu.com/p/9017e6649228
33.音乐文件的缓存策略
过期策略、最大容量策略(移除早期的缓存)
音乐文件列表本地缓存了媒体下载信息,能通过下载信息拿到音乐的下载状态已经本地存放地址(单独有一张表key就是下载id),以及历史歌单/媒体播放列表、音频播放队列缓存。
34.swizzle存在哪些问题
https://www.jianshu.com/p/1bacd182329f
1.只在 +load 中执行 swizzling 才是安全的。 常见!!
2.被 hook 的方法必须是当前类自身的方法,如果把继承来的 IMP copy 到自身上面会存在问题。父类的方法应该在调用的时候使用,而不是 swizzling 的时候 copy 到子类。
3.被 Swizzled 的方法如果依赖于cmd ,hook 之后 cmd 发送了变化,就会有问题(一般你 hook 的是系统类,也不知道系统用没用 cmd 这个参数)。
4.命名如果冲突导致之前 hook 的失效 或者是循环调用。
造成的影响子类hook了父类的方法,无法保证方法的调用顺序, Hook 不到父类的派生类
重复的去hook某个方法,调用顺序取决于编译文件中文件的添加顺序(后添加的会先执行)
35.当网络卡顿,或者播放一段空的音频时怎么处理
播放器核心类会对播放状态监听,可获取缓冲区域决定是否播放
网络差时会进行buffer,可以监听buffer的回调,正在buffer则先暂停一会
36.异步执行顺序队列里的任务
37.avplayer需要注意的点
1.新系统下iOS10以上有时播放不了音频,必须设置automaticallyWaitsTomMinimizeStalling=NO
2.需要特殊处理的:音频通道被抢占导致无法播放、其他app播放声音打断、用户插入和拔出耳机,可以监听这些事件的通知让其继续播放
38.swizzle的坑,有哪些隐患
1.直接交换imp是危险的,如果当前类中没有实现被交换的方法,class_getInstanceMethod得到的是某个父类的Method对象,这样就会把父类的原始实现IMP跟这个类的swizzle实现交换了,这样其他的父类和子类防范调用就会出问题,严重可能crash
2.如果在子类的分类中hook了父类中的某个方法,如果是通过add的方式则会将父类的此方法复制一份到子类的方法列表中。如果父类没有实现这个方法,子类中去替换方法时会找不到而crash
3.使用方法交换时,需要注意命名,hook的方法是当前类自己的方法,load方法中的交换只能进行一次(dispatch_once)、在不同的类别中进行了多次hook
39.load的加载时机
程序启动一般实现加载框架类以及一些库文件,在此之后在加载类.main文件执行之前的时候load被调用
40.iOS优化、瘦身
瘦身:
1.尽量使用assetcatalog存放资源、开启bitcode,apple自身的优化机制
2.资源文件的去重、压缩
优化:https://juejin.cn/post/6844904131941892110 着重关注卡顿优化
1.减少视图数量和层级,减轻GPU负担(层级多了渲染时进行视图叠加的计算)
2.视图创建、布局计算、文本绘制放到子线程中
tableview卡顿解决:ASDK
cell的重用、避免cell重新布局(创建的时候布局就确定好)、提前计算cell的属性缓存起来、减少cell的控件数、不要使用clearcolor以及透明度为0的视图(会导致gpu额外的渲染消耗)
避免图层混合:确保控件的opaque属性设置为true,确保backgroundColor和父视图颜色一致且不透明,确保控件的opaque属性设置为true,确保backgroundColor和父视图颜色一致且不透明,确保UIImage没有alpha通道
CPU:负责对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)
GPU:负责纹理的渲染(将数据渲染到屏幕)
可以围绕cpu和gpu去说,例如cpu会涉及列表cell的动态高度缓存、gpu的话就是异步渲染,把ui渲染部分的消耗放到子线程中。ui布局的时候尽量不要设置透明度为0的叠加视图,这样会增加渲染消耗
41.房车宝app里的路由表的实现,要基于runtime那部分去讲(有个类的注册过程)
封装一个基类,基类需要实现路由协议(提供参数、跳转模式等)
app启动的时候需要去动态注册路由表,注册的过程就是去遍历所有被加载到工程中的类列表,找出其中实现了路有跳转协议的类,将类信息存储到一个单例中,这样不用每次跳转的时候都要遍历整个类列表,直接在内存中去通过key取就可以,因为你去跳转一个类的时候需要判断这个类是否在工程里存在(也就是被加载过),
跳转的时候参数处理分两种1.kvc的方式:获取到属性列表找到对应的key进行赋值 2.自定义参数处理,需要队友页面实现route的协议,自行处理参数
通常在使用UrlRouter的时候注册类的时候也在+load方法中注册
42.rsa算法的实现(大概提一下,其实主要是要撤一下安全相关的问题)
RSA算法的核心就是欧拉定理,根据它我们才能得到私钥,从而保证整个通信的安全
43.如何去检测野指针
野指针:所指向的对象被释放或者收回,但是该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址。这个指针就是野指针
解决方案:
1.Zombie Objects,其官方解释如下:一个对象已经解除了它的引用,已经被释放掉,但是此时仍然是可以接受消息,这个对象就叫做Zombie Objects(僵尸对象)。这种方案的重点就是将释放的对象,全都转为僵尸对象
2.Malloc Scribble ,其官方解释如下:申请内存 alloc 时在内存上填0xAA,释放内存 dealloc 在内存上填 0x55,当访问到对象程序就会出现异常
44.runtime相关的问题
https://juejin.cn/post/6844904121531809806
45.notification跨线程通知
addObserverForName:(NSString)nameobject:(id)objqueue:(NSOperationQueue)queue 可以载mainqueue主线程中执行任务
NSNotification接受线程是基于发送消息的线程的,也就是要同步。默认的情况是子线程发出的通知,执行代码也会在子线程中。有些UI相关的操作需要将执行代码放到主线程执行
46.iOS App之间的通信方式
https://juejin.cn/post/6844903752328019975
URL Scheme、UIPasteboard、UIDocumentInteractionController、App Groups
47.mvc、mvp、mvvm的原理
https://juejin.cn/post/6844903508521680909
48.runloop的原理
https://juejin.cn/post/6844904110991360008 简单的说RunLoop是一种高级的循环机制,让程序持续运行,并处理程序中的各种事件,让线程在需要做事的时候忙起来,不需要的话就让线程休眠。
49.gcd是否可以取消线程
GCD原生并不支持取消操作。
dispatch_suspend函数也只能暂停开启新的未执行的block,已经处于执行中的block是无法暂停的。
但是,通过参考NSOperation的cancel机制,你只要加一个外边变量,用于标记block是否需要取消。然后block中通过及时的检测这个外部变量的状态,当发现需要取消时,停止block中的后续操作,释放资源。就能达到及时取消block的目的。这里有个例
50.工厂模式的具体应用场景?
简单来讲就是共同继承与一个父类,子类的创建写在父类中,通过传递不同的类型参数得到不同的子类。父类可以定义通用的行为,子类差异化去复写.
51.如何自己实现mvvm中数据和视图的绑定?除了rac
rac本质上是通过kvo来做的。
52.项目中什么情况产生崩溃,如何去避免崩溃
53.野指针怎么去检测,说说僵尸模式是干了什么?
54.dispatch_after延时执行是否是准确的?gcd的timer是否是准确的?
dispatch_after 方法并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after 方法是很有效的
55.什么情况会导致死锁?
本身是在同步队列1中,内部的任务执行又在同步队列中执行。这样会导致内部的任务在等外面的执行结束,外部的又在等内部任务执行结束 这样就导致了死锁。会导致崩溃
56.runtime的关联属性什么时候会被释放?
当对象引用计数为0也就是调用dealloc的时候,会清理关联对象、释放与c++相关的内存、清除weak引用
57.用户账号加密存储怎么做,能用md5吗?
使用base64避免明文传输 md5是不可逆的 不能使用