理查·罗素 Richard Russell 是美国地平线航空(Horizon Air)的一名地面服务人员,负责飞机的拖行和装卸行李等工作,没有飞行员执照或正式飞行经验。2018年8月10日,理查未经授权,在工作时间私自登上一架停泊的Q400客机并成功起飞。
理查驾驶飞机在普吉特海湾上空飞行了大约一个小时,甚至完成了一个“桶滚”(barrel roll)的高难度特技动作。最终,他故意将飞机坠毁在普吉特海湾中人烟稀少的科特罗岛上。罗素本人在事故中遇难,没有造成其他人员伤亡。联邦调查局调查排除了恐怖主义行为,认定理查是独自行动,并且最终的坠机是故意行为,被定性为自杀。
这个事件在当时引起了巨大的关注和讨论,很多人对他复杂的动机和在没有飞行经验的情况下做出危险飞行动作的能力感到震惊和好奇。他因为这次事件被一些网友称为天空之王“Sky King”。
在坠机前与空中交通管制员的无线电通话中,理查表达了各种情绪,包括道歉和迷茫。其中,理查提到想去看一条当时引起全国关注的虎鲸,这条虎鲸在当时因为带着死去的幼崽游动而受到广泛报道。理查主动询问那条引起关注的虎鲸的坐标,说:“我想去看看那个家伙”。他可能希望在人生的最后时刻,与一个具有象征意义的、自然界中的悲伤故事产生某种连接。
理查想去看的虎鲸名字叫 Tahlequah,编号是 J35。她是生活在普吉特海湾及萨利希海域的“南方定居虎鲸群”的一员。这个虎鲸群是濒危物种,数量稀少。
2018年7月,Tahlequah 产下一头幼崽,但幼崽很快就死亡了。令人心碎的是,Tahlequah 没有放弃幼崽的尸体,而是用头顶着它,或拖着它,进行了长达17天的游动。在这17天里,Tahlequah 带着死去的幼崽游过了大约 1000英里(约1600公里)的距离,整个过程被科学家和公众目睹。
科学界认为,虎鲸携带死去的幼崽是一种已知的“哀悼”行为,但 Tahlequah 持续的时间是前所未有的,被媒体称为“哀悼之旅”。
鯨魚在不在?理查,理查,只有你自己知道。
在日常的 SwiftUI 开发中,我们时常会遇到一些令人困惑的 UI bug。它们中的大多数可以通过调整视图布局或状态管理来解决,但偶尔,我们会遇到一些“幽灵”般的问题——它们似乎违反了我们对框架的直觉理解,只在特定的系统版本和交互场景下出现。
最近,我就与一位开发者一同经历了一次这样的“探案”过程,最终解决了一个在 iOS 17 上关于嵌套手势的顽固 bug。这篇文章将复盘整个排查思路,并从中提炼出一套可供借鉴的调试心法。
我们的问题发生在一个常见的界面布局中,一个无法再次点击的卡片:
诡异的现象出现了:
这无疑是一个典型的、由复杂手势冲突引发的、特定于 iOS 17 的系统 bug。面对这样一个问题,我们的排查思路遵循了从“最小侵入性修复”到“根本性重构”的路径。
假设: 可能是 Menu 关闭的动画和状态清理,与父 Button 恢复可点击状态之间存在时序冲突。Menu 关闭得太快,导致父 Button 的手势识别器没来得及重置。
方案: 使用 DispatchQueue.main.async 将“复制文本”这类操作延迟到下一个主线程运行循环中执行,给 SwiftUI 留出“反应时间”。
// 在 QuestionItemView 的 Menu Button Action 中
Button("复制文本") {
DispatchQueue.main.async {
self.copyQuestionText()
}
}
结果:失败。 延迟执行并没有解决问题,这说明问题并非简单的时序冲突,而是一种更深层的状态“卡死”。
假设: 问题出在外层 Button 的默认样式和行为上,它与内部 Menu 的手势产生了冲突。
方案: 在 CategoryPageView 中,为包裹 QuestionItemView 的 Button 添加 .buttonStyle(.plain) 修饰符。这是解决嵌套按钮交互问题的标准做法,它能移除父按钮的默认视觉效果,并简化其手势处理逻辑。
// 在 CategoryPageView 的列表中
Button { ... } label: {
QuestionItemView(...)
}
.buttonStyle(.plain) // 尝试消除手势冲突
结果:再次失败。 连标准方案都无效,这让我们意识到 bug 的顽固程度超乎想象。它不仅仅是样式或行为冲突,而是手势识别器本身在框架层面被破坏了。
假设: 既然无法阻止状态被破坏,那我们能否在事后强制刷新视图,来“解救”那个卡死的 Button?
方案: 在 QuestionItemView 内部创建一个 @State 变量(如 bugWorkaround: UUID),并在执行完“复制文本”后更新它。同时给视图根节点添加 .id(bugWorkaround) 修饰符,强制 SwiftUI 认为这是一个全新的视图,从而进行彻底的重绘。
// 在 QuestionItemView 中
@State private var bugWorkaround: UUID = UUID()
var body: some View {
VStack { ... }
.id(bugWorkaround)
}
func copyQuestionText() {
...
bugWorkaround = UUID() // 强制刷新
}
结果:依然失败。 这是最令人震惊的结果。它告诉我们,被破坏的状态甚至不在 QuestionItemView 自身,而是在其父视图 CategoryPageView 的 Button 的手势识别器中。子视图的任何“自救”行为都无法触及和修复父视图的这个“僵死”状态。
在穷尽了所有“变通方案”后,得出了最终结论:任何试图在保留嵌套按钮结构的前提下修复问题的尝试都注定失败。 问题的唯一解法,就是从架构上彻底消除手势冲突的根源。
最终方案:
1. 移除外层 Button:在 CategoryPageView 中,不再用 Button 包裹 QuestionItemView。
2. 精确定位手势:在 QuestionItemView 内部,为真正需要响应点击的区域(例如,上半部分的文本区 HStack)添加 .onTapGesture。
// 在 CategoryPageView 中 (移除 Button)
QuestionItemView(...)
// 在 QuestionItemView 中 (添加 onTapGesture)
VStack(spacing: 0) {
HStack { ... } // 正文区
.contentShape(Rectangle())
.onTapGesture {
openQuestion()
}
HStack { ... } // 底部状态栏,包含 Menu
}
结果:成功! 通过这个改动,卡片的点击手势和 Menu 的点击手势从“父子嵌套”关系变成了“兄弟并列”关系。它们各自管理自己的点击区域和事件,互不干扰,从而完美地绕开了那个深藏在 iOS 17 框架底层的 bug。
经验总结:调试 SwiftUI 交互问题的系统性思路
这次艰难的调试过程为我们留下了一套宝贵的排查思路:
1. 首先怀疑嵌套交互:当你遇到涉及按钮、手势、Menu、List 选择等交互元素的奇怪 bug 时,第一反应应该是检查是否存在“可交互控件”的嵌套。这在 SwiftUI 中是问题的常见来源。
2. 遵循由浅入深的修复路径:
3. 精确定义手势区域:使用 .contentShape(Rectangle()) 来明确地告诉 SwiftUI 一个手势的可点击区域,这可以避免很多由于布局透明区域导致的意外手势行为。
4. 接受并记录平台特性:要认识到,有时候代码逻辑本身没有错,是特定版本的操作系统框架存在 bug。在这种情况下,找到一个可靠的、能绕过问题的架构方案,比无休止地尝试小修小补要重要得多。
最终,我们不仅修复了一个 bug,更重要的是,我们通过一次系统性的探索引导,对 SwiftUI 的手势处理机制有了更深刻的理解。希望这次的“探案笔记”也能在你未来的开发工作中,为你提供一盏指路明灯。

