@@ -121,7 +121,7 @@ AVSystemController_SystemVolumeDidChangeNotification; object = <AVSystemControll
121121
122122偶然看到 ` Price Tag ` 有个替换应用图标的功能,如图,研究了一下。
123123
124- ![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/3-1.jpg ?raw=true )
124+ ![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/3-1.png ?raw=true )
125125
126126这个功能是在 ` iOS 10.3 ` 后新增的,主要的 ` API ` 如下所示:
127127
@@ -161,10 +161,12 @@ AVSystemController_SystemVolumeDidChangeNotification; object = <AVSystemControll
161161除了调用 ` API ` 外,最主要的还需要在 ` info.plist ` 中配置 ` CFBundleIcons ` 项,这是一个字典,可包含 ` CFBundlePrimaryIcon ` 、` CFBundleAlternateIcons ` 、` UINewsstandIcon ` 三个键。
162162
163163` CFBundlePrimaryIcon ` 为主图标,即 ` Assets.xcassets ` 中 ` AppIcon ` 的信息,一般置空。` CFBundleAlternateIcons ` 即用于设置替换图标,具体的配置项描述可以参考[ 官方文档] ( https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html ) ,通常的配置如图所示。
164- ![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/3-2.jpg?raw=true )
164+
165+ ![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/3-2.png?raw=true )
165166
166167这里需要注意的是,替换图标应该放在工程的某个目录下,而不放在 ` Assets.xcassets ` 中,如图所示。
167- ![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/3-3.jpg?raw=true )
168+
169+ ![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/3-3.png?raw=true )
168170
169171
170172iOS 关于音频播放调研
@@ -176,7 +178,7 @@ iOS 关于音频播放调研
176178由于最近做音频方面的工作,就调研了一下关于音频播放的一些知识,中间也走过不少弯路,希望这篇小集能对关注我们的同学一点启示,少走一些弯路。最后提供一份我看过的资料。这里关于音频播放简单做一个总结。iOS 中音频播放有以下 5 种方式(如果你有更多的方式告诉我,非常感激),它们的使用场景各不同。
177179
178180[ 1] 播放小于 30s 的音频:
179- AudioServicesPlaySystemSound 可以播放小于等于30s的音频 ,主要用于播放一些提示音,你可以利用 AudioServicesPlaySystemSoundWithCompletion 的值播放完成的 callback。它有以下特点:
181+ ` AudioServicesPlaySystemSound ` 可以播放小于等于 ` 30s ` 的音频 ,主要用于播放一些提示音,你可以利用 ` AudioServicesPlaySystemSoundWithCompletion ` 的值播放完成的 ` callback ` 。它有以下特点:
180182
181183- 使用系统音量,不能修改播放音量;
182184- 立刻开始播放,不能暂停;
@@ -186,20 +188,20 @@ AudioServicesPlaySystemSound 可以播放小于等于30s的音频,主要用于
186188
187189[ 查看更多的系统声音ID] ( http://iphonedevwiki.net/index.php/AudioServices )
188190
189- [ 2] AVAudioPlayer 播放本地的音频,或者已加载到内存中的音频流,主要用于播放本地的一些音频文件。注意它不能播放网络音频。它有以下特点:
191+ [ 2] ` AVAudioPlayer ` 播放本地的音频,或者已加载到内存中的音频流,主要用于播放本地的一些音频文件。注意它不能播放网络音频。它有以下特点:
190192
191193- 可以从任意位置播放,可快进,快退;
192194- 可以循环播放;
193195- 可以同时播放多个音频;
194196- 可以控制播放速率;
195197
196- [ 3] AVPlayer 可以播放本地和网络音频,也可以播放视频,它支持流媒体播放,也就是说我们可以用它来做边下别播的使用场景。
198+ [ 3] ` AVPlayer ` 可以播放本地和网络音频,也可以播放视频,它支持流媒体播放,也就是说我们可以用它来做边下别播的使用场景。
197199
198- [ 4] AVQueuePlayer 是 AVPlayer 的子类,它含有一个队列,主要用来播放一个音视频队列。
200+ [ 4] ` AVQueuePlayer ` 是 ` AVPlayer ` 的子类,它含有一个队列,主要用来播放一个音视频队列。
199201
200- [ 5] Audio Queue 主要用来播放音频,录音,它比较底层,会有更多的控制权,如果 APP 主要功能是基于音频播放,推荐使用这个。
202+ [ 5] ` Audio Queue ` 主要用来播放音频,录音,它比较底层,会有更多的控制权,如果 ` APP ` 主要功能是基于音频播放,推荐使用这个。
201203
202- 总的来说,如果普通的本地音频播放,可以选择 AVAudioPlayer ,这个不需要了解更多的音频知识,就可以达到一个基本的播放;如果想做流媒体播放,建议使用 AVPlayer + Local Server 的方式,类似于唱吧目前开源的方式。当然也可以选择 Audio Queue,不过这个难度比较高,需要对音频播放有一个整体的了解,推荐使用三方库 FreeStream,不过需要一些 C++ 的知识,因为使用过程中有一些坑需要填,这样不得不阅读源码。最后推荐一些不错的文章。
204+ 总的来说,如果普通的本地音频播放,可以选择 ` AVAudioPlayer ` ,这个不需要了解更多的音频知识,就可以达到一个基本的播放;如果想做流媒体播放,建议使用 ` AVPlayer + Local Server ` 的方式,类似于唱吧目前开源的方式。当然也可以选择 ` Audio Queue ` ,不过这个难度比较高,需要对音频播放有一个整体的了解,推荐使用三方库 ` FreeStream ` ,不过需要一些 ` C++ ` 的知识,因为使用过程中有一些坑需要填,这样不得不阅读源码。最后推荐一些不错的文章。
203205
204206[ 官方 Audio Queue] ( https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioQueueProgrammingGuide/AQPlayback/PlayingAudio.html#//apple_ref/doc/uid/TP40005343-CH3-SW1 )
205207
@@ -212,36 +214,43 @@ AudioServicesPlaySystemSound 可以播放小于等于30s的音频,主要用于
212214
213215iOS中NSArray/NSSet的一些巧妙用法
214216--------
217+
215218** 作者** : [ Vong_HUST] ( https://weibo.com/VongLo )
216219
217220最近用到很多操作集合类型的方法,这里总结分享一下,也欢迎大家一起补充。
218221
219- 1、假设我们已经有一个 ` NSArray<Model *> ` 类型的数组,但是我们想把这个数组中的 ` Model ` 的某个属性取出组成一个新的数组,一般情况下可能是直接去遍历,但是 ` NSArray/NSSet ` 有一个更便捷的方法 ` valueForKey: ` ,可以快速取出对应属性组成的数组。但是有个问题就是这个方法的效率比for循环低,数据量不大的时候使用还是没有问题的。如下面两张图
222+ * 假设我们已经有一个 ` NSArray<Model *> ` 类型的数组,但是我们想把这个数组中的 ` Model ` 的某个属性取出组成一个新的数组,一般情况下可能是直接去遍历,但是 ` NSArray/NSSet ` 有一个更便捷的方法 ` valueForKey: ` ,可以快速取出对应属性组成的数组。但是有个问题就是这个方法的效率比 ` for ` 循环低,数据量不大的时候使用还是没有问题的。如下面两张图:
223+
220224![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/4-1.jpg?raw=true )
225+
221226![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/4-2.jpg?raw=true )
222227
223- 2、要取两个数组的交集的时候,可以先将 ` NSArray ` 转换成 ` NSMutableSet ` ,再通过取二者交集即可。但是需要注意一点的是数组中的元素最好复写一下 ` isEqual: ` 和 ` hash ` 方法,保证取交集后的结果是正确的。
224- 3、要将数组内元素排序或者过滤等操作,可以结合 ` NSSortDescriptor ` 和 ` NSPredicate ` 使用,可以避免掉大量冗余的 ` for ` 循环之类的代码。关于 ` NSPredicate ` 的用法可以参考 [ NSHipster] ( http://nshipster.com/nspredicate/ ) 和 Realm 的 [ Cheetsheet] ( https://academy.realm.io/posts/nspredicate-cheatsheet/ )
225- 4、关于图中 ` valueForKey: ` 的参数为什么不直接用 ` @"name" ` 而是用 ` NSStringFromSelector(@selector(name)) ` ,是因为
226- 后者会有代码提示可以避免硬编码带来的错误,同时后续该 ` key ` 换名字了之后,会有对应的警告。这个也是从 AFNetworking 中学到的。如图
228+ * 要取两个数组的交集的时候,可以先将 ` NSArray ` 转换成 ` NSMutableSet ` ,再通过取二者交集即可。但是需要注意一点的是数组中的元素最好复写一下 ` isEqual: ` 和 ` hash ` 方法,保证取交集后的结果是正确的。
229+
230+ * 要将数组内元素排序或者过滤等操作,可以结合 ` NSSortDescriptor ` 和 ` NSPredicate ` 使用,可以避免掉大量冗余的 ` for ` 循环之类的代码。关于 ` NSPredicate ` 的用法可以参考 [ NSHipster] ( http://nshipster.com/nspredicate/ ) 和 ` Realm ` 的 [ Cheetsheet] ( https://academy.realm.io/posts/nspredicate-cheatsheet/ )
231+
232+ * 关于图中 ` valueForKey: ` 的参数为什么不直接用 ` @"name" ` 而是用 ` NSStringFromSelector(@selector(name)) ` ,是因为后者会有代码提示可以避免硬编码带来的错误,同时后续该 ` key ` 换名字了之后,会有对应的警告。这个也是从 ` AFNetworking ` 中学到的。如图所示:
233+
227234![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/4-3.jpg?raw=true )
228235
229236对清除图片缓存的思考
230237--------
231238
232239** 作者** : [ 高老师很忙] ( https://weibo.com/517082456 )
233240
241+ 众所周知,使用 ` +[UIImage imageNamed:] ` 方法加载图片是会进图片缓存的,清除缓存是系统触发,并没有为我们提供API;使用 ` +[UIImage imageWithContentsOfFile:] ` 方法加载图片是不会进入图片缓存的。如果想要有图片缓存机制,并且能手动清除图片缓存,我们可以这样做:
234242
235- 众所周知,使用` +[UIImage imageNamed:] ` 方法加载图片是会进图片缓存的,清除缓存是系统触发,并没有为我们提供API;使用` +[UIImage imageWithContentsOfFile:] ` 方法加载图片是不会进入图片缓存的。如果想要有图片缓存机制,并且能手动清除图片缓存,我们可以这样做:
243+ 从 ` +[UIImage imageWithContentsOfFile:] ` 方向下手:
244+ 我们可以自己维护一套图片缓存,` Swizzle +[UIImage imageWithContentsOfFile:] ` 方法加入缓存机制。加载图片后,加入到 ` NSCache ` 缓存,再次取该图片时,优先取 ` NSCache ` 内的缓存,如果缓存内没有再去真正加载。` NSCache ` 在 ` Memory Warning ` 的时候会自动清除缓存,我们也可以使用 ` -[NSCache removeAllObjects] ` 手动清除缓存。当然,你也可以不使用 ` Swizzle ` ,写一个 ` Manager ` 也是可以的,我只是提供一种思路。
236245
237- 从` +[UIImage imageWithContentsOfFile :] ` 方向下手:
238- 我们可以自己维护一套图片缓存, ` Swizzle +[UIImage imageWithContentsOfFile :]` 方法加入缓存机制。加载图片后,加入到NSCache缓存,再次取该图片时,优先取NSCache内的缓存,如果缓存内没有再去真正加载。NSCache在Memory Warning的时候会自动清除缓存,我们也可以使用 ` -[NSCache removeAllObjects] ` 手动清除缓存。当然,你也可以不使用Swizzle,写一个Manager也是可以的,我只是提供一种思路 。
246+ 从 ` +[UIImage imageNamed :] ` 方向下手:
247+ 在 ` Memory Warning ` 或进入后台时,系统会自动帮我们清除使用 ` +[UIImage imageNamed :] ` 的图片缓存。我们也可以通过模拟发送 ` UIApplicationDidReceiveMemoryWarningNotification ` 或 ` UIApplicationDidEnterBackgroundNotification ` 来清除图片缓存,风险可以根据实际情况来评估 。
239248
240- 从` +[UIImage imageNamed:] ` 方向下手:
241- 在Memory Warning或进入后台时,系统会自动帮我们清除使用` +[UIImage imageNamed:] ` 的图片缓存。我们也可以通过模拟发送` UIApplicationDidReceiveMemoryWarningNotification ` 或` UIApplicationDidEnterBackgroundNotification ` 来清除图片缓存,风险可以根据实际情况来评估。
242- 还可以从私有API来下手,` +[UIImage imageNamed:] ` 系统底层是通过` UIAssetManager ` 来管理图片缓存的,如图1,如图2,我们可以模拟调用` _clearCachedResources ` 方法来实现清除缓存。
249+ 还可以从私有API来下手,` +[UIImage imageNamed:] ` 系统底层是通过 ` UIAssetManager ` 来管理图片缓存的,如下两图所示,我们可以模拟调用 ` _clearCachedResources ` 方法来实现清除缓存。
243250
244251如果有其他思路的,欢迎提出!
245252
246253![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/5-1.jpg?raw=true )
247254![ ] ( https://github.com/southpeak/iOS-tech-set/blob/master/images/2018/01/5-2.jpg?raw=true )
255+
256+
0 commit comments