iOS摸鱼周报 第三十八期

iOS摸鱼周报 第三十八期

本期概要

  • 话题: Xcode 13.2 和 iOS 15.2 的正式版值得关注的几项新功能。
  • Tips:内存相关的一些机制。
  • 面试模块:dealloc 在哪个线程执行;NSString *str = @”123” 这里的 str 和 “123” 分别存储在哪个区域。
  • 优秀博客:SwiftUI 相关的几篇优秀博客。
  • 学习资料:软件随想录,Joel Spolsky 的 Blog 纸质版文集。
  • 开发工具:Dropshelf,一款 Mac OS 下的拖拽效率应用。

本期话题

@zhangferry:12 月 13 号,Apple 发布了 Xcode 13.2 和 iOS 15.2 的正式版。其中有几项新功能值得关注。

Xcode 13.2

编译系统和 Swift 编译器有了一个新模式可以充分利用 CPU 核心,以达到优化 Swift 项目的效果。该模式可选,可以执行如下命令打开该模式:

1
defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1

iOS 15.2

是关于 StoreKit 的新特性:

  • StoreKit 中展示退款请求的弹窗可以在 Xcode 中进行测试了。UIKit 模式下可利用:beginRefundRequest(in:) 或者 beginRefundRequest(for:in:) ,SwiftUI 下可利用 refundRequestSheet(for:isPresented:onDismiss:) 作为视图修饰器。
  • StoreKit 中管理订阅的弹窗也可以在 Xcode 中进行测试了。 UIKit 模式下课利用 showManageSubscriptions(in:) ,SwiftUI 下可利用 manageSubscriptionsSheet(isPresented:) 作为视图修饰器完成。
  • 新的 SKTestSession.TimeRate 值可用于 StoreKit Test 模块的自动化测试。

开发Tips

整理编辑:zhangferry

内存相关的一些机制

虚拟内存寻址

为了安全性,防止物理内存被篡写(还有其他很多优势),操作系统引入了虚拟内存机制,虚拟内存是对物理内存的映射,操作系统会为每个进程提供一个连续并且私有的虚拟内存空间。

实际的数据读写首先要通过虚拟地址找到对应的物理地址,这个过程就是 CPU 寻址,CPU 寻址由位于 CPU 的 MMU(Memory Management Unit 内存管理单元)负责。

为了便于管理,虚拟内存被分割为大小固定的虚拟页(Virtual Page, VP)。程序加载过程中,虚拟内存由磁盘到内存的映射是以页为单位进行处理的。每次映射完成都会对应一个关联的物理内存地址,为了管理这个映射关系出现了页表条目(Page Table Entry)PTE 的一个数据表。这个页表条目里有一个标记位比特位,0 表示还未加载到内存,1 表示已经加载到内存。当访问到 0 就出产生缺页(Page Fault),之后会填充数据到内存,并修改这个标记位。

这个 PTE 通常是位于高速缓存或者内存中的,但即使是高速缓存它相对于 CPU 的读取速度仍然是很慢的。后来又引入了后备缓冲器(Translation Lookaside Buffer,TLB),这个缓存器位于 CPU 内部,其存储了 PTE 内容。虽然它的存储空间较小,但因为在 CPU 内部访问很快,由局部性原理来说这个处理仍是非常值得的。

汇总一下寻址流程如下图所示:

内存不足的处理

在 Linux 中虚拟内存空间是大于实际物理内存地址的,这就会出现一个状况,当内存物理地址不够用时会发生什么?实际操作系统会将物理内存中的部分内容迁移到磁盘中,然后腾出地方给申请内存方使用,这个过程叫 Swap Out。当又要使用那部分内存时会触发 Swap Int,再移出部分内存,将需要的内容映射到空缺内存空间里。这个机制的好处是可以使用更大的内存地址,但坏处也很明显就是 Swap 会造成较大性能损耗。

iOS 处理机制

iOS 没有 Disk Swap 机制,因为其本身磁盘空间相对电脑是比较小的,而且频繁读取闪存会影响闪存寿命。基于此 iOS 设备可申请内存地址是有限度的,关于各个设备所能允许申请的最大内存,Stack OverFlow 有人做过测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
device: (crash amount/total amount/percentage of total)

iPhone5: 645MB/1024MB/62%
iPhone5s: 646MB/1024MB/63%
iPhone6: 645MB/1024MB/62% (iOS 8.x)
iPhone6+: 645MB/1024MB/62% (iOS 8.x)
iPhone6s: 1396MB/2048MB/68% (iOS 9.2)
iPhone6s+: 1392MB/2048MB/68% (iOS 10.2.1)
iPhoneSE: 1395MB/2048MB/69% (iOS 9.3)
iPhone7: 1395/2048MB/68% (iOS 10.2)
iPhone7+: 2040MB/3072MB/66% (iOS 10.2.1)
iPhone8: 1364/1990MB/70% (iOS 12.1)
iPhone X: 1392/2785/50% (iOS 11.2.1)
iPhone XS: 2040/3754/54% (iOS 12.1)
iPhone XS Max: 2039/3735/55% (iOS 12.1)
iPhone XR: 1792/2813/63% (iOS 12.1)
iPhone 11: 2068/3844/54% (iOS 13.1.3)
iPhone 11 Pro Max: 2067/3740/55% (iOS 13.2.3)

以 iPhone 11 Pro Max 为例,应用可申请内存为 2067M,占系统内容的 55%,这已经是很高了。但即使这样,仍会出现内存过高的情况,iOS 系统的处理主要是清理 + 压缩这两个方案。

Clean Page & Dirty Page

iOS 将内存页分为 Clean Page 和 Dirty Page,Clean Page 一般是固定内容,可以被系统回收,需要时从磁盘再加载回来。

上图可以看出,写入数据前申请内存为 Clean 内存,使用的部分就变成了 Dirty 内存。

Compressed Memory

iOS 还有另一种机制是压缩内存(Compressed Memory),这也是一种 Swap 机制。举个例子,某个 Dictionary 使用了 3 个 Page 的内存,如果一段时间没有被访问同时内存吃紧,则系统会尝试对它进行压缩从 3 个 Page 压缩为 1 个 Page 从而释放出 2 个 Page 的内存。但是如果之后需要对它进行访问,则它占用的 Page 又会变为 3 个。

这部分内存可以被 Instrument 统计到,对应的就是 VM Tracker 里的 Swapped Size:

参考:

面试解析

整理编辑:zhangferry

dealloc 在哪个线程执行

在回答这个问题前需要了解 dealloc 在什么时机调用,dealloc 是在对象最后一次 release 操作的时候进行调用的,我们可以查看 SideTable 管理引用计数对应的 release 源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
ASSERT(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];

bool do_dealloc = false;

table.lock();
auto it = table.refcnts.try_emplace(this, SIDE_TABLE_DEALLOCATING);
auto &refcnt = it.first->second;
if (it.second) {
do_dealloc = true;
} else if (refcnt < SIDE_TABLE_DEALLOCATING) {
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
refcnt |= SIDE_TABLE_DEALLOCATING;
} else if (! (refcnt & SIDE_TABLE_RC_PINNED)) {
refcnt -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc && performDealloc) {
// 可以释放的话,调用dealloc
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return do_dealloc;
}

这里可以看出 dealloc 的调用并没有设置线程,所以其执行会根据触发时所在的线程而定,就是说其即可以是子线程也可以是主线程。这个也可以很方便的验证。

NSString *str = @”123” 这里的 str 和 “123” 分别存储在哪个区域

可以先做一下测试:

1
2
3
NSString *str1 = @"123"; // __NSCFConstantString
NSLog(@"str1.class=%@, str1 = %p, *str1 = %p", str1.class, str1, &str1);
// str1.class=__NSCFConstantString, str1 = 0x1046b8110, *str1 = 0x7ffeeb54dc50

这时的 str1 类型是 __NSCFConstantString,str1 的内容地址较短,它代表的是常量区,指向该常量区的指针 0x7ffeeb54dc50 是在栈区的。

再看另外两种情况:

1
2
3
4
5
6
7
NSString *str2 = [NSString stringWithFormat:@"%@", @"123"];
NSLog(@"str2.class=%@, str2 = %p, *str2 = %p", str2.class, str2, &str2);
// str2.class=NSTaggedPointerString, str2 = 0xe7f1d0f8856c5253, *str2 = 0x7ffeeb54dc58

NSString *str3 = [NSString stringWithFormat:@"%@", @"iOS摸鱼周报"]; //
NSLog(@"str3.class=%@, str3 = %p, *str3 = %p", str3.class, str3, &str3);
// str3.class=__NSCFString, str3 = 0x600002ef8900, *str3 = 0x7ffeeb54dc30

这里的字符串类型为 NSTaggedPointerString__NSCFString,他们的指针都是在栈区,这三个对象的指针还是连续的,内容部分,前者在指针里面,后者在堆区。(栈区地址比堆区地址更高)

这里再回顾下内存的分区情况,大多数情况我们只需关注进程的虚拟内存就可以了:

优秀博客

整理编辑:东坡肘子我是熊大

1、SwiftUI 视图的生命周期研究 – 来自:东坡肘子

@东坡肘子:在 UIKit(AppKit)的世界中,通过框架提供的大量钩子(例如 viewDidLoad、viewWillLayoutSubviews 等),开发者可以将自己的意志注入视图控制器生命周期的各个节点之中,宛如神明。在 SwiftUI 中,系统收回了上述的权利,开发者基本丧失了对视图生命周期的掌控。不少 SwiftUI 开发者都碰到过视图生命周期的行为超出预期的状况(例如视图多次构造、onAppear 无从控制等)。本文将作者对 SwiftUI 视图、SwiftUI 视图生命周期的理解和研究做以介绍,供大家一起探讨。

2、探究视图树 – 来自:Javier

@东坡肘子:大多 SwiftUI 的开发者都已经熟练掌握了如何从父视图向子视图传递数据的方法,但如何获取子视图的状态和变化对很多人仍然比较陌生。swiftui-lab 的 Javier 写了三篇文章详细介绍了如何通过 PreferenceKey、AnchorPreferences 等方式向上传递数据的手段。链接中提供的是 Swift 花园的中文译本。

3、SwiftUI 中的 Text 插值和本地化 – 来自:onevcat

@东坡肘子:Text 是 SwiftUI 中最简单和最常见的 View 了,相较 String,Text 提供了更加丰富的差值和本地化功能。本文不仅介绍了 Text 中关于差值和本地化的一些特色功能,并讲解了在 Text 中创建自定义差值的方法。

4、TCA - SwiftUI 的救星?(一) – 来自博客:onevcat

@我是熊大:SwiftUI 似乎可以真正走向前台,成为开发利器,本文是 TCA - SwiftUI 系列的第一篇文章。

5、SwiftUI Tab Bar – 来自博客:objc.io

@我是熊大:当你信心满满的准备用 SwiftUI 开发 App 时,可能会被困在第一步:Tab Bar 样式怎么处理,本文将替你解惑。

学习资料

整理编辑:Mimosa

软件随想录

地址https://book.douban.com/subject/4163938/