一句来自布莱士·帕斯卡的名言说:“人类所有的问题都源于人类无法安静地独自坐在房间里。”我相信这句话中蕴含着很多真理。我认为,这句话中就包含了过上简单生活的关键。
首先,让我们谈谈这句话为什么是真实的…… 我们可以想象:你总是需要娱乐、分心、忙碌。这就是为什么许多人沉迷于分心、手机和忙碌。 如果你感到压力或不幸,你需要让自己麻木,因为你的悲伤或压力感会太不舒服而难以承受。你需要找到外部寻求快乐的方法——从在线购物、视频游戏、看电视或视频、社交媒体、食物、药物、酒精等。由于上述所有原因,你可能会花太多钱,在财务上遇到困难;你的健康状况可能会恶化;你的生活将会变得令人不堪重负。
现在让我们想象一下,你拥有了与自己独处并感到舒适的能力。这会带来什么结果呢?简单。以下是一些如果你能习惯安静独处,就会意识到的事情:
你不需要很多东西——不需要一大堆财产、购物、奢侈品。你不需要一直被娱乐,所以生活可以更简单。你更加满足,不需要太多的安慰(尽管安慰并不是坏事)。当事情变得混乱时,你更能控制自己的情绪,这使事情保持简单、更平衡。当你发展出这种能力时,生活就会变得简单。
这种能力需要通过练习来获得。而且练习可以相当简单(尽管并不总是舒适的)。 以下是如何练习的方法:
随着时间的推移,你可以更习惯于与自己相处。你可能感到无聊,这也是可以的——让自己体验无聊!但你也可以找到让自己娱乐的方法。反思你所感激的。反思你的更深层次的目的。反思你的所学。与自己玩游戏。给自己讲故事。让自己玩耍。
顺便说一下,你不必总是保持静坐,虽然保持静坐是有用的。但无目的地散步,或者在房间里做一些小动作,做一些拉伸,都可以成为你日常练习的一部分。
家里的 Macbook Pro 升级到 macOS 26 正式版后,发现播放音乐软件时会出现爆音/噼啪声的情况。这种情况在每次播放音频一小段时间后就会出现,并且所有应用程序都存在这种情况。
这是 macOS 26 的未修复的 bug。临时的解决方法是,在终端中重新启动核心音频进程守护程序,应该就可以解决问题了。代码如下:
sudo killall coreaudiod
不少人反馈该方法虽然暂时修复了噼啪声,但过一段时间它又会复现,所以必须每隔一段时间就运行一次此命令。所以最终还得等苹果官方的更新修复。
由于早期发现该问题的大多数是开发者,大家后来摸出规律,似乎只有打开 Xcode 的 iOS 模拟器时,才会听到爆音/噼啪声。一旦它们全部关闭,声音似乎又恢复正常了。所以把模拟器关掉也是一个临时的可选项。

很早以前,极简生活推崇者 Leo Babauta 就开始阅读关于简单生活的文章,那种生活图景深深吸引了他:宽敞、从容、专注、感恩,以及一种没有杂物干扰的富足感。
直到2005年左右,Leo 才真正开始认真对待这件事,因为那时的他的生活,跟“简单”两个字简直是南辕北辙。他不仅结了婚,还是六个孩子的爸爸!生活里堆满了用不完的东西,日程表被塞得密不透风,感觉自己做什么都力不从心,一团乱麻。
那么……他的这段旅程是怎么走过来的呢?
首先,他开始看清什么才是对他最重要的。 他经常问自己,生活中哪些人和事是他的核心(家庭、写作、阅读学习、锻炼)?哪些物品是他真正需要、真正在乎的?想清楚这些后,他的简化之路就有了清晰的起点。
第二,他开始一小步一小步地清理生活。 他明白,这不可能是一夜之间的革命。在简化过程中的每一步,Leo 都把它当成一次学习的机会,去感受生活变得更清爽是什么感觉。
第三,他开始有意识地放慢、甚至停止了“买买买”的脚步。 如果你一边扔东西,一边又不停地买新东西,那清理就变得毫无意义。当然,他现在也买东西,但频率比以前低太多了。
第四,他开始领悟“知足”意味着什么。 这才是最关键的一步。如果把你生活中所有额外的东西都拿掉,最后剩下的是什么?如果你能学会在拥有很少东西、做着任何简单小事时都能感到心满意足,那生活真的会容易很多。简单,也因此成了一件特别美好的事。
我也曾探索过极简生活的边界,一度自称是“极简主义者”。“极简”和“简单”有什么区别吗?其实没啥区别,那只是我探索“用最少的东西生活会是怎样”的一种方式。那段时间我扔了超多东西!过程还挺有趣的。
后来,我慢慢放下了“极简主义者”这个标签,不再执着于此,而是纯粹地去享受一种简单的生活。
现在,我的简单生活大概是这个样子的:
在工作上也保持简单。每天只列一小串待办事项,然后一次只专注做一件。运动也很简单,不需要太多复杂的器械。可以尝试跑步、举举铁、打打篮球、出门散散步。
在经历这一切的过程中,我一直在探索什么才是真正重要的。当生活被剥离到只剩下最核心的部分时,我发现,我终于可以过上一种与自己内心最重要的东西完全同频的生活了。
工作有时真的挺让人头大的,对吧?任务多到感觉快被淹没,选择困难症发作,不知道下一步该干嘛;总有些硬骨头一样的工作,只想一拖再拖;感觉自己永远追不上进度,还特别害怕那些有点难开口的对话。但如果我们能学着多信任自己一点,这一切都会变得轻松很多。
这里的“信任自己”,不是说要盲目自信,觉得自己能把每件事都做得完美无瑕。它是一种更柔软、更坚韧的信念:相信自己会全力以赴,并且,就算事情没那么顺利,我们也有能力去应对和收拾。
培养这种信任感并不总是一蹴而就,但今天我想和你聊聊,当我们开始这么做时,工作会发生哪些奇妙的变化。好啦,话不多说,我们来看看,当你开始真正信任自己时,工作中的哪些方面会悄悄变好:
我们之所以感到不堪重负、压力山大,其实往往不是因为事情真的多到做不完,而是因为我们打心底里不相信自己能搞定这一切。那种“被淹没”的感觉,本质上是一种“我应付不来”的恐惧。
但如果我们换个角度,试着信任自己呢?当然,我们可能确实无法完成清单上的每一件事,但那又怎样?如果我们信任自己,就会相信自己总有办法处理好后续。也许是和人重新协调一下截止日期,或是调整一下会议时间,如果真的不小心搞砸了什么,那就坦诚地道个歉。你看,这些我们都能处理好的。
当成百上千件事都在等着我们处理时,我们常常会卡在“下一步做什么”这个问题上,因为我们害怕做出错误的选择。但如果我们信任自己,其实完全可以凭感觉选一个就好。然后呢?就相信自己的这个选择。这样一来,事情就简单多了,不用再反复纠结,让自己陷入自我怀疑的泥潭。
一旦你凭着信任感选择了一项任务,接下来要做的就简单了:把其他事情暂时轻轻放下,全心全意地投入到眼前这一件事上,就好像它此刻是全世界唯一重要的事。你会发现,用这种心态去工作,专注力会自然而然地回来。
我们之所以拖延,往往不是因为懒,而是因为我们害怕自己搞不定那些困难。事情一旦变得棘手,我们下意识的反应就是逃避。
但如果我们信任自己,我们就会相信,自己完全有能力去面对一点点挑战。我们可以去试一试,无论结果如何,我们都能接得住。所以,下次再对某个任务或项目感到畏惧时,试着信任自己:先深呼吸,然后带着一点点勇气,一头扎进去就好。
我们常常会(有时甚至是不知不觉地)回避那些棘手的谈话,因为我们害怕把事情搞砸,或者害怕那种不舒服的感觉。但逃避很少能解决问题,反而可能让情况变得更糟。
如果我们信任自己,就可以深呼吸,然后坦然地去开启对话。让自己放松一点,和对方聊一聊。允许他有他的反应,关心他的感受但又不过分放大,同时相信自己完全有能力承受那一点点不自在的感觉。
你看,这五件事,恰恰是工作中大部分压力的主要来源。当它们都变得轻松起来,整个工作状态自然也就顺畅多了。当然,这些道理在生活中也同样适用。
那么,到底该怎样培养这种宝贵的自我信任呢?这是一个需要我们刻意练习的过程。
你可以试试这样一个小练习:
如果你能每天都这样练习几次,一天又一天,你会看到自己惊人的成长。
两周前在推上看到这条帖子。视频内容是一位临终的老人,说看到自己妈妈在前面,就是幻视。帖子配文是:“女儿已经知道,此时的场景意味着什么了”。当时自己在这帖子上停留了好一会,是因为一周前回老家看望养老院的外公时,他也是这样。
那时,外公的眼睛一直看着自己左上方的天花板上,固定是这个方向,然后一直不停地喃喃自语,声音还挺有中气。我在场的时候,他一直念叨的是以前住他楼下的一个好朋友,一直在叫他一起出去兜风,看看家乡市容变化,说自己外孙回来了,有自己的车,很方便,不用怕,兜兜风再回家吃饭。一周后我大舅妈去看望他时录了视频,外公念叨的是让我小舅(他最小的儿子)收拾被褥,从广州回他床边一起住。
从帖子的留言来看,很多人都遇到过类似的经历:临终的老人在快过世时会做梦或者看见亲人,好友来接。这个时候完全没必要在意或纠正他的话,只要顺着他说就可以了。人临终前会看到曾经自己最在意的人吗?还是只是身体机能衰退,自己潜意识投射引发的幻觉?
虽然隐约有了某种预感,但从外公那还算敞亮的声音,以及皮肤的血色来看,觉得他身体机能似乎还行。所以内心有个祈祷是但愿能熬过明年春节,明年就是外公一百岁了。
昨天是中元节,今天一早起床,收到我妈的信息,说外公刚刚过世了。
我想如果一个人临终前能看见自己的亲人好友,这种幻觉是否至少稍微让死亡没那么可怕?人生到此就像一场梦,梦的最后有自己在这个世上最在意的人陪伴着。