Joel Spolsky 的 Blog 纸质版文集,中文版由阮一峰翻译。作者 Joel Spolsky 毕业于耶鲁大学,曾任微软公司 Excel 开发小组项目经理,现在自己创业做 CEO,同时也是 Stack Overflow 的合伙人。他在书中以诙谐幽默的笔触将自己在软件行业的亲身感悟娓娓道来,其中包含关于软件、人才、创业和管理的很多看法。需要提醒读者的是本书的大部分内容都写于 2004 年底左右,是一本老书了,但其中很多内容都值得细细品味。这边节选一些有意思的观点供没看过的读者过过瘾:

  1. 从数量上来说,优秀的人才很少,而且从不出现在招聘市场上。
  2. 我从来没有见过哪个能用 Scheme 语言、Haskell 语言、C 语言中的指针函数编程的人,竟然不能在两天里面学会 Java,并且写出的 Java 程序质量竟然不能胜过那些有 5 年 Java 编程经验的人士。
  3. 看东西的时候,你的视力只是在你的视野中很小一块区域是高分辨率的,而且视野中央还有相当大的一个盲点。但是,你依然想当然的认定你能够超清晰的看清视野中的每一个角落。
  4. 别担心所有工作都被印度人抢走。😁

工具推荐

推荐来源:iOSleep

Dropshelf

地址https://pilotmoon.com/dropshelf/

软件状态:之前付费但是目前下架了,可以使用上面链接免费使用。

软件介绍

Dropshelf 是一款 Mac OS 下的拖拽效率应用。它提供了一个可以吸附在屏幕边缘的 Dock,你可以拖拽任何东西「图片、文件、文字、链接…」暂存到 Dock 中,方便你在其他 App 中来使用。

Dropshelf

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS摸鱼周报 第三十七期

iOS摸鱼周报 第三十六期

iOS摸鱼周报 第三十五期

iOS摸鱼周报 第三十四期

人物访谈 | 一位普通的 iOS 程序员

人物访谈 | 一位普通的 iOS 程序员

夏天是摸鱼周报的一位主编,主要负责 Tips 部分的编写和审核。他曾参与掘金翻译计划,翻译过很多篇优质博客内容,也翻译过像是 iOS Crash Dump Analysis Book 这一类的外文图书。今年拉他来聊一聊关于外文翻译和他个人的一些情况。

简单介绍下自己吧

哈喽,各位摸鱼周报的读者好,我是夏天,是一位双 iOS 程序员。目前在摸鱼周报里面摸鱼

一般优秀的英文文章,不会马上就有人翻译,所以,如果想获取一手英文资讯,有什么有趣或者必看的外国网站推荐吗

一般都是通过MediumRealm 之类的网站,然后注册一下邮箱,接收推送,没事翻两篇。

看到好文章时,看看他们的参照了谁,参照作者可能也有好的文章。

之前翻译过不少内容,像是:《iOS Crash Dump Analysis Book》,是出于什么理由进行翻译的呢?翻译这个长篇有什么收获?

《iOS Crash Dump Analysis Book》这本书,我是在 19 年看到的,其实并没有仔细阅读下去。后来想仔细阅读的时候,发现网上并没有译文,我就申请翻译一下这本书,作者也是很慷慨,给予了这个机会,让我完成了 1.0 和 2.0 的翻译。

我觉得整体翻译下来的收获,大概就是形成体系的去了解一些关于崩溃的结构和作者在进行奔溃分析时的分析途径和方法。整本书是一个概论,虽然不能让你成为一个 crash 分析高手,但给了一个格式,在碰到崩溃的时候,可以按部就班的去进行分析。

为此我还写了一篇观后感iOS 崩溃分析

技术文章翻译的过程中那部分是相对困难的,有没有什么经验可以分享的

第一就是语感,翻译完以后多读几遍,没有 deadline 的翻译,你都可以翻译完以后,过几天再去读,再优化一下,至少读到你自己不变扭,能通顺为止。

第二就是实践,虽然里面很多技术你不能去实践,但是你可以去了解。文章的引申阅读和搜索一下相关技术。我在写 字节跳动移动研发工具链 - MBox 一文的时候,也是尽量保留原作者的本意,去了解对应的技术及其实践,进行再创作。

听说你最近一直在写前端需求,前端和iOS有什么区别?iOS开发想往前端过渡的话,有哪些注意事项?

写的都比较简单,都是偏业务方面的。思想上和逻辑上没有大的差异,唯一需要注意的是,如果你接手别人的项目时,一定要心平气和,因为前端很坑爹的是怎么写都对

学点CSS,学点JS,就可以开始过渡了,期间体系化的学习一下。

期待你在一家好的公司,什么东西都有,然后你顺着经验就可以开始夯实之路。

利用公司的项目锻炼自己,可以根据现有的模板,从零开始,从第一个组件,到第一个业务模块等等。

对于“大前端”这个词,你怎么看?随着跨平台、Web WebAssembly等技术发展,会不会统一成一个工种?

我理解是为用户体验负责,各种与用户有直接接触的都算是大前端,不过这可能更偏向于程序员这一职业。

前端程序员几乎都做着类似的事情,差不多的开发流程,页面埋点,发布流程,项目持续集成,页面性能监控 APM ,工程化,组件化…

技术永远都有差别,都是往前也不一定是同向。

也许未来这些为页面服务的人员可能会慢慢的变成一类,但是其中的某些人难以忍受这种越来越同化的工作又会开始异化出新的种类。

技术不停步,工种就永远有差异。

在这几年的iOS生涯里有什么比较大的收获?你怎么看待程序员这个行业?

没看到有啥收获,连个只能被铭记的功能都没有。

程序员这个行业,是一个注重基础和思考的行业,用数据结构和设计模式规范具体的行为,注重实践动手能力。

这个行业需要你一直学习,终身学习。

不应过分功利,需要一定的职业操守和道德。

自己在工作中有没有什么好习惯或者高效的工作方法可以分享的

解决问题的方式不要困于一种途径,交流能让你成为更好的人,多出去分享去交流,可以使人进步。

学到什么一定要做出来, 写下来,讲出去。

最近的一个生活感悟是什么

努力,生活总有好事发生。准备迎接好事发生

偶尔咸鱼也没关系,躺着是很舒服,但是还是要动一动的,不然真的会腰酸背痛

你的公众号感觉最近更新少了,后续还会恢复吗?

不更新的原因有很多:

  • 创作热情:在更新一段时间后,读者的增长量没有达到自己的预期,心理有些懈怠

  • 创作质量:期望与创造出真正有价值的东西,但是囿于能力等因素,既不想水,也写不出好文章

  • 创作方向:平常的工作更多是业务相关的,作为一名 iOSer 并没有自己固定擅长的地方,想像书籍一样写点东西,但是不太清楚具体些什么。

最最主要的原因就是懒

后续还会恢复更新的。目前在进行 Swift 的学习,之前也开了一个Swift 100 Days的专题,还在想怎么在里面添砖加瓦,丰富成一个系列

有什么想借助摸鱼周报宣传的

希望大家支持我的公众号:iOS成长指北,也希望大家与我交流。

承接各种技术类文章写作,一起交流学习。

iOS摸鱼周报 第三十七期

iOS摸鱼周报 第三十七期

本期概要

  • 话题:苹果正式开放 AppStore 的 2 个重磅功能:自定义产品页和产品页优化。
  • Tips:设备启动流程的梳理。
  • 面试模块:HTTP/1.0,HTTP/1.1,HTTP/2 有哪些区别?
  • 优秀博客:Swift 相关的几个优秀三方库介绍。
  • 学习资料:计算机教育中缺失的一课(中文版)。
  • 开发工具:myyearwithgit,开启你的年度报告。

本期话题

@iHTCboy:12 月 8 号,苹果正式开放 AppStore 的 2 个重磅功能:

  • Custom product pages(自定义产品页)
  • Product page optimization(产品页优化)

默认 App Store Connect 后台是没有这些功能,开发者需要通过 Updated App Store Submission 页面接受苹果的内测协议后,才能使用这些功能。

具体的功能介绍可以参考苹果文档:Product Page OptimizationCustom Product Pages

App Store Connect 后台的操作文档,可以参考:配置自定产品页产品页优化

推荐有推广需求的开发者尝试使用,通过自定义产品页来突出 app 不同的功能和不同用户群体的喜好,通过测试不同图标、商店图和视频的效果,增加 App 的曝光量和提高用户下载意愿等。

开发Tips

整理编辑:zhangferry

设备启动流程

我们对 App 的启动流程通常会关注比较多,而忽视设备的启动流程,这次来梳理一下设备的启动流程。设备的启动流程分两类:OS X 和 iOS 等 i系列设备,过程大致如下图所示:

开机

按开机键,激活设备,此时硬件通电,CPU 就可以开始工作了。

启动引导

启动引导即是引导至系统内核的加载。

OS X 通过 EFI 进行引导,EFI 是相对 BIOS 的一种升级版设计,它有多种功能,像是引导加载器、驱动程序等。OS X 中的 boot.efi 就是一个引导加载器,由它激活 OS 的内核程序。

i 系列设备没有采用 EFI 的方案,而使用了一种更加注重安全性的设计。i 系列设备内部有一个引导 ROM,这个 ROM 是设备本身的一部分。引导 ROM 会激活 LLB(Low Level Bootloader 底层加载器),LLB 这一步开始就有了签名校验。这一步完成之后会进入 iBoot 阶段。iBoot 是内核的引导加载器,由它来加载内核。

(iOS 系列设备的升级其实还有一个 DFU 升级的流程,为了简化这里略过。)

launchd

Launchd 是用户态的第一个进程,由内核直接启动,其 pid=1,位于如下路径,该路径会被硬编码到内核中:

1
2
$ ll /sbin/launchd
-rwxr-xr-x 1 root wheel 857K Jan 1 2020 /sbin/launchd

launchd 的主要任务就是按照预定的设置加载其他启动需要的程序。这类预定的任务分为守护进程(daemon)和代理进程(agent)。

launchd 不仅是负责这些启动必备进程,很多用户使用中的进程,像是我们点击应用图标所创建的进程也是由它处理的。

守护进程 & 代理进程

守护进程是与用户登录无关的程序。代理进程是用户登录之后才加载的程序。

iOS 没有用户登录的概念,所以只有守护进程。这些启动进程会被放到 plist 文件里:

1
2
3
4
5
/System/Library/LaunchDaemons #系统守护进程plist文件
/System/Library/LaunchAgents #系统代理进程plist文件
/Library/LaunchDaemons #第三方守护进程plist文件
/Library/LaunchAgents #第三方代理进程plist文件
~/Library/LaunchAgents #用户自由的代理程序plist文件,用户登录时启动

Finder 是 OS X 的代理进程,用于提供图形功能,配合 Dock 我们就能看到 Mac 的桌面了。

在 iOS 上与之对应的就是 SpringBoard,它就是 iPhone 的桌面进程。

到这一步就算是设备启动完成了!

参考:《深入解析 MAC OS X & IOS 操作系统》

面试解析

整理编辑:夏天

面试题:HTTP/1.0,HTTP/1.1,HTTP/2 有哪些区别?

什么是 HTTP