传统上认为对抗权力拥有者,就是明着与其对抗,例如通过游行等明显的抗议活动,表达对其的不满。而在一个监管极其严厉的环境里,以往的很多做法都是奢望、不现实。普通人在强大的体制面前是真正的弱者。弱者还能做什么呢?
有个说法提到,集权国家的趋势,就是要将社会的方方面面管起来。例如结婚、生育都要管起来。弱者没有办法去践行游行、集会、甚至发批判文章等活动。但既然体制的触手是深入方方面面的,那抗争其实也可以发生在方方面面,而并非只有直接面对面的抗争才有用。
所以当下的躺平、不婚不育、不买房都是弱者的抗争方式,总之你自己认为对什么领域的制度和条例不认可,你都可以选择不合作进行抗争。而不是自己希望环境改变,但又一直在 “体制设计好的游戏中” 循规蹈矩地玩下去。
关于勇气,近年听得最多的一句话是罗翔的: 在人类所有的美德中, 我最欣赏的是勇敢,因为当下勇敢是最稀缺的。这句话和另一句 “学了那么多道理,但还是过不好人生” 有着相同的击中人心的点,普通人在遭遇一些困境时,会觉得“更好”、“更向往” 的选择其实自己也知道,但往往迈不出践行的那一步,主要都是因为自己缺少勇气。我自己也持有这种思维模式很多年,遇到许多纠结的时刻都会叹息自己不是一个很有勇气的人。
直到最近听到另一个说法,说:有时候人类的认识,是指经验上的认知,而非道德层面的认知。当这些认知趋向于是道德层面的认知的时候,就算你不是一个很有勇气的人,只要意志不是那么薄弱,最终也能走向行动。例如孝顺,作为经验的认知,我们知道世界上有这么一种社会习俗或约定,和知道世界上各种奇闻趣事一样没什么区别;但为什么我们不孝顺,就会有愧疚感,从而导致最终都会有具体的行动?因为孝顺这件事在我们长年累月的生活中,已经上升到我们的道德认知层面。
比如,某个同性恋者,即使知道自己的性取向,但也不敢出柜。在过去会归咎于没有勇气面对家庭和社会的压力,但按上面的另一种说法,可能是他对这件事的认知还没有上升到道德层面的认知。也就是说他知道世界上存在同性恋这样的合理行为,但对自己来说,这并非是一件好或坏之事。如果他某天认为 “不敢承认自己同性恋事实” 的这个行为,是一个在道德上“极坏”的事情,那他可能就会去做。
按这个说法,推动行动并不是只靠勇气,反而是靠更有强度的认知,即道德层面的认知。社会里,许多观念的推广都是这样做的,它不会只把这种观念当作经验的认知,例如”世界上有同性恋“;而是要把它当作道德的认知,即”同性恋是道德败坏的“。这样才能有效地影响人的决定和行为。当”同性恋是正常的“成为了社会大多数人的道德认知,那很多人就并不需要强迫自己有勇气才敢出柜了。当然这些都是属于社会层面的。
而说回个体,一个认知是否属于道德层面,人与人之间就千差万别了。“不忠于自己内心” 是坏的吗?”选一个不爱的人结婚,只是因为他很有钱“ 是坏的吗? 需要因此感到愧疚吗?每个人的答案都不一样。如果按上面的逻辑,有时候人选择的区别不能说是因为”勇气“,可能是每个人心里的那个道德标尺不一样。
我个人认为比较理想的状态是:在社会层面上,许多认知尽可能不要趋向于道德认知。而在个人层面上,道德标准应不断提高。简单说就是严于律己,宽于待人。严于律己可以帮助认识自己到底是个什么样的人,以及要成为什么样的人;宽于待人是说除了社会法律规定的那些准则之外,其他的认知应默认当作经验认知,算是自己的知识盲区,带着好奇心去了解即可,不做判断。
人群是那么像羊群,这种相似甚至可以体现在定价心理学上。心理学上说人们做选择,会倾向于不选择极端,例如某企业推出一款产品的几个型号规格,人们一般不会选最便宜的,也不会选最贵的,而会选中间的一档。
这样的道理简直和枪打出头鸟一样,所谓的羊群特点,就是我在群中随波逐流,而处于群边缘的总是充满危险。人们在生活中的诸多决策都符合这个特点,考研考公、结婚生子。
羊群里的“羊”很难看见远离中心的边缘“羊”,它们周边被无数的羊挡住了视线,它们通过口口相传来了解外部的世界,自然它们也很难看到边缘“羊”所看到的美丽风景。
羊群里的“羊”甚至会通过嘲笑边缘“羊”的遇难来强化自己的选择逻辑——呆在中间是最安全的。然而可怜之处是羊群中的羊如果遇难了,除了紧挨着的羊知道外,这条消息几乎都不会在羊群中传播,也没人关注,因为羊群中的羊实在太多了。
许多家长都希望孩子去学习编程,同时我们也知道未来若干年后,可能许多编程语言,编程方式都会发生天翻地覆的变化,那孩子的这段学习时间会不会白学了呢?如果还是让孩子学习编程,那主要学习什么呢?那很多时候我们会说,我们希望孩子提高逻辑思维能力,学习编程重点是学习编程的思维。
这里提到的编程思维,也可以叫计算思维。计算思维通常指的是一种处理信息和问题的方式,强调运用计算机科学的思维方式来解决各种问题。一般来说,计算思维包括以下四个要素。这些要素共同构成了计算思维的基本框架,使人们能够更有效地利用计算机科学的原理来解决各种复杂的问题。计算思维不仅仅是对计算机科学专业的学生有用,对于任何需要解决问题、优化流程的领域都有实际应用。
首先是将一个大问题分解成更小、更容易处理的子问题的能力。这有助于理清问题的结构和逻辑。大部分人如果遇到一个没做过的很难的任务,通常都会束手无策。而问题分解的能力能够让你在面对困难任务时,不至于害怕止步不前,而会尝试去拆解它。
辨别问题中的模式或规律。通过识别模式,可以更好地理解问题并推导出解决方案。这方面在孩子学习编程的过程中非常重要的,并且实践的可行性相对较高。例如使用 Swift Playground 设计的游戏化任务去训练特别好,因为它任务呈现的形式是在地图上搜集宝石,是很具体看得见的可视化。孩子需要多观察地图去识别出 —— 可以复用或者具有对称性的模式。模式识别能力的训练,可以形成一种“直觉”,为将来的算法设计打好基础。
识别问题中的模式,提取出关键信息,将其抽象为更一般的形式。这有助于建立通用解决方案。
制定解决问题的详细步骤或算法。这涉及到逐步定义解决方案的过程,以便计算机或其他系统可以执行。这个对孩子来说还很难,要随着数学知识的提升,逐步建立和加强。
https://www.seangoedecke.com/good-system-design
GitHub 的高级工程师肖恩·戈德克(sean goedecke)谈何为良好的系统设计…
1、程序设计是组装代码,系统设计是组装服务。程序设计的组件是变量、函数、类等,系统设计的组件是服务器、数据库、缓存、队列、事件总线、代理等。
2、如果一个系统很长时间不出错,它的设计就是良好的。如果你进一步看了代码,脱口而出:”哈,这比我想的要简单”,或者”这个部分不用我操心,即使出问题也容易解决”,它的设计就是优秀的。
3、良好的系统设计,总是从一个有效的简单系统发展而来。千万不要从零开始设计一个复杂的系统。
4、系统设计的难点在于状态。尽量采用无状态组件,最小化”有状态组件”的数量。状态的复杂性在于,你无法简单地重启服务。一旦出错,往往需要手动修复状态。
5、状态需要保存在数据库。数据库是最重要的系统组件,用来管理状态。数据库的设计目标是每张表易于理解:打开看一下表结构,就能大致了解存储的数据内容及其原因。千万不要采用复杂的表结构(也就是数据结构),会给代码带来极大的复杂性和性能约束。
6、数据库往往是系统瓶颈,因为每个页面请求可能要调用数十次、数百次数据库,而且是按顺序调用。为了避免瓶颈,数据库可以做成一个写入节点和多个只读副本。数据查询都发往只读副本,数据写入发往写入节点。写入节点与只读副本之间,存在数据复制延迟。如果更新一条记录后,你需要立即读取它,那么可以将数据放入内存,写入数据库成功后从内存读取。
7、耗时的操作要拆分出来,放在后台作业(即系统外部的单独服务),排队完成。后台作业主要分成两个组件:一个队列服务,一个作业运行器(从队列中获取任务并执行)。队列任务的软件,可以用 Redis(需要尽快执行的任务),也可以用数据库(不着急的任务)。
8、如果数据的生成速度和读取速度不匹配,经典解决方案就是缓存。缓存的最简单做法,就是把数据保存在内存,否则就使用专门的键值存储软件(比如 Redis 或 Memcached),后者的好处是多个服务器可以共享缓存。初级工程师希望缓存所有内容,而高级工程师希望尽量少用缓存。因为缓存是状态的来源,不可避免需要校验状态和处理状态过期。
9、除了缓存和后台作业,大型系统通常还有事件中心,例如 Kafka。事件中心也是一个队列,存放的是”某件事发生了”的消息。比如,用户注册触发了”新帐户创建”事件,该事件就放入事件中心,然后由事件中心去通知订阅该事件的多个服务:发送欢迎电子邮件、设置个人空间等等。事件中心适用于:发送事件的代码不关心其他服务如何处理事件,或者事件量很大且对响应时间不太敏感。
不要过度使用事件,很多时候,更简单的做法是让一个服务请求另一个服务的 API。为了便于除错,所有日志最好都放在一起,你可以立即看到另一个服务的响应。
10、推拉:如果数据需要传送到多处,有拉取(pull)和推送(push)两种选择。一般来说,拉取比较简单(比如大多数网站采用的轮询),推送更节省资源,不需要用户主动请求数据,一旦后端数据发生变化,服务器主动将数据推送给每个客户端。
如果你确实需要向100万个客户端提供最新数据(就像 GMail 那样),应该采用推送还是拉取?这要视情况而定。如果采用推送,就要把每次推送放入一个事件队列,并让一大群事件处理器从队列中拉取数据并推送。如果采用拉取,就要部署一堆(比如100台)快速的只读缓存服务器,处理所有读取流量。

类型就像是生产一个东西的蓝图,它描述了这个东西长什么样,有什么功能。关于类型主要讲解:
实例就是根据“类型”这个智能蓝图制造出来的具体的一个东西,例如一辆蓝色的甲壳虫汽车。实例和类型的关系可以用这样的类比:“人”是一种类型,但它只是一个概念。具体存在的是“小明”和“小红”,他们就是“人”的具体实例。
“类型”这个智能蓝图不能直接执行我们的命令,只有具体的“实例”可以。那我们就要先根据蓝图生产出一些实例。这就是初始化对象。初始化(生产)出来的对象,一般要给它们起个名字,方便后面指挥它们执行命令。否则有多个实例的时候,你想执行某个命令,它们不知道应该谁去做。
既然有可能有多个实例,我们输入命令时要明确指出是哪个实例来执行。
这里反复跟孩子强调一个习惯,就是想让人帮你做事情之前,要先称呼别人的名字。这也是现实中一个好的礼貌习惯。因为孩子小时候很多都是自我中心,认为全世界都是围着自己转的,想让别人帮忙的时候会说“帮我把饭拿过来”,而借助编程学习,我也会跟孩子强调,应该说“爸爸,帮我把饭拿过来”这样才是好习惯。否则计算机也不知道要怎么响应你的命令。
如果用 Swift Playground 学习以上这一套类型和实例的概念,基本都是现代的面向对象编程的理念。日后迁移到其他现代编程语言的学习中基本都是没问题的。理念都适用,不用担心。