HTTP(超文本传输协议,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。其默认使用 80 端口,由 HTTP 客户端发起一个请求,建立一个到服务器指定端口(默认是 80 端口)的 TCP 连接。

HTTP/1.0

老的 HTTP 协议标准是 HTTP/1.0,为了提高系统的效率,HTTP/1.0 规定浏览器与服务器只保持短暂的连接,每次请求都需要与服务器建立一个 TCP 连接,服务器完成请求处理后立即断开 TCP 连接,服务器不会跟踪客户也不会记录已经请求过的请求。

这就是 HTTP/1.0 的两个主要特性:

  • 无状态:服务器不跟踪不记录请求过的状态
  • 无连接:每次请求都需要建立 TCP 连接

对于 无状态 来说,可以通过设置 cookieseesion 等机制来实现身份校验和状态记录。

影响一个 HTTP 网络请求的因素主要有两个:带宽延迟。 当下,网络设施的逐渐完善使得带宽问题得到较好的解决,从而延迟成为主要的影响因素。

HTTP/1.0 无连接 特性导致两种了性能缺陷:

  • 连接无法复用

    连接无法复用会导致每次请求都需要进行一次 TCP 连接(即 3 次握手 4 次挥手)和慢启动,降低了网络使用率。

  • 队头阻塞

    在前一个请求响应到达之后下一个请求才能发送,如果前一个阻塞,后面的请求也会阻塞的。这会导致带宽无法被充分利用,以及后续健康请求被阻塞。

HTTP/1.1

为了消除 HTTP/1.0 标准中的歧义内容和提升性能,我们很快的就过渡到了 HTTP/1.1 标准,也是当前使用最为广泛的 HTTP 协议 :

  • 默认支持长连接:在 Header 中新增 Connection 参数,其值默认为 Keep-Alive。默认保持长连接,数据传输完成后保持 TCP 连接不断开,可以继续使用这个通道传输数据。

    默认的服务端的长连接时间是 30S。在 iOS 端的实践过程中会有概率出现下面的错误:

    Error Domain=NSURLErrorDomain Code=-1005 “The network connection was lost.

  • HTTP pipeline:基于长连接的基础,在同一个 TCP 连接上可以传送多个 HTTP 请求和响应,减少了建立和关闭连接的消耗和延迟。管道化可以不等第一个请求响应继续发送后面的请求,但响应的顺序还是按照请求的顺序返回。
  • 缓存处理:相较于 HTTP/1.0HTTP/1.1 提供了更为丰富的缓存策略。在 HTTP/1.0 中主要是根据 Header 里的 If-Modified-SinceExpires 来做为判断缓存的标准,HTTP/1.1 则引入了诸如 Entity tagIf-Unmodified-SinceIf-MatchIf-None-Match 等缓存方式。并且还在 Header 中新增了 Cache-control 参数来管理缓存。
  • 断点传输:相较于 HTTP/1.0 无法部分返回数据对象,HTTP/1.1 在 Header 中新增了两个参数来支持请求响应分块,客户端发请求时对应的是 Range,服务器端响应时对应的是 Content-Range
  • Host 头处理HTTP/1.0 认为每台服务器都指向了唯一的 IP 地址,请求消息中的 URL 中并没有主机的信息。在 HTTP/1.1 中新增了 Host 头域,能够使不同域名配置在同一个 IP 地址的服务器上。

HTTP/2

SPDY 协议是 HTTP/2 协议的基础HTTP/2 最大的改进就是从文本协议转变为二进制协议

  • 帧、消息、流和 TCP 连接HTTP/2 将一个 TCP 连接分为若干个流(Stream),每个流中可以传输若干消息(Message),每个消息由若干最小的二进制帧(Frame)组成。HTTP/2 中,每个用户的操作行为被分配了一个流编号(stream ID),这意味着用户与服务端之间创建了一个 TCP 通道;协议将每个请求分割为二进制的控制帧与数据帧部分,以便解析。
  • 多路复用:基于二进制分帧,在同一域名下所有访问都是从同一个 TCP 连接中走,HTTP 消息被分解为独立的帧,无序发送,服务端根据标识符和首部将消息重新组装起来。
  • 请求优先级:为了避免多路复用可能会导致关键请求被阻塞,即利用请求优先级完成高优先级请求先处理。
  • HPACK 算法HTTP/2 引入了头部压缩算法。利用合适的压缩算法来处理消息头的数据。避免了重复 Header 的传输,减小了传输数据的大小。
  • 服务端推送(Server Push):在 HTTP/2 中,服务器可以对客户端的一个请求发送多个响应。

优秀博客

整理编辑:我是熊大东坡肘子

1、适配 SwiftUI 的苹果语言识别封装库 —— SwiftSpeech – 来自:cayZ

@东坡肘子:SwiftSpeech 是苹果语音框架的一个封装器,与 SwiftUI 和 Combine 深度集成。只需几行代码就能实现 UI 控制 + 语音识别功能。SwiftSpeech 的作者是一位国人,提供的范例代码很有中国特色。

2、适用于苹果生态的音频合成、处理分析平台 —— AudioKit – 来自:AudioKit

@东坡肘子:AudioKit 是一个由代码库、包、库、算法、应用程序、playgorunds、测试和脚本组成的整个音频开发生态系统,由音频程序员、应用程序开发人员、工程师、研究人员、科学家、音乐家、游戏玩家和编程新手组成的社区建立和使用。AudioKit 提供了丰富的文档和范例,拥有十分活跃的社区。

3、Swift 编写的相机系统 —— NextLevel – 来自:NextLevel

@东坡肘子:NextLevel 最初是一个周末项目,现在已经发展成为一个由相机平台爱好者组成的开放社区。该软件为管理媒体录制、相机界面定制、手势交互定制和 iOS 上的图像流提供基础组件。同样的功能也可以在 Snapchat、Instagram 和 Vine 等应用中找到。NextLevel 提供了用于捕捉 ARKit 视频和照片的组件,并利用了 NextLevel 现有的记录功能和媒体管理。

4、GPUImage2 – 来自:GitHub

@我是熊大:早在 2016 年,GPUImage 就已经被大家知晓,各大图像处理软件,几乎都基于此开发,可以说是一个里程碑意义的开源库,后续作者又写了 Swift 版本 GPUImage2(OpenGL ES)、GPUImage3(Metal)。

5、神笔马良——基于 Metal 的涂鸦框架 – 来自博客:Harley

@我是熊大:MaLiang 作者是中国人,一个全栈工程师;MaLiang 可以用于开发画板类似的功能,同样也是基于 Metal 开发的,是一个有了 4 年沉淀的开源库,目前 star 1.2k+。

学习资料

整理编辑:Mimosa

计算机教育中缺失的一课(中文版)

地址https://missing-semester-cn.github.io/

大学里的计算机课程通常专注于讲授从操作系统到机器学习这些学院派的课程或主题,而对于如何精通工具这一主题则往往会留给学生自行探索。在这个系列课程中,会讲授命令行文本编辑器Git 提供的多种特性等等。学生在他们受教育阶段以及在他们的未来职业生涯中都会和这些工具朝夕相处。因此,花时间打磨使用这些工具的能力并能够最终熟练地、流畅地使用它们,在计算机教育中也是重要的一个部分。

工具推荐

整理编辑:CoderStar

myyearwithgit

地址https://github.com/Co2333/myyearwithgit

软件状态:开源、免费

软件介绍

看名字大家就知道这个是干啥的,还是配上老王的描述吧:

本程序目前支持使用常见 Git 仓库托管提供商以及本地代码仓库进行分析。

myyearwithgit

往期推荐

iOS摸鱼周报 第三十六期

iOS摸鱼周报 第三十五期

iOS摸鱼周报 第三十四期

iOS摸鱼周报 第三十三期

iOS摸鱼周报 第三十五期

iOS摸鱼周报 第三十五期

本期概要

  • 话题:Typora 开启收费模式,支持!
  • Tips:混编|为 Objective-C 添加枚举宏,改善混编体验。
  • 面试模块:事件传递及响应链。
  • 优秀博客:几篇关于 Swift 字符串的优秀博客。
  • 学习资料:独立开发者经验分享。
  • 开发工具:思源笔记,一款本地优先的个人知识管理系统, 支持细粒度块级引用和 Markdown 所见即所得。

本期话题

@zhangferry:知名 Markdown 编辑软件 Typora ,最近迎来了最新的 1.0 版本(正式版)。这次更新功能上没啥大变化,最大的变化是从之前的免费变为了付费。

这在官方 Github 的 Issus 里早有说明:

Typora is a minimal markdown editor, providing new ways for reading and writing markdown. It is currently in beta.
Typora is commercial software (not open source), but is free during beta. Typora on GitHub supports collaboration between its developer, and its user community, providing a place to transparently report issues, collect feedback and discuss future direction. It also provides open source resources for user customization of themes.

Typora 的口碑一直很好,在各类 Markdown 软件的推荐中基本都是排名首位。我也是 Typora 的重度用户,借助于 Typora 精巧的单栏设置,配合 Orangheart 主题、PicGo 图床插件,可以很方便的输出和排版每篇公众号文章。

面对这次由免费转付费的事情,网上有不少文章都在说放弃 Typora,还推荐了很多平替方案,却少有人站出来说应该支持开发者,多少有一点让人感觉遗憾。

Typora 的收费标准:89 元(可支付宝)、买断制(终身)、最多三台设备(可重置)。这相比很多按年续订,动辄上百的产品,价格上算是良心了,况且 Typora 已经免费 6 年了。

说回付费本身,好的软件就应该是有收益的,有了收益作者能更专注地开发设计产品,更好的产品能给我们带来更多便利,最终还是回馈到用户自身,这是正向循环。单纯为爱发电的事也能持续,但肯定持续不久。所以如果你感觉它好,你喜欢它,那就支持一下吧。

同时我也理解大家并非都是 Typora 重度用户,我保存了收费前的那个版本(v0.11.18),大家如果不小心安装了最新版还想继续白嫖的话可以在公众号后台回复:Typora,下载旧版本。

再来说说 Typora 这个软件吧,其创建于 2014 年,作者 Abner Lee 还是一位国人,为人非常低调。只找到其 2015 年在 Medium 里发的一篇介绍 Typora 设计原由的文章:Introduce Typora — Why another markdown editor 。其对当时 Markdown 工具的分析可谓句句精准,在 Typora 之前的 Markdown 编辑器都是双栏的,而在这之后越来越多 Markdown 工具尝试单栏的编辑模式。好的产品就是能发现痛点,解决痛点,Abner Lee 不仅完美地解决了原有设计的痛点, 还很大程度做了不少创新,真的非常厉害。

希望以后 Typora 能越来越好!

开发Tips

整理编辑:师大小海腾

混编|为 Objective-C 添加枚举宏,改善混编体验

  • NS_ENUM。用于声明简单枚举,将作为 enum 导入到 Swift 中。建议将使用其它方式来声明的 Objective-C 简单枚举进行改造,使用 NS_ENUM 来声明,以更好地在 Swift 中使用。
  • NS_CLOSED_ENUM。用于声明不会变更枚举成员的简单枚举(简称 “冻结枚举” ),例如 NSComparisonResult,将作为 @frozen enum 导入到 Swift 中。冻结枚举以降低灵活性的代价,换取了性能上的提升。
  • NS_OPTIONS。用于声明选项枚举,将作为 struct 导入到 Swift 中。
  • NS_TYPED_ENUM。用于声明类型常量枚举,将作为 struct 导入到 Swift 中。可大大改善 Objective-C 类型常量在 Swift 中的使用方式。
  • NS_TYPED_EXTENSIBLE_ENUM。用于声明可扩展的类型常量枚举。与 NS_TYPED_ENUM 的区别是生成的 struct 多了一个忽略参数标签的构造器。
  • NS_STRING_ENUM / NS_EXTENSIBLE_STRING_ENUM。用于声明字符串常量枚举,建议弃用,使用 NS_TYPED_ENUM / NS_TYPED_EXTENSIBLE_ENUM 替代。在 Xcode 13 中,Apple 已经将原先使用 NS_EXTENSIBLE_STRING_ENUM 声明的 NSNotificationName 等常量类型改为使用 NS_TYPED_EXTENSIBLE_ENUM 来声明。

可以看看:@师大小海腾:iOS 混编|为 Objective-C 添加枚举宏,改善混编体验

官方文档:@Apple:Grouping Related Objective-C Constants

面试解析

整理编辑:师大小海腾

事件传递及响应链

对于 iOS 的事件传递及响应链,你是否还掌握得不够好,推荐阅读我们编辑 @Mim0sa 和 @CoderStar 的这几篇文章以及 Apple 的文档,相信你一定能在面试中所向披靡。

优秀博客

整理编辑:皮拉夫大王在此我是熊大东坡肘子

1、Swift 字符串性能问题 – 来自:JerryChu

@东坡肘子:本文的作者在处理一道 LeetCode 算法题时碰到了 Swift 字符串遍历超时的状况,通过分析 Array 同 String 之间遍历机制的不同,最终获得了优化 String 遍历效率的方法。

2、Swift 的字符串为什么这么难用? – 来自掘金:四娘

@东坡肘子:在其它语言里一个语句就能解决的字符串操作在 Swift 中需要多个,但这些其实都是 Swift 有意而为之的设计。本文通过对 SE-0265 Offset-Based Access to Indices, Elements, and Slices 提案的讲解,让开发者了解 Swift 字符串背后的部分设计思路。

3、WWDC 2021 新 Formatter API:新老比较及如何自定义 – 来自:东坡肘子

@东坡肘子:在 Swift 中开发者通过使用 Formatter ,可以让很多常用类型的数据都转换成可格式化字符串。在 WWDC 2021 中,苹果推出了使用 Swift 编写的全新 Formatter API。本文通过介绍如何创建符合新 API 的 Formatter,让读者从另一个角度了解新 Formatter API 的设计机制;并对新旧两款 API 进行比较。

4、Swift 正则表达式完整教程 – 来自掘金:SunshineBrother

@我是熊大:如何在 Swift 中使用正则表达式,本文做了详细描述,操作字符串时应该会用得到。

5、SwiftString – 来自:GitHub

@我是熊大:SwiftString 是一个轻量级的字符串扩展,功能丰富且强大,GitHub star 1.6k+。

学习资料

整理编辑:Mimosa

独立开发者经验分享

地址https://github.com/loonggg/DevMoneySharing

全职独立开发者是一群相对比较自由的开发者,但自由的背后也有赚不到钱、产品没人用的焦虑,在这个仓库中你可以看到很多国内国外的独立开发者赚钱的经验。

工具推荐

整理编辑:CoderStar

思源笔记

地址https://github.com/siyuan-note/siyuan

软件状态:开源、免费

软件介绍

思源笔记是一款本地优先的个人知识管理系统, 支持细粒度块级引用和 Markdown 所见即所得。

siyuan-note

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS摸鱼周报 第三十五期

iOS摸鱼周报 第三十四期

iOS摸鱼周报 第三十三期

iOS摸鱼周报 第三十二期

利用 Automator 快速符号化 Crash 文件

利用 Automator 快速符号化 Crash 文件

背景

起因是最近有接到一个临时协助任务,其中有几个重要的流程:

  • QA 方导出 .crash 文件(必要的)
  • 我方要根据测试提供的 crash 文件的build number,去下载对应的 xx.app.dSYM
  • 把下载的dSYM给合作方
  • 合作方解析crash文件

从上的步骤可以看出第一步不可省略。第二、三步完全可以干掉,流程越多越浪费时间。
第四步也可以我们自己做,就可以优化成 QA 直接解析好 crash 文件然后给合作方。

那么就提效了提效 50% 是不是,两个人的事情一个人搞定 (那么就可以卷点别的)

初版方案

小插曲

一开始第一周我写了个Shell,调试通过之后就没继续,就干其他大活了(这里有个有悲剧)
……
第二周的时候,不知怎的崩溃出奇的多(应该是合作方更新SDK之后导致的)
当时我正Coding热火朝天,QA和合作方夺命的Call
我就去找那个当时写好的shell脚本,一通翻箱倒柜之后,我悟了,悲剧来了,找不到了
呵呵,被自己强迫症日常清理垃行为给清理了(自己有个日常清理的垃圾的行为,无奈Mac配置就这样)

打工人不得不含着泪重新写了一份 (源码在下面),快速应付下那边的夺命Call
然后我就在想这个事,为啥要我来做,也没啥技术含量,为啥不可自动化?
Bingo~ 说来就来

Shell 源码

1
2
3
4
5
6
7
crash_txt=$1
crash_log=${crash_txt%%.*}.log
# find /Applications/Xcode.app -name symbolicatecrash -type f
# cp /Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash symbolicatecrash
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
./symbolicatecrash $1 $2 > $crash_log
open $crash_log -a sublime

具体如何符号化解析这里就不再唠叨了,网上一大堆:附一个参考链接

使用 Automator 的自动化方案

要使用 Automator 还需要编写 AppleScript 代码。

工具 & 语言

  • 工具:Automator Service, 脚本编辑器
  • 语言:AppleScriptShell

automator

automator script editor

脚本编辑器,AppleScript调试用。

好玩的 AppleScript

下面是一些好玩的 AppleScript 代码,唤起你的好奇心:

1
2
3
4
5
6
7
8
9
10
display dialog "你说假如地球没有了空气,我们会怎样...
那么没有工程目录,后面该怎么办?" default answer "会死" buttons {"我知道了"} ¬
default button "我知道了" with title "Handsome ERROR"
set theInput to text returned of the result
--display dialog text returned of the result
if theInput is equal to "会死" then
display dialog "没救了" with title "ERROR" buttons {"我知道了"} ¬
default button "我知道了"
end if
--忽略下面部分
1
2
3
4
5
say "Hello world"

display dialog "Hello World" with title "Alert"

display notification "Hello World" with title "Notification"

或者直接在终端里面跑

1
osascript -e "display notification \"Hello World\" with title \"Notification\""

-- single comment# single comment 是单行注释

(* this mutli comment *) 是多行注释

Markdown问题AppleScript脚本里意外出现<p data-line这种代码忽略

AppleScript 需要注意的问题

主要还是路径问题 ApeleScript 获取的路径如下:

1
Macintosh HD:Users:xxxxxxx:Documents:xxxxx.app_副本_2.dSYM:

这种冒号的路径在shell命令行根本没法用,所以下面代码成了常客:

冒号字符串 打包成数组 set my_array to split(input as string, ":")

1
2
3
4
5
6
7
on split(the_string, the_delimiter)
set old_delimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to the_delimiter
set the_array to every text item of the_string
set AppleScript's text item delimiters to old_delimiters
return the_array
end split

字符串 set target_path to join(my_array, "/"),这里要注意拼接文件与文件夹用的index下标不同:

1
2
3
4
5
6
7
8
9
10
11
12
on join(the_array, the_delimiter)
set split_str to the_delimiter
set target to " "
set list_length to the length of the_array
set list_length to list_length - 1
set short_list to items 2 through list_length of the_array

repeat with dir in short_list
set target to target & split_str & dir
end repeat
return target
end join

实现过程

思路分析

1、定位dSYM路径

2、定位xx.crash件路径

3、唤起终端,切入指定路径

4、symbolicatecrash解析并重定向输入结果

5、自动打开展示结果

其实这前两步有个大坑:重复下载 dSYM 文件以及导出的 xxx.crash 文件路径会存在空格。在AppleScript调用Shell的时候路径有空格,会报错找不到对应的文件。

解决办法

  • 利用 AppleScript 给文件重命名
  • 借助Automator 现有的快捷操作修改

期间有周报群群主指点使用 AppleScript 借助 quoted 这个 API 来转义引用空格。
结果是终端识别了,但是symbolicatecrash还是不识别,虽然结果不尽人意,但是学到了新技能。

如果你看过AppleScript API,除了想哭就没别的,上面说的很清楚干啥用,但是不知道语法该咋写。因为没写过这种自然语法,每次都是不停的尝试、失败,尝试、失败,尝试、失败。
AppleScript小众到谷歌都没有,大部分都是查阅Stack Overflow

我这边选择是第二种

xxx.crash 文件名有空格的解决办法是直接重命名,查找之后直接把空格替换成下划线。dSYM 父目录路径空格,这边多次导出之后会导致父目录存在空格,这个相对上面就比较复杂。
这里有几点思考:

在事物本身很难解决问题时,我们就需要放开视野,跳出事物本身,提升更高的角度去思考
当你这么想了,你思考问题的维度和角度就变了
在我们这个问题上,既然它的路径上存在空格,我给它换个不存在的路径不就好了
是不是一个很简单的解决办法,所以有时候不要太局限一点一面

一点瞎扯淡

其实日常编码或者修复 BUG 的过程中也会遇到类似情况,我们在一个问题上纠结好久好久到快死了吧!但是问题还没能解决,这个时候就可以尝试:

  • 冷静下来
  • 刻意放慢节奏
  • 全身心放松下来
  • 想点别的换换脑子或者睡一觉(我通常就是睡觉)
  • 冥想(这个相对高级 需要练习)

不去想这个问题一段时间之后,慢慢就会发现脑子开始活络起来,之前的问题解决办法好像一下子思路如泉涌,睡一觉精神也恢复了,思路也有了,简直两全其美是不是,比死磕一天啥都没有强千百倍吧,最后还得被喷延误工期,拉胯身体,最后无奈身不由己加入996.icu 这个 Big Party

工程创建

1、选中dSYM文件 -> 右键 -> 服务 -> 创建服务

2、弹出一个快捷操作的模板空工程,可以配置参数入口(因为第一步选中了,参数就不需要配置了)

3、然后就可以拖拽你要的操作(类似于storyboardxib操作)

4、保存 -> 命名,就会自动存储到本机的~/Libray/Services目录

所有的快捷操作,工作流都会在这个目录,就是说你想用别人写好的最后安装的也是这个目录

示例图:

完整的操作步骤

脚本交互

  • Shell 调用 AppleScript可以用osascript -e
  • AppleScript调用Shell可以用do shell script & do script
  • do script需要配合终端

示例:

1
2
3
4
5
6
7
tell application "Terminal"
activate
--set new_tab to do script "echo fire"
delay 1
do script "pwd" in front window
do script "ls" in front window
end tell

演示

gif
模糊了点,为了加载快,压缩的有点狠,但是也能看大概流程就OK了

有两种使用方式启动 dSYM 自动化服务:

  • 首先选中 dSYM 文件,然后右键 -> 快捷操作 -> dSYM
  • 首先选中 dSYM文件,快捷键即可(这里需要到 Finder -> Service 偏好设置里面配置好按键)

automator rightkey

执行流程如下:

1、启动之后就自动去/Users/$(whoami)/Downloads/目录文件下搜索.carsh文件

这里写死Downloads目录的原因是想提高搜索速度,所有导出的时候选择的就是Downloads目录。如果你想要全局搜索也不是不是可以, 但是你得等等Spotlight

2、搜索完毕之后会列出该目录下所有的 .crash 文件。

3、选择对应的文件(build number 一致),就会打开一个终端进入解析流程。

4、解析完毕之后会通过 sublime 打开。没有sublime会怎样? 就去掉 -a sublime

ApplesScript 代码负责的部分:

  • 冒号:转斜杠/
  • 调用了剪切板做缓存
  • display dialog 显示.crash文件的搜索结果
  • 唤起终端,执行解析

总结

这里本来想在Automator里面加一个调用Shell脚本的的服务,这样就可以静默解析不用唤起终端,调试过程中解析一直失败,因为运行解析的 symbolicatecrash 需要的环境变量报错,也在对应的目录进行了export,但是最终还是不行,最后还是选择唤起终端来执行操作,或许看起来更酷一点吧 哈哈哈。

其他文件读取/拷贝/搜索/重命名都是Automator提供现成服务。Automator真的很强大,但是你要发现它的美,学会使用它。

最后就是要告诫自己:该做的事还得及时做出来, 不然就是午饭没吃 午休没睡。

人物访谈 | 一位 iOS 程序媛

人物访谈 | 一位 iOS 程序媛

Jojo 是我的前同事,做事非常细心,负责过团队多项重要功能的开发与建设,是我接触过为数不多的比较优秀的程序媛之一。这次邀请到她,来聊一聊她眼中的 iOS 开发。

简单做个自我介绍吧

大家好,我是 Jojo ,90后,一直从事 iOS 开发的一枚湖南妹子,喜欢交朋友,容易与人相处。现在是一名娃 3 岁➕的宝妈,如果有北京的宝妈,一起遛娃呀,哈哈哈~

当初学计算机专业出于什么考虑?后来为什么选择了 iOS 开发?

从小农村长大,初中才开始接触电脑,感觉计算机特别神奇,出于好奇,就走了编程这条路。当时 iOS 比较火热,苹果手机高端,能在高端手机上开发 App,会是一件愉快的事情。

目前程序员群体的男女比例大致在 9:1,女生的比例还是比较小的。

作为女生,你怎么看待这种状况,有质疑自己选择做程序员这件事吗?

我工作时遇到开发的女生还是挺多的,也许现在比例较低,个人认为以后会越来越多。目前为止,我认为自己走上编程这条路没错,也没后悔过。自己是比较简单的人,开发圈相对来讲也比较简单,挺喜欢的。

你目前的工作状态是什么样的,有没有困惑或迷惘?

目前工作状态还不错,知道自己想要什么,需要哪方面充电,也正在往自己希望的方向走,工作三年左右那会有过迷惘,感觉大部分东西能实现,工作中需求也能完成,遇到了程序生涯第一个天花板,却不知道怎么突破,那会感觉这辈子只能这样了。

后来遇到了一群可爱又优秀的人,也不排除年纪增长带来的觉悟,能发现自己的不足,目标越来越清晰,了解目标应具备什么能力,寻求突破。不知道大家有没有过相同感受,我曾经不太喜欢看书,逼着自己都看不下去那种,近期却非常想看书,发现认真读完一本书真的可以学到不少东西。

最近在看《非暴力沟通》,读这本书一开始是因为家里有个调皮小孩,天天跟我唱反调,心累得很。收获,让我懂得分析矛盾产生原因,以及如何去解决矛盾,书中会提供几种方法,总而言之,冲突产生时,学会倾听内心的声音,然后站在对方角度去感受,这样很大可能能够建立积极关系,收到良好反馈。不仅育儿有用,工作中同样受益匪浅。

如果大家有看过的好书,感谢推荐给我看看。如果有跟我一样困惑的人,还没遇见影响着自己变的更好的人,那就多看些好的有意义的书,说不定能找到点思路或者多交交朋友。遇见优秀的人多了,自己也会变得更加优秀。

作为程序媛,有没有什么与程序猿不同的工作方式?也可以介绍一下自己的高效工作法。

个人感觉程序媛会更细心,可能这是女性特质。

仅代表个人的一个有效的工作方式是,习惯写 ToDo List 。按照重要和紧急两个维度把待办事项列出来,然后按照紧急重要排优先级一个个完成,这样做会有这些好处:

1、做事不会遗漏。工作中随时都有可能被一些事情打断,有插入事项时,能及时记录进去,我比较健忘,所以对我帮助还挺大的。

2、有助于回顾。写周报时看 To Do List,秒完成。

3、建立目标感。有目标的每天,时间过得快而充实。

关于“程序员是青春饭、35岁失业”等,作为程序媛,你怎么看这个问题?

青春饭 35 岁就失业这个我以前也有过这样的想法,现在则有不同的看法。淘汰的永远是年纪跟能力不匹配的人,所以与其为未来焦虑,不如为现在努力,让自己保持进步和成长。

你觉得工作中最重要的一项技能是什么?

自学能力。曾经就职的公司,开发能用 Swift 的得用 Swift 开发,而我在这之前一直只用 OC ,如果自学能力不够,很难跟得上。不知道大家有没有这样一种错觉,干过编程的,再去干别的,只要给足够多时间,没有学不会的自信😁,我认为这是自学能力带来的信心。

你现在还要带娃,个人时间应该更少了吧,你是如何平衡工作和学习的呢?

相对来讲会减少一点,但也不是没有个人时间,以下方面起到不少作用:

1、提高效率,像上面记录 ToDo List 的方式,自己认为是提高效率方法之一。

2、寻找合作伙伴,平时我婆婆在这边帮我,我老公也会一起带娃。

3、娃上幼儿园后,大部分时间都在学校度过了,目前工作学习带娃没有失衡。

不过追剧逛街的时间确实没以前多了,要买啥,记录下来,申请一下逛次狠的😁。平常还会再抽出一些时间看看书,学习一下。

让你坚持做程序员的动力或目标是什么?有没有什么心得或经验分享给其它程序媛?

在任何行业做好一件事都是有难度的,感觉计算机行业自己也挺喜欢,好像没有理由不继续下去。
对所有女程序员说:如果不讨厌编程,Just do IT。

有没有什么想借助于摸鱼周报进行宣传的?

大家如果对百度感兴趣,可以随时把简历发我邮箱:jojocaonet@163.com,找我内推。马上过年了,买不买新衣回家过年就靠大家了,哈哈哈。

大家如果不嫌弃认识我的话,可通过飞哥加我微信,也可以通过邮件跟我交流哦。

iOS摸鱼周报 第三十五期

iOS摸鱼周报 第三十五期

本期概要

  • Tips:count vs isEmpty
  • 面试模块:Swift 中 struct 和 class 的区别,值类型和引用类型的区别。
  • 优秀博客:本期继续介绍几个优秀的 Swift 开源库。
  • 学习资料:戴铭的 Swift 小册子。
  • 开发工具:PhotoSweeper,一款快速而强大的重复照片清理器。

开发Tips

整理编辑:zhangferry

count vs isEmpty

通常在判断一个字符串或者数组是否为空的时候有两种方式:count == 0 或者 isEmpty。我们可能会认为两者是一样的,isEmpty 内部实现就是 count == 0。但在 SwiftLint 的检验下,会强制要求我们使用使用 isEmpty 判空。由此可以判断出两者肯定还是存在不同的,今天就来分析下两者的区别。

count 和 isEmpty 这两个属性来源于 Collection,count 表示数量,这个没啥特别的,需要注意的是isEmpty的实现。在标准库中,它的定义是这样的:

1
2
3
extension Collection {
var isEmpty: Bool { startIndex == endIndex }
}

集合的首索引和尾索引相等,则表示为空,这里只有一个比较,复杂度为 O(1)。

大部分集合类型都是走的该默认实现,但也有一些特定的集合类型会重写该方法,比如 Set

1
2
3
extension Set {
var isEmpty: Bool { count == 0 }
}

那为啥会出现两种不同的情况呢,我们再看 Collection 里对 count 的说明。

Complexity: O(1) if the collection conforms to RandomAccessCollection; otherwise, O(n), where n is the length of the collection.

所以对于遵循了RandomAccessCollection 协议的类型,其count获取是 O(1) 复杂度,像是 Array;对于未遵循的类型,比如 String,其 count 复杂度就是 O(n),但是isEmpty 却还是 O(1)。

这里的 Set 还要再特殊一些,因为其没有实现 RandomAccessCollection 却还是用 count 的方式判定是否为空,这是因为 Set 的实现方式不同,其 count 的获取就是 O(1) 复杂度。

当然为了简化记忆,我们可以总是使用 isEmpty 进行判空处理。

因为涉及多个协议和具体类型,这里放一张表示这几个协议和类型之间的关系图。

图片来源

面试解析

整理编辑:师大小海腾

Swift 中 struct 和 class 的区别,值类型和引用类型的区别

struct & class

在 Swift 中,其实 classstruct 之间的核心区别不是很多,有很多区别是值类型与引用类型这个区别隐形带来的天然的区别。

  • class 可以继承,struct 不能继承(当然 struct 可以利用 protocol 来实现类似继承的效果。);受此影响的区别有:

    • struct 中方法的派发方式全都是直接派发,而 class 中根据实际情况有多种派发方式,详情可看 CoderStar|Swift 派发机制
  • class 需要自己定义构造函数,struct 默认生成;

  • class 是引用类型,struct 是值类型;受此影响的区别有:

    • struct 改变其属性受修饰符 let 影响,不可改变,class 不受影响;
    • struct 方法中需要修改自身属性时 (非 init 方法),方法需要前缀修饰符 mutating
    • struct 因为是值类型的原因,所以自动线程安全,而且也不存在循环引用导致内存泄漏的风险;

值类型 & 引用类型

  • 存储方式及位置:大部分值类型存储在栈上,大部分引用类型存储在堆上;
  • 内存:值类型没有引用计数,也不会存在循环引用以及内存泄漏等问题;
  • 线程安全:值类型天然线程安全,而引用类型需要开发者通过加锁等方式来保证;
  • 拷贝方式:值类型拷贝的是内容,而引用类型拷贝的是指针,从一定意义上讲就是所谓的深拷贝及浅拷贝

你可以在 CoderStar|从 SIL 角度看 Swift 中的值类型与引用类型 中查看详细内容。

优秀博客

整理编辑:皮拉夫大王在此我是熊大东坡肘子

1、第三方日期处理库SwiftDate使用详解 – 来自航歌:hangge

@东坡肘子:SwiftDate 是在所有苹果平台,甚至在 Linux 和 Swift 服务器端框架(如 Vapor 或 Kitura )上操作和显示日期和时区的权威工具链。在 CocoaPods 上有超过 300 万的下载量。SwiftDate 功能强大,无论是简单的日期操作,还是复杂的业务逻辑都能满足。本文将对 SwiftDate 的使用方法做详尽说明。

2、搞事情之 Vapor 初探 – 来自掘金:PJHubs

@东坡肘子:Vapor 是一个基于 Swift 语言的开源 Web 框架,可用于创建 RESTful API、网站和使用 WebSockets 的实时应用。在核心框架之外,Vapor 还提供了 ORM 、模板语言,以及用户身份验证和授权模块。本文主要记录了第一次上手 Vapor 所遇到的问题、思考和总结。

3、用 Publish 创建博客 – 来自肘子的Swift记事本:东坡肘子

@东坡肘子:Publish 是一款专门为 Swift 开发者打造的静态网站生成器。它使用 Swift 语言构建整个网站,并支持主题、插件和其他大量的定制选项。本系列文章将通过三个篇幅分别介绍 Publish 的基本用法、主题定制以及插件开发。

4、Using Realm and Charts with Swift 3 in iOS 10 – 来自:Sami Korpela

@我是熊大:一个十分强大并且是纯 Swift 的图表相关的开源框架 – Charts。本文作者利用 Swift 中的轻量级数据库 Realm 和 Charts,构建了一个 Demo。篇幅较长,适合新手,但美中不足的是:Demo 基于 Swift 3。此外我早期写过一篇关于 Realm 的实践代码的文章:如何降低Realm数据库的崩溃,有兴趣可以看一下。

5、今天我们来聊一聊WebSocket(iOS/Golang) – 来自掘金:齐舞647

@我是熊大:Starscream swift 中的 7k+ star 的 socket 开源库,本文作者利用 GO 和 Starscream,模拟了客户端和服务端 websocket 的交互过程,建议对 Socket 感兴趣的阅读。

6、Hero Usage Guide – 来自:Hero官方

@我是熊大:Hero 是我用过的最好的转场动画,没有之一,20k+ star 的成绩也表明了它在转场动画的地位;Hero 应该能满足绝大部分需求,这是它的官方使用手册。

学习资料

整理编辑:Mimosa

戴铭的 Swift 小册子

地址:https://github.com/ming1016/SwiftPamphletApp

戴老师用 Swift 写的、按照声明式 UI、响应式编程范式开发的 Swift 小册子。你可以在这本小册子中查询到 Swift 的语法指南,同时还有 Combine、 Concurrency 和 SwiftUI 这些库的使用指南,你还可以在这追踪到一些知名仓库、开发者的 Github 动态和本仓库的 Issues。由于是开源的,你也可以自己调试学习或者是为项目作出贡献。

工具推荐

整理编辑:CoderStar

PhotoSweeper

地址https://overmacs.com/

软件状态:$9.99,可以试用

软件介绍

PhotoSweeper 是一款快速而强大的重复照片清理器,旨在帮助您在 Mac 上查找和删除重复和相似的照片。

我们可以考虑用在给应用瘦身时扫描相似图片资源场景下。

PhotoSweeper

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS摸鱼周报 第三十四期

iOS摸鱼周报 第三十三期

iOS摸鱼周报 第三十二期

iOS摸鱼周报 第三十一期

人物访谈 | 一位游戏行业的 iOSer

人物访谈 | 一位游戏行业的 iOSer

iHTCboy 是摸鱼周报里负责校对的成员之一。记得他第一次参与校验时,我把一期整理好的周报发给他看,他后来给我发了 1600 字的修改意见,我都震惊了。修改意见里即有关于技术点的,也有格式和语言表述的,关键读过之后,感觉他说的还非常有道理,由此可见他做事认真的态度。下面是访谈内容。

简单介绍下自己吧

大家好,我是 iHTCboy,从事游戏 iOS SDK 开发快五年了。目前在 37手游,以 SDK 开发为主栈,自动化技术实践为副栈。目前我们海外版 SDK 中除了仅剩 1% hook 的 OC 代码,全部是 Swift 代码;在游戏行业有多年经验积累沉淀后,我们团队希望打造和分享一些 SDK 相关的自动化方案或框架,未来会逐渐开源,敬请期待,跟大家一起学习交流&进步!

当初为什么选择计算机这个行业呢?

应该是小时候对黑客的幻想,《电脑报》90后应该都有看过吧?而《黑客防线》、《黑客x档案》、《黑客手册》这些杂志,不知道大家有没有看过,初中时很着迷,初中时用破解软件逃过网吧收费系统,高中帮亲戚修电脑重装系统,有非常大的好奇心和成就感。在对计算机编程有全面了解前,总是幻想自己当上黑客后xxx,神秘又强大是每个男孩都有过的梦想吧!所以不加思索的就选择了计算机。

游戏公司的 SDK 开发日常是什么样的,它跟应用开发有什么区别呢?

每一位不熟悉游戏 SDK 开发的朋友都会问这个问题。应用开发大家可能比较熟悉,我们在开发中,也会引用第三方的 SDK,比如友盟 SDK、微信 SDK,这些 SDK 提供推送、分享或者支付的能力。而游戏 SDK 开发也是这个道理,为游戏提供特定平台的原生 API 功能。举例来说,目前主流游戏都是 Unity 引擎开发,游戏开发好后,可以导出 Android 项目或者 iOS 项目(Xcode 项目),然后在接入原生平台的 API。

游戏发展到今天,已经高度标准化…..

游戏研发专注于游戏内容的设计和开发(跨平台),而 iOS 和安卓平台原生的能力(如支付 IAP)就由我们封装成 SDK,提供接口给游戏调用。

1、SDK 开发包括:

  • 帐号体系(包括封装第三方账号体系:WeChat、Facebook、Google、Line、Twitter 等)
  • 支付体系
  • 分享社交
  • Web页面(游戏活动、客服、攻略等)
  • 数据埋点(广告、留存、数据分析等)
  • 性能监控

将以上功能模块封装成 API 给游戏调用,一般就是登录、用户中心(有界面),其它都是业务逻辑处理。

2、SDK 是提供给游戏使用,所以不像 App 快速迭代 UI,而是迭代业务逻辑。详细的开发细节,以后有时间专门写篇文章吧。关注游戏行业的发展,可以参考之前写的文章:游戏出海本地化概述

看你发多篇调研苹果审核和内购相关的文章,针对最近苹果在美国败诉,撬开苹果 IAP 的限制,你怎么看?这个改动可能带来的影响有哪些?

我们先来梳理一下,苹果 AppStore 垄断案的几个新闻关键点:

时间 概述 引用来源
2021-08-26 Apple 与美国开发者就 App Store 更新达成一致,开发者可以使用电子邮件等通信方式与用户共享 iOS App 之外的支付方式信息。 来源
2021-08-31 韩国国会立法和司法委员会通过《电信业务法》修正案,禁止占据市场主导地位的应用商店强迫移动内容提供者使用特定支付方式。 来源
2021-09-01 日本公平贸易委员会结束对 App Store 的调查,允许“阅读器”类 App 的开发者在 App 内提供访问其网站的链接,开发者可将用户引导向外部网站进行购买。(阅读器 App:提供数字杂志、报纸、书籍、音频、音乐和视频的预付费内容或内容订阅。 来源
2021-09-10 Epic诉苹果垄断案裁决:美国联邦法院命令苹果不得禁止应用发商引导用户通过第三方支付平台付费。法院并不认为苹果公司是手游交易市场垄断者,但认为苹果公司禁止将用户导向其他渠道是反竞争行为。要求苹果必须在12月9日之前执行。 来源1来源2
2021-11-10 Yvonne Gonzalez Rogers 法官拒绝了苹果公司关于推迟执行永久禁令的请求,苹果打算根据这些情况要求第九巡回法院暂缓执行。 来源

关于 IAP 内购问题,随着 AppStore 体量越发庞大,大多数开发者认为 30% 税不再合理,其实很多事本身就没有公平合理可言,省略一万字~ 你懂的。

大家可能忽视了一个重要的问题:苹果 IAP 收入占比最大的是什么类型的 App?答案是游戏!所以,对非游戏类 App 允许使用 IAP ,目前对于苹果的损失还不是很大,当然,如果没有损失是最好,但是目前的大环境和反垄断,一定会让苹果做出调整。(注:游戏一直以来都是 App Store 中的重头戏,从 2017 年 iOS 11 开始,苹果在 App Store 增加了 Games 游戏标签入口。)

对于苹果败诉带来的影响:

(1)苹果在美国一定会上诉,一般垄断案可能长达几年时间,所以苹果愿意继续打官司,尽量拖延时间(参考微软 IE 浏览器反垄断案,从1997年到2002年)。

(2)苹果降低 IAP 税率或允许所有 App 使用第三方支付是时间问题,但没有了 IAP,苹果可能会推出其它的服务费。

就在 11 月 19 号,谷歌宣布韩国地区的 Google Play 商品允许应用接入第三方支付系统。谷歌目前每年前100百美元抽15%,超过后30%。按谷歌的新规,韩国地区接入第三方支付系统,谷歌依然从中收取11%(26%),也就是说抽成只是被降低 4%。
简单来说,就是谷歌允许开发者使用第三方支付系统,但没有说不能抽成啊!抽成和允许第三方支付,不冲突!

为了应对韩国的新法规,Google 已更新 Google Play 付款政策

(3)对于开发者来说,肯定是好事。但这件事背后,是大资本之间博弈的结果,一定要明白为什么可以赢,比结果重要。

看过你的博客:https://ihtcboy.com/ ,从2008年就开始写了, 这是一个非常长的时间跨度。关于博客部分也有几个问题想问下你。

培养一个良好的习惯并持之以恒是一件不那么容易的事情,能讲下你是如何培养写作的兴趣以及对培养一个有益的习惯有什么建议吗?

高中语文课最讨厌的就是写 500 字作文,但没有想到自已大学后会喜欢写作。写作可能是我小时候比较孤僻,很少与人交流,总喜欢自言自语道,也许是这个原因,自己会有很多天马行空的想法或者内心独大。而作文是根据要求写作,限制你话题,没有自由的发挥和空间。

当然喜欢是一回事,写的好不好才最重要!培养写作,我认为要了解写作的注意事项,怎么样的文字修饰有多妙,多模仿多练习。培养习惯,更多是从一个个可行的小目标做起,积累量多了,慢慢就是习惯。切忌急功近利!

写作一定也有通用的规律规则,比如 中文技术文档的写作规范。大家多写写,多总结,一定会有自己的收获。

你写过如何建立自己的开发知识体系的文章,程序员也是一个需要不断学习的行业,关于如何让学习这件事变得有趣,再分享一下你的见解吧?

(1)说起来见笑了,建立自己的开发知识体系 只是觉得大家可以往一些通用的方向去梳理。现在回想起来,发现自己也没有什么都懂,可能接触和学习更多知识后,发现自己空白面更多,然后就越想学习。所以,也许再过几年,会有更深的理解,之后再跟大家分享啊。

(2)“不断学习”我认为是21世纪,每个人都要学会的。就像父母要学会用智能手机,否则健康码都打不开,在城市里寸步难行。所以,大家一定要正面接受它,每天都要学习和进步。

(3)“让学习变有趣”,其实我一直认为看书,应该是让人感到愉快的事情。但为什么大家都不愿意学习了呢?我觉得最重要的是,大家越来越急功近利,一切都为更快,从而忘记了体验过程。举个例子,去旅游时,可能为了发一个朋友圈,一直拍照修图,刻意的、绞尽脑汁的想,却没有享受风景的心境。所以学习本身如果慢下来,不只关注结果,还关注或者尝试从问题到结果的推演过程,这样的学习会更有趣一些。

你博客有很多思维导图,思维导图除了能够帮助梳理知识外,还有其他什么好处吗?

大家一定知道一个道理,图片比文字更加形象。思维导图比一段文字也更加生动形象,所以,它不仅能够梳理知识,还能理清知识的层次和架构,帮助记忆知识。举个例子,Vim 快捷键,如果你忘记了,打开思维导图,一看就知道:

再比如,说到人工智能,可能想法很多,那么大的框架应该是怎么样的呢?

最近有什么感悟想跟大家分享的吗?

关于内卷的话题,网上有很多讨论,大多数是吐槽或者劝退。我想换一个词,“努力”,为什么会有人很努力?现在大家都达到小康生活后,大多数人会选择躺平,然后自嘲自己是打工人。其实自甘打工的还是少部分人,大部分还是想往上跳一跳当老板的。另一方面在一个阶级中,努力的人,往往是想要跳出这个阶级的。就是这个往上跳的愿望,它需要的正是努力。

所以,努力它本身上没有错,这代表一种向上的态度。但需要注意的是努力的结果,是不是可以为你改变什么?如果可以,我觉得努力无可厚非。否则,就是真内卷。

最后,有一句话送给大家一起共勉:

Follow your own course, and let people talk.
走自己的路,让别人说去吧!
- 但丁 《神曲》

有什么想借助摸鱼周报宣传的?

欢迎大家关注我们掘金公众号:37手游iOS技术运营团队,会定期分享一些有趣的文章。

如果大家有兴趣加入三七互娱,可以查找相关岗位 :https://zhaopin.37.com ,或者将简历发给我内推:ihetiancong@gmail.com。不要担心有没有相关岗位,优秀的人,在哪里都是金子!

如果有其他问题想跟 iHTCboy 交流,还可以在留言区评论。

iOS摸鱼周报 第三十四期

iOS摸鱼周报 第三十四期

本期概要

  • 话题:Apple 宣布推出自助维修计划。
  • Tips:混编|NS_SWIFT_NAME。
  • 面试模块:二叉树的三序遍历。
  • 优秀博客:几个 Swift 三方库的解析文章。
  • 学习资料:棒棒彬·技术参考周刊。
  • 开发工具:Mounty:一个在 macOS 下以读写模式重新挂载写保护的 NTFS 卷的免费的小工具。

本期话题

@zhangferry:Apple 宣布推出自助维修计划。Apple 将面向个人消费者提供零件、工具与维修手册,从 iPhone 12 与 iPhone 13 开始。搭载 M1 芯片的 Mac 电脑也将很快加入。自助维修计划将从明年初开始在美国率先启动,并在 2022 年或更晚推广到其他国家。

这跟苹果的环保政策相符,可以预料到本来就很能打的 iPhone 更换原装部件之后再多用个 1-2 年也不是啥难事。虽然惠及的用户不会特别多,但是苹果的做法还是值得点赞的。

同时这也带来一个问题,自助维修算是一件有一些技术操作的事情,如何保证操作的正确性?

想起之前我自己给 iPhone 换电池的经历,前两次换 iPhone6SP 电池成功。对自己有了一定的信心,结果,第三次给 iPhone7P 换电池就失手了,不仅因为电池排线插的错位导致一块电路板烧掉,还扯断了连接 Home 键的一根线。有一部分原因是手机屏和后盖的链接方式从之前的前后变成了左右,操作空间变小了,但更主要的还是相应知识的缺乏。这还只是简单的电池,像是摄像头的更换将会更复杂。所以这个自助维修计划比较重要的是应该允许哪些人能够自助维修,如何评估这些人。

题外话:如果自己修坏了,肯定还是算到自己头上的吧🤔。

信息来源:Apple 宣布将推出自助维修计划

开发Tips

混编|NS_SWIFT_NAME

整理编辑:师大小海腾

NS_SWIFT_NAME 宏用于在混编时为 Swift 重命名 Objective-C API,它可用在类、协议、枚举、属性、方法或函数、类型别名等等之中。通过 Apple 举的一些例子,我们可以学习到它的一些应用场景:

  • 重命名与 Swift 风格不符的 API,使其在 Swift 中有合适的名称;

  • 将与类 A 相关联的类/枚举作为内部类/枚举附属于类 A;

  • 重命名 “命名去掉完整前缀后以数字开头的” 枚举的 case,改善所有 case 导入到 Swift 中的命名;

  • 重命名 “命名不满足自动转换为构造器导入到 Swift 中的约定的” 工厂方法,使其作为构造器导入到 Swift 中(不能用于协议中);

  • 在处理全局常量、变量,特别是在处理全局函数时,它的能力更加强大,能够极大程度地改变 API。比如可以将 全局函数 转变为 静态方法,或是 实例⽅法,甚至是 实例属性。如果你在 Objective-C 和 Swift 里都用过 Core Graphics 的话,你会深有体会。Apple 称其把 NS_SWIFT_NAME 用在了数百个全局函数上,将它们转换为方法、属性和构造器,以更加方便地在 Swift 中使用。

你可以在 iOS 混编|为 Swift 重命名 Objective-C API 中查看上述示例。

面试解析

整理编辑:夏天

作为最常见的数据结构之一,在算法中有举足轻重的地位。

理解树有助于我们理解很多其他的数据结构,例如等。也有助于我们理解一些算法类型,例如,回溯算法动态规划。当然在练习关于树的解题过程中,也能够加深我们对深度优先广度优先算法的理解。

今天我们以二叉树的三序遍历为题,来开启我们二叉树的学习。

题目

给定一个二叉树,返回他的 前序 中序 后序 三种遍历

输入: [4,2,6,1,3,5,7]
4
/
2 6
/ \ / \
1 3 5 7

输出

前序遍历:首先访问根结点,然后遍历左子树,最后遍历右子树(根->左->右)

顺序:访问根节点->前序遍历左子树->前序遍历右子树

前序遍历: [4, 2, 1, 3, 6, 5, 7]

中序遍历:首先遍历左子树,然后访问根节点,最后遍历右子树(左->根->右)

顺序:中序遍历左子树->访问根节点->中序遍历右子树

中序遍历: [1, 2, 3, 4, 5, 6, 7]

后序遍历:首先遍历左子树,然后遍历右子树,最后访问根节点(左->右->根)

顺序:后序遍历左子树->后序遍历右子树->访问根节点
后续遍历: [1, 3, 2, 5, 7, 6, 4]

二叉树的遍历方法一般有三种

  • 递归
  • 迭代(常规迭代加颜色标记法
  • 莫里斯遍历(今天暂时不涉及)

递归

在树的深度优先遍历中(包括前序、中序、后序遍历),递归方法最为直观易懂,但考虑到效率,我们通常不推荐使用递归。

递归步骤一般需要遵循以下三种:

  1. 确定递归的参数以及返回值
  2. 确定递归的终止条件,递归算法一定有终止条件,避免死循环。
  3. 确定单次递归的逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// traversals 为输出的数组
func preorder(_ node: TreeNode?) {
guard let node = node else {
return
}
/// 前序遍历
traversals.append(node.val)
preorder(node.left)
preorder(node.right)
/// 中序遍历
preorder(node.left)
traversals.append(node.val)
preorder(node.right)
/// 后序遍历
preorder(node.left)
preorder(node.right)
traversals.append(node.val)
}

迭代

二叉树的迭代步骤一般是将节点加入到一个 中,然后通过访问栈头/栈尾,根据遍历顺序访问所有的符合的节点。

前序遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
func preorderIteration(_ root: TreeNode?) {
var st:[TreeNode?] = [root]
while !st.isEmpty {
let node = st.removeFirst()
if node != nil {
traversals.append(node!.val)
} else {
continue
}
st.insert(node?.right, at: 0)
st.insert(node?.left, at: 0)
}
}
中序遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func inorderIteration(_ root: TreeNode?) {
var st:[TreeNode?] = []
var cur:TreeNode? = root
while cur != nil || !st.isEmpty {
if cur != nil {
st.insert(cur, at: 0)
cur = cur?.left
} else {
cur = st.removeFirst()
traversals.append(cur!.val)
cur = cur?.right
}
}
}
后序遍历

后序遍历其遍历步骤是 左→右→中,但是这个代码实现起来不简单。 所以我们可以先访问依次访问 中→右→左 的节点,最后将得到结果进行 reversed,其结果最终变成 左→右→中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func postorderIteration(_ root: TreeNode?) {
var st:[TreeNode?] = [root]
while !st.isEmpty {
let node = st.removeFirst()
if node != nil {
print(node!.val)
traversals.append(node!.val)
} else {
continue
}
st.insert(node?.left, at: 0)
st.insert(node?.right, at: 0)
}
traversals = traversals.reversed()
}

颜色标记法

传统的迭代由上述代码可知,比较繁琐,而且迭代过程中易错。参照 颜色标记法-一种通用且简明的树遍历方法 ,利用一个兼具栈迭代方法的高效,又像递归方法一样简洁易懂的方法,更重要的是,这种方法对于前序、中序、后序遍历,能够写出完全一致的代码

其核心方法如下:

  • 标记节点的状态,已访问的节点标记为 1,未访问的节点标记为 0

  • 遇到未访问的节点,将节点标记为 0,然后根据三序排序的要求,按照特定的顺序入栈

    // 前序 中→左→右 按照 右→左→中
    // 中序 左→中→右 按照 右→中→左
    // 后序 左→右→中 按照 中→右→左

  • 结果数组中加入标记为 1 的节点的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    func tuple(_ root: TreeNode?) -> [Int] {
var traversals = [Int]()
var statck = [(0, root)]
while !statck.isEmpty {
let (isVisted, node) = statck.removeLast()
if node == nil {
continue
}
if isVisted == 0 {
// ///前序遍历
// statck.append((0, node?.right))
// statck.append((0, node?.left))
// statck.append((1, node))
// ///中序遍历
// statck.append((0, node?.right))
// statck.append((1, node))
// statck.append((0, node?.left))
// ///后序遍历
statck.append((1, node))
statck.append((0, node?.right))
statck.append((0, node?.left))
} else {
traversals.append(node!.val)
}
}
return traversals
}

利用颜色标记法可以简单的理解迭代的方法,并写出模板代码。

莫里斯遍历

作为兼具性能及低空间复杂度的莫里斯遍历,可以在线下讨论。

优秀博客

整理编辑:我是熊大东坡肘子

1、Alamofire的基本用法 – 来自掘金:Lebron

@我是熊大:Alamofire 是 AFNetWorking 原作者写的,Swift 版本相比 AFN 更加完善,本文介绍了 Alamofire 基本用法,很全面,适合精读;作者还有一篇高级用法,推荐阅读。

2、Kingfisher源码解析 – 来自掘金:李坤

@我是熊大:Kingfisher 对标 OC 中的 SDWebImage,作者是大名鼎鼎的王巍,本文是 Kingfisher 源码解析系列的总结,推荐阅读。

3、iOS SnapKit架构之道 – 来自博客:Rimson

@我是熊大:SnapKit 作为在 Swift 中的页面布局的地位,相当于 OC 中的 Masonry,使用起来几乎一模一样,本文作者详细梳理了 Snapkit 布局的过程和原理。

4、第三方图表库Charts使用详解 – 来自航歌:hangge

@东坡肘子:Charts 是一个功能强大的图表框架,使用 Swift 编写。是对 Android 上大名鼎鼎的图表库 MPAndroidChart 在苹果生态上的移植。hangge 通过大量的范例代码对 Charts 的使用进行了相近地说明。

5、访问 SwiftUI 内部的 UIKit 组件 – 来自 Swift花园:猫克杯

@东坡肘子:抛开 SwiftUI 尚不完备的工具不说,SwiftUI 的确因其构建 UI 的便捷性给开发者带来了兴奋。有一个令人欣慰的事实是,许多 SwiftUI 组件实际上是基于 UIKit 构建的。本文将带你探索一个令人惊讶的 SwiftUI 库,它叫 Introspect 。利用它,开发者能够访问 SwiftUI 组件底层的 UIKit 视图。

学习资料

整理编辑:Mimosa

棒棒彬·技术参考周刊

地址:https://www.yuque.com/binboy/increment-magzine

这是一份由 Binboy👻棒棒彬 在语雀上梳理总结的技术参考周刊。这份周刊是作者学习与生活的沉淀和思考,既有广度,也有深度,还有态度。就像其发刊词的标题:「与技术世界保持链接」,在周刊中你可以看到作者学习技术的过程和经验,也能看到科技与生活的一些新鲜事,这里可能有你正在关注的,亦或者是从来没听说过的技术信息,这些信息既是作者与他自己「第二大脑」的链接,也是作者与读者交流的媒介,同时推动着作者与读者一起前进。这里改编引用一段发刊词中的一段话来抛砖引玉 :

做技术,追求深度无可厚非,只是无需厚此薄彼,我个人而言倾向于「修学储能,先博后渊」的。技术世界的开源、互联网的开放更是给了见多识广一片良好的土壤,我们可以了解了技术、工具现状,将其充分地应用、解决现实世界中的普通问题,并在过程中不断完善。当真正遇到边界时,再结合对已有技术和工具的融会贯通去创造真正的新技术、新工具,也不迟。

朝一个方向看得再远,你也未必能看到新方向

———— 修学储能,先博后渊

工具推荐

整理编辑:CoderStar

Mounty

地址https://mounty.app/

软件状态:免费,开源

软件介绍

Mounty 是一个在 macOS 下以读写模式重新挂载写保护的 NTFS 卷的小工具,功能类似于 NTFS For Mac,最大也是最重要的区别是它是免费的。

mounty

关于我们

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

往期推荐

iOS摸鱼周报 第三十三期

iOS摸鱼周报 第三十二期

iOS摸鱼周报 第三十一期

iOS摸鱼周报 第三十期

人物访谈 | 一位参加过 WWDC 的 iOSer

人物访谈 | 一位参加过 WWDC 的 iOSer

Mimosa 是摸鱼周报的编辑之一,负责每期的学习资料整理。在一次聊天过程中他晒出了跟 Tim Cook 的合影,给我们馋哭了。于是就有了这期访谈的主题:WWDC 之旅,也算是拉着 Mimosa 一起回忆了一把,哈哈。因为是在 17 年,WWDC 还是线下,活动真还挺多的,不知道还要多久才开发者大会才能再次回归线下,希望疫情赶紧过去吧。

简单介绍下自己吧

哈咯大家好,我是 Mimosa,中文谐音名棉毛衫,小号是 Mimoku(棉毛裤),是一名 iOS 开发者,工作经验一年左右,是 iOS 摸鱼周报的编辑之一。我是在大学的时候接触的 iOS 开发,当时加入了学校的 iOS Club,在大二那年获得了来自 Apple 的 WWDC Scholarship Winner,从此算是与 iOS 开发结缘,参加了许多与 iOS 相关的活动,毕业到如今一直是从事 iOS 开发。

能讲下自己获得 WWDC 奖学金的经历吗?

首先先介绍一下这个奖学金吧,WWDC Scholarship Winner 是 Apple 每年为全球学生提供的一个小福利,每年 4 月份的时候,Apple 会在官网放出当年 Scholarship 的要求,比如你需要是一个学生,你不能年龄太小等等,你还需要做一个有创意、激动人心的小作品,并提交,全球每年将会有大约 350 名学生的作品会被接收。如果你的作品被接收了,那么将免费获得一张当年 6 月份参加 WWDC 的门票,价值 $1599 😎(且提供住宿、来回差旅费也有机会报销)。在 2020 年之后,这个奖学金改名叫做 Swift Student Challenge 了,且由于疫情这两年的 WWDC 改为线上,就无法去现场参加了。

我获得的是 17 年的奖学金,申请 WWDC 奖学金时我正值大二,当时是刚学完学校的 C 语言课程和数据结构,加入了我们学校的 iOS Club,也是第一次接触 iOS 开发。我还清晰的记得当时是在 Swift 3.0 的环境下用 Swift 2.0 的教学视频在自学😂,周围会 Swift 的人也是屈指可数,只能自己上网摸索。由于当时有学长获得过上一年的 WWDC 奖学金(但由于时间问题没去 WWDC 现场),所以当时的刚学我们也想试一试,就熬了几天用自己学过的所有东西做了一个作品提交了。没成想那年我们学校有 4 位同学获得了奖学金!但最后由于签证问题(我是之前已经有申根和美签了,但我的朋友们没有😢),所以只有我一个人去圣何塞 WWDC 的现场。

你当时开发的是什么作品?

那一年是第一次 Apple 要求参赛者制作一个 playground(之前都是要求做一个介绍自己的 App 这种类型的题目),我制作了一个简单又可爱的可视化冒泡排序教程💂‍♀️,运行在 Swift Playgrounds 这个 App 中(可以在 iPad App Store 和 Mac App Store 中找到这个 App),利用这个 App 一侧可以写代码,一侧可以查看运行结果这种所见即所得的特性,来教使用者冒泡排序算法的原理。

参加 WWDC 期间有什么好玩的事情,这个过程有什么收获?

可以说是收获颇丰。首先一个是,作为 Scholarship Winner 可以额外参加一个 WWDC 正式开幕之前的 WWDC Scholarship Meeting,由 Apple 组织在一个小剧场和我们讲了一点 Apple 在各个方面做出的一些努力等等,还会给我们发一个 dev 版本的 Apple TV。重要的是,在所有演讲结束后,Tim Cook 会突然走出来和所有 Scholarship Winner 合影,然后接下来就是激动人心的和 Tim Cook 合影环节,我当时的情况是你只需挤到他旁边他就会和你合照一张!🤩据我观察现场大约有 2/3 的人都在成功合影了。(剩下没合影成功的只能落寞地望着 Tim Cook 和他保镖的背影😇

对于大多数开发者来说,可能 WWDC 里面重要的是各种各样的 session,但其实也会有一些无关技术但与生活相关的演讲,给我印象深刻的有两个。

一个是在 WWDC 的第二天,由于我时差没倒过来睡不着,参加的当天最早的一个演讲,是奥巴马夫人米歇尔的一个演讲,(虽说我排队时排在前几个但是仍没坐到前排,气死了),她讲了许多科技与生活的事情,谈论了他的孩子,还讲了去长城的经历,吐槽长城人多等等。

另有一个让我印象深刻的演讲是来自 NASA 的一个女数学家奶奶,她谈论了她当年是怎么加入 NASA 以及工作至退休期间的经历,讨论了那个年代女性在 NASA 的情况,讨论了那个年代女科学家的境地,让我大受震撼。

作为 Scholarship Winner 还有一大好处就是,除了能接触到说中国话的学生开发者之外,还可以接触到来自世界各地的 Scholarship Winner,由于所有 Winner 住在圣何塞州立大学的宿舍里,我去的那年 350 个 Winner 中大概只有15个是来自大陆,同时黄皮肤的面孔不超过30个,意味着活动中有大量的时间我需要与母语不是中文的人交流。

比如我的室友就来自俄罗斯🇷🇺,他大四在读,我们用散装英语交流了很多东西,他的作品是用 playgrounds 模拟 Metal(?我到现在也没懂这怎么做的😅),他的计算机基础踏实的一塌糊涂,聊天的时候我就像被面试一样,绞尽脑汁地想他到底在说啥。还有一位意大利小哥🇮🇹一看我这是亚洲人面孔,就凑上来问我们是哪里人,问我是不是中国人,问我来自上海嘛,在得到一系列肯定的答复后他说他去同济大学交流过一段时间,然后他突然很想告诉我他是哪个校区,然后我们开始一起苦思冥想同济大学除了四平路还有在哪的校区,想了半天没想出来,意大利小哥也不尴尬,和我面面相觑地走了一路,最后挥手道别😂。

在 WWDC 期间的会场外,也会有很多的 tech talk 在附近举办(蹭 WWDC 热度),我误打误撞参加了一个 Realm 举办的 tech talk。一开始先是吹了一波 Realm,然后是邀请了一些从 Apple 离职的人来做对话,结果发现请到了 Chris Lattner(当时就职于特斯拉)。大家伙那叫一个激动啊, 连下面的好多爷爷辈的听众都提了好几个问题(真的爷爷辈👨‍🦳,拄拐杖的,不开玩笑),只能感叹一下计算机技术这该死的魅力。

另外 WWDC 的倒数第二天有个 Bash,算是一场露天音乐会,气氛很热烈!来的是 Fall Out Boy(代表作有”The Phoenix”,”Immortals”等,现场太顶了),大家在现场把酒言欢,给 WWDC 收了个尾。在 Bash 现场还和来自国内各大互联网公司的工程师们合影了,不知道各位看官有没有在照片里面的。

在 WWDC 玩的这几天拓宽了我看待技术的视野,把我从学校里教的那些知识里面拉了出来,在会场的内外,看到了更多与业界发展相关的东西,看到了计算机科学的广度和深度。我本科就读于魔都某不知名二本,且不是工科强校,所以我的学校计算机专业水平比起其他获得奖学金的同学的学校来说(我记得有中大、同济、新国大、中科大、港大等🙂)差的不是一点半点,无意贬低我的母校。但确实这次的经历让我觉得我能努力的地方原来还有这么多,我在学校所认识到的那些知识似乎只是计算机科学的冰山一角,突然在心中对一些东西产生了渴望,让我突然对未来感到期待和兴奋,在这些经历和感受一起冲击我心灵之后,我知道了一件事:我掉进代码这个陷阱里了🥰。

听说你还在上海科技馆和 Apple 供应链工厂等场合进行过一些线下演讲,这是一种什么体验?

由于在学校参加的 iOS Club 与 Apple 有一些方面的交流和合作,所以也参加了一些特别的活动,例如在夏天的时候去上海科技馆免费给小朋友上编程课,就是用上文提到过的 Playgrounds 这个 App,也给科技馆的小朋友分享参加 WWDC 的经历(同时我也发现,现在的10岁左右小朋友有编程经历的真不少)。除小朋友外,我还作为助教参加过给 Apple 供应链工厂的 Swift 教学活动,用一个月的时间让一点代码都不懂的工厂员工通过学习实践之后,能够自行开发一个简单的 iOS App。另外我还受邀参加香港教与学博览会分享 playgroundbook 的制作经验,向参观者介绍 Playgrounds 这个 app 作为教学工具的一些优点等等。这些交流的经验带给了我从别的角度看待 Swift 以及 iOS 开发的机会,在准备教学教案这些东西的时候,不可避免地会去研究和比较别的语言、别的技术、别的生态是怎么样的😵,让我了解了一些跨平台技术,对客户端开发多了一点理解。

除了这些技术方面的感悟,这些经历带给我了更多情感上的震撼,比如有位10岁小朋友学过了python、c++等等语言,对编程思想也理解的很到位,而且为人很谦虚,还会帮助别的小朋友理解代码;还比如在工厂中我们作为老师教那些20多岁、30多岁、甚至40多岁的员工写代码,而他们就像是科技馆的小朋友一样,对代码一无所知然后带着好奇和热切的眼神来上课,并且当做不出作业时会很羞愧,这些经历会让我感觉到:诶好像这个世界不是我以为的那个世界这种感觉,我没想到有这么多人在推动编程教育、我没想到这么多人对编程这么大的兴趣、我也没想到学编程对一些小孩子来说是一种消遣、我更没想到多会那么一点点编程能改变一个人的发展。根据给我们的反馈,有一小部分上过我们课的工厂员工由于在编程课的表现很好,被“挖”到了工厂的技术部门,从此工作的地点从流水线换到了办公室🤑。

能分享下你的学习历程以及保持学习热情的一些方法吗?

我想我的答案可能和大家想象的很不一样。在校的时候,我是在我社团的推动下成长的,我参加了很多活动、比赛,几乎都是和 iOS 有关的,这些都是是我的社团带给我的资源和推动,比如去参加移动应用创新赛(Apple 赞助)的期间,我学了 ARKit、CoreML、SceneKit、SpriteKit 等等,了解了很多较冷门的 iOS 技术,了解了隐私政策、了解了怎么上架一个 app、以及审核的诸多事项,我的比赛或者活动需要什么我就去学什么,当然这没有什么不好的,没有这些推动我的话我肯定窝在寝室打 dota(嘿嘿👍)。

但至于其他方面的知识?额我懂得很少,记得有次有个评委发现我的 app 里内存泄漏导致 crash 了,他问我为什么 crash 了,我答不出来;记得大四我去面试的第一家实习是英x流x说✍️,面试官和我说他们以前也有一个奖学金的 winner 实习生,表达的意思就是对我蛮期待的,但是后面的面试让他大跌眼镜,我对这次面试印象很深刻,我从第一个问题开始,没有一个问题是答对的(不夸张),答得面试官都有点怀疑人生,他的眼神像是在问我是不是来砸场子的,我对面试的这些内容算是一点都不懂,当时很难受,也很后悔,后悔第一次面试就去了最想去的公司。再之后经过了准备之后换了一家公司得到了 offer,所以在那段时间,促使我学习的是那种落差感,是那种想要再证明自己的不甘。再往后我去考研了然后技不如人没考上,那段时间更焦虑了,会一直督促自己去学习,所以其实失败才是我源源不断学习动力的源泉,也希望在今后的成长道路上,失败不会成我唯一的学习动力👨‍💻。

有什么想借助摸鱼周报宣传的?

明年计划换工作了,想去大厂被资本家摧残🤡,坐标上海,邮箱:mim0sa@qq.com,欢迎各位老板骚扰。

也希望大家多关注 iOS 摸鱼周报,如果有好的建议和意见快来告诉我们。