当我们经常需要输入一组命令(包含若干个命令)时,会觉得很烦。这时我们可以将这样的一组命令定义成函数,方便使用。讲解函数主要是:
当我们需要反复输入一组命令,但是又希望有些区别,怎么实现呢?这就引出变量。讲解变量主要是跟孩子解释,我们定义了函数,但是其中有些细节是只能在具体使用函数时才能确定,没法提前预知,所以我们先给它起个名字占位,让使用者在使用时再确定。例如我们定义了一个函数,它调用时会向前走1步。但是这样的函数不方便,因为每次走多少步可是根据当时情况不同经常会变化的,这时我们就可以引入步数作为参数。在调用函数时候再指定。
这里可以联系方程、代数的理念给孩子讲解,人们会将一些还不知道不确定的事物,先起个名字表示起来。而不会因为不确定就卡住停滞不前。
当需要储存一些信息(例如游戏中的得分)时,就需要变量了。计算机能记录信息的量可比人大多了,所以我们可要经常好好利用变量。讲解变量主要包括:

学习具体的编程知识,首先都是将命令执行的逻辑。
编程时,主要就是编写一个个命令,输入给计算机去执行。你的目标就是弄清楚,哪些命令,怎么排序才会得到最佳的效果。
这里我花了很多时间反复跟孩子强调的是:计算机执行命令,正常都是从上到下,按顺序执行的。并且是做完一条命令,才会做下一条命令。(初期还不涉及同步、异步这些高级概念,所以先略过不讲)。命令总是按顺序执行的,这条规则看似简单,但如果没有尽早植入观念内,后面就会犯很错低级错误,且不知道怎么找原因。这是我初学编程时犯了很多错误得到的经验。
当遇到一些没法事先确定的情景,我们就没有办法提前设置好一组命令。这时就引出“判断”功能。判断在计算机中主要是通过 if ... if else ... else 去表示。有判断,自然就引出判断表达式(也就是我们判断什么)。常见的判断表达式有 ==、<=、>= 等。这时可以联系起小学数学知识给孩子讲解,也很容易理解。
一个判断表达式,例如 3 <= 5 是有具体的表现值的,这个值就是 “对 True” 或者 “错False” 这就是布尔值 Bool。布尔值是只含有两种可能性的一种类型,在英语判断题中的填写的 T 和 F,也是从这里来的。
当遇到更复杂的场景时,经常一个判断条件还不够。例如我们过马路,除了判断当前是否时绿灯,还需要判断两边看看有没有车开过来吧,不然就有被撞的可能。这时就可以引出逻辑运算符,无非就是 && 与、|| 或、! 非,三个。逻辑运算符能够让我们的条件判断更加具体和精准。
再接下来,我们会发现计算机最初的游戏并不在于很聪明,而是在于它的运算速度比人快很多。同时我们经常都需要给计算机输入大量的重复命令,这就需要引入循环来帮我们简化工作。
基本的循环有两种:for 和 while。两者的区别是:for 循环是事先可以知道一件事需要干多少次的,于是可以提前指定迭代的次数。而 while 循环是无法提前知道一件事情要做多少次,它通常是以达到某个条件为判断结束的标准。例如不断抄写错别字直到会了为止。