普通视图
《Flutter全栈开发实战指南:从零到高级》- 12 -状态管理Bloc
SwiftUI ScrollView导致视图塌陷(高度为0)问题
iOS/Macos C++ thread_local 具体实现分析
SwiftUI 组件开发: 自定义下拉刷新和加载更多(iOS 15 兼容)
AppStore卡审44小时的产品,重新提交后已经过审了。
SwiftUI 支持呼吸动画的图片切换小控件
Swift 扩展(Extension)指南——给现有类型“加外挂”的正规方式
【Swift 错误处理全解析】——从 throw 到 typed throws,一篇就够
【Swift 并发编程入门】——从 async/await 到 Actor,一文看懂结构化并发
《Flutter全栈开发实战指南:从零到高级》- 11 -状态管理Provider
欧陆风云 5 的经济系统
很早从“舅舅”那里拿到了《欧陆风云 5》的试玩版。因为开发期的缘故,更新版本后需要重玩,所以一开始只是陆陆续续玩了十几个小时。前段时间从阳朔攀岩回来,据说已经是发售前最后一版了,便投入精力好好玩了 50 小时,感觉非常好。
我没有玩过这个系列的前作,但有 800 小时《群星》的经验,还有维多利亚 2/3 以及十字军之王 2/3 的近百小时游戏时间,对 P 社的大战略游戏的套路还是比较了解的。这一作中有很多似曾相识的机制,但玩进去又颇为新鲜,未曾在其它游戏中体验过。
我特别喜欢 P 社这种在微观上使用简洁公式,宏观展现出深度的游戏设计。我试着对游戏的一小部分设计作一些分析,记录一下它的经济系统是如何构建的。
这里有一篇官方的开发日志:贸易与经济 其实说得很清楚。但没自己玩恐怕无法理解。
我在玩了几十小时后,也只模糊勾勒出经济系统的大轮廓。下面是我自己的理解,可能还存在不少错误。
EU5 的经济系统由人口/货币/商品构成,市场为其中介。
游戏世界由无数地区构成。地区在一起可以构成国家,也能构成市场。一个国家可以对应一个市场,也可以由多个市场构成,也可以和其它国家共享市场。
每个市场都会以一个地区为市场中心,这反应了这个地区的经济影响力,它不同于国家以首都为中心的政治版图。市场自身会产生影响力,而市场中心地区的所属国家则因其政治影响力而产生市场保护力,两相作用决定了市场向外辐射的范围。每个地区在每个时刻都会根据受周围不同市场的影响强弱,最终归属到一个唯一市场。
每个地区有单一的原产物资(商品)。原产在版图上不会改变,可以被人口开发,计入所属市场供给。
地区上可以修建生产建筑,生产建筑通过人口把若干种商品转换为一种商品。转换效率受地区的市场接入率影响。市场中心地区的接入率为 100% ,远离市场的地区接入率下降,在边远地区甚至为 0 (这降低了同样人口的工作效率)。注:原产不受市场接入率的影响。
市场每种商品有供给和需求。每种商品有一个额定价格。需求和供给的多寡决定了目标价格和额定价格的差距,目标价格在 10% 到 500% 间变化。实际价格每个月会向目标价格变动,变动速度受物价波动率影响。
商品的价格减去原料成本(原产物资没有原料成本)为其利润,利润以货币形式归属生产人口,并产生税基。
负利润的生产建筑会逐渐减员,削减产量,除非政府提供补贴。缺少原料的生产建筑会减产。
人口会对商品产生需求。食物类型的商品是最基本的需求。 不能满足食物需求的人口会饿死,不能满足其它需求的人口会产生不满。
对于单一市场:
- 人口在市场上产生商品需求。
- 生产建筑通过人口生产出商品供给市场。
- 市场上的供给和需求影响了商品价格。
- 人口通过生产获取货币形式的利润,并产生税基。
税基中的一部分货币留给人口,一部分以税收形式收归国库。
货币用来投资新增生产建筑,或对其升级。建筑升级需要商品,这部分商品以需求形式出现在市场。人口会用自己的钱自动投资建筑,玩家可以动用国库升级建筑。
多个市场间以贸易形式交换商品:
每个市场有一个贸易容量,贸易容量由市场中地区中的建筑获得。贸易容量用来向其它市场进出口商品。
市场所属国家拥有贸易竞争力,贸易竞争力决定了向市场交易的优先级。高优先级贸易竞争力的市场先消耗贸易容量达成交易。
商品在不同市场中的价差构成了贸易利润,其中需要扣除贸易成本(通常由两个市场中心间的距离决定)。贸易利润的一部分(由王权力度决定)进入国库,其它部分变为税基。
在国家主动进行贸易外,人口也有单独的贸易容量,自动在市场间贸易平衡供需。
我觉得颇为有趣的部分是这个经济系统中货币和商品的关系。
游戏中的生态其实是用商品构成的:人口提供了商品的基本需求,同时人口也用来生产它们。在生产过程中,转换关系又产生了对原料的需求。为了提高生产力,需要建造和升级建筑,这些建筑本身又是由商品转换而来。所以这么看来,是这些商品构成了这个世界,从这个角度完全不涉及货币。
但货币是什么呢?货币是商品扭转的中介。因为原产是固定在世界的各个角落的,必须通过市场和贸易通达各处。
建立一个超大的单一市场可以避免贸易,它们都直接计入市场中心。但远离市场中心生产出来的商品(非原产)受市场接入度的影响而削弱生产效率,所以这个世界只能本分割成若干市场。不同市场由于供需关系不同而造成了物价波动。价差形成贸易的利润,让商品流动。这很好的体现了货币的本质:商品流动的中介。
政府通过税收和其主导的国家贸易行为获得货币,同时也可以通过铸币获取额外收益(并制造通货膨胀)。再用这些货币去投资引导世界的发展:建造和升级生产建筑光有钱是不行的,必须市场上有足够的商品;没有钱也是不行的,得负担得起市场上对应商品的价格。
注:铸币可以看作是用黄金或白银直接变成货币的生产行为。它的基础产能似乎和人口无关,也不需要分配人口去工作,也没有特定的生产地点 。只需要在国家结余菜单中勾选即可。选择更大的产能会导致货币贬值,本质上是单位黄金/白银兑换的价值变少,但游戏中是以通胀变现的。即,生产出来的货币并没有变少,但国家的通胀增加了。铸币行为表现为在市场中自动消耗一定量的贵金属。如果整个市场中都没有贵金属,也就无法铸币。
11 月 7 日补充:
玩游戏的时候,一直没弄懂游戏中市场间的交易是怎么撮合的。官方 wiki 现在没有详细解释,游戏内的说明也不够完整。昨天和玩友讨论后,又仔细在游戏中研究了一下,发现其实游戏中说明还是很丰富的,只是细节散布在不同界面中。我是这样理解的:
由于同一市场中可能有不同国家的贸易建筑,所以导致了在一个市场中,不同国家分别拥有一定的贸易影响力和贸易容量。注:在友好的外国,可以修建贸易站,这种建筑占用当地的人口,为当地产生税基,但可以为本国带来贸易影响力和贸易容量。这些数值可以在市场界面上看到。
贸易影响力在出口紧俏商品时起作用,即商品的当月节余不能满足当月出口需求时,高影响力的贸易订单先被满足。在订单详情界面中可以看到相关商品的供给和需求细节。我不确定进口商品过多时是否也按影响力排序,游戏内的说明中说贸易影响力不影响进口,但我认为受商品仓库上限的影响,如果当月进口数量太多会超过库存上限,同样需要对进口订单排序。只不过在库存过高时,通常交易是负利润,很难出现这种情况罢了。
即使在一个外国市场内没有贸易容量,也可以发起贸易,只要该市场在你拥有贸易容量市场的贸易范围内。计算贸易距离似乎看的是市场边界的最短距离(而不是市场中心的距离),在贸易订单界面可以看到商品的出口地点和进口地点,很多时候并不是贸易中心地。贸易范围看起来是一个国家相关值。所以,在周边国家建贸易站可以扩展可以交易的市场。但如果在交易对手的市场内没有贸易容量,那么贸易影响力一定为零,对紧俏商品的采购很可能失败。
在两个市场间对某种商品进行贸易,会产生订单,同一商品只会有一张。订单上会指定商品数量,数量越多,消耗的贸易容量越大。每种商品有一个基础消耗比例,根据贸易距离乘一个系数。所以距离越远的贸易,消耗的贸易容量越大。在下订单时,并不能立刻确定是否能成交。如果商品库存不够,需要根据贸易影响力排序。低影响力国家发起的订单可能不能完全满足,甚至为 0 。这种情况下,订单所属的贸易容量就被浪费掉了。
每个订单可以看作是一支商队,贸易容量就是商队的规模。长途贸易需要支付额外的费用,可以理解为商队的工资以及海峡过路费等。这个费用以贸易容量为比例支付,而不是实际成就额。所以,即使订单数量为 0 ,这些额外费用也是要支付的。所以订单有亏本的风险。所以大容量订单(未能成交的)风险更大。如果是采购战略物资,采用多个小订单分散去不同市场采购获得足够成交数量更安全。
订单可以被人工固定,每月固定执行,但有更大的可能无法成交。大部分情况下,采用系统自动生成的订单。系统看起来会按利润多寡分配贸易容量。但似乎也不会产生过大的订单,具体规则没有写。总之,系统自动生成的订单浪费贸易容量的机率更小。
人口自身也有一定的贸易容量(独立于国家可以控制的贸易容量)自发进行贸易。对应的订单在游戏界面中无法查到。但在商品界面详情中可以看到一个合计数值,显示该商品由人口自发贸易行为产生的输入或输出总量。人口如何产生这些贸易的规则不清楚,从文字说明看,和人口自身的需求相关。我怀疑也和贸易利润相关。
《Flutter全栈开发实战指南:从零到高级》- 10 -状态管理setState与InheritedWidget
Swift 泛型深度指南 ——从“交换两个值”到“通用容器”的代码复用之路
Swift 中的不透明类型与装箱协议类型:概念、区别与实践
惊险但幸运,两次!| 肘子的 Swift 周报 #0109
![]()
📮 想持续关注 Swift 技术前沿?
每周一期《肘子的 Swift 周报》,为你精选本周最值得关注的 Swift、SwiftUI 技术文章、开源项目和社区动态。
- 📬 在 weekly.fatbobman.com 免费订阅
- 💬 加入 Discord 与中文 Swift 开发者深入交流
- 📚 访问 fatbobman.com 查看数百篇深度原创教程
一起构建更好的 Swift 应用!🚀
惊险但幸运,两次!
我的岳父岳母的身体一向很好,尤其是岳父,八十多岁了从未住过院。但在短短的时间里,他们先后经历了两场惊魂时刻——好在结果都十分幸运。
先是岳母。那天我们刚从机场接她回家,发现她脸色不好,自述昨晚没睡好,似乎又有肾结石发作的迹象。正当我们决定取消外出用餐时,她突然开始剧烈发抖,但额头仍是凉的。测量血氧后发现指标在快速下降——即使立即开始吸氧,最低时也掉到了 78。
救护车到达时(约 40 分钟后),体温已升至 39.8℃;送医时更是接近 41℃。人的状态非常差。幸运的是,在此前她提到过肾结石的不适,医生据此快速展开检查,确诊为尿源性脓毒症——结石堵住输尿管,细菌在尿液中迅速繁殖,最终进入血液。 医生直言,若再晚些送来,后果可能不堪设想。想到她刚下飞机,我们也不禁后怕——幸亏航班没有延误,幸亏那天决定乘坐早班机。经过紧急手术引流后,她的状况在第二天便明显好转,数日后顺利出院。
不久之后,又轮到岳父经历一次“惊险但幸运”的考验。 在照顾岳母期间,他出现咳嗽并伴随低热。CT 显示只是支气管扩张,但考虑到年龄,我们坚持让他住院观察。 在例行心电图检查中,医生意外发现他有房颤,心率在 130–170 之间剧烈波动。令人吃惊的是,他本人毫无不适感。事后他说,今年 5 月体检时医生就提醒过有房颤,但他没当回事,也没有告诉我们。 由于心率过快,治疗重心立即转为控制心律。幸运再次眷顾——在使用胺碘酮(Amiodarone)数小时后,心率逐渐恢复至 80 以下,成功复律。如今虽然仍在住院,但情况稳定,只需出院后按医嘱服用控制心率与抗凝药物即可。这次的阴差阳错,也让他在不经意间避免了房颤可能带来的更严重后果。
短短一周内的两次意外,让人既感叹生命的脆弱,也更懂得珍惜当下。幸运往往属于那些及时行动、认真对待健康警示的人。愿我们都能对自己和家人的身体多一分关注,少一分侥幸。
近期推荐
让 onChange 同时监听多个值 onChange(anyOf:initial:_:)
onChange 是 SwiftUI 中常用的状态监听工具,开发者可以通过它在视图中针对某个值的变化执行逻辑。不过,尽管该修饰符历经多次改进,它仍然只能同时观察一个值。若要在多个状态变更时执行相同操作,就需要重复添加多个内容相同的 onChange 闭包。在本文中,Matt Comi 借助 variadic generics(可变参数泛型),让 onChange 能够同时监听多个值,从而构建出一个更高效、更优雅的解决方案。
SwiftUI 中的滚动吸附 (ScrollView snapping in SwiftUI)
从 iOS 17 开始,开发者可以通过 scrollTargetBehavior 控制 ScrollView 的滚动行为,比如按页面吸附或按视图吸附。Natalia Panferova 在这篇文章中详细探讨了该 API 的用法,并分享了实践经验和需要注意的陷阱:视图对齐模式要求每个吸附目标适配可见区域;当单个项目尺寸超过容器大小时,滚动会感觉"卡住";对于超大项目,需要考虑自定义 ScrollTargetBehavior。
在 精确掌控 SwiftUI 滚动:自定义 Paging 实现 一文中,我也介绍了如何通过自定义 ScrollTargetBehavior 解决横屏模式(Landscape)下滚动偏移、页面对齐不精确的问题。
Swift 类型检查器改进路线图 (Roadmap for improving the type checker)
作为 Swift 开发者,你一定不会对那句 "unable to type-check this expression in reasonable time" 感到陌生。为什么会出现这个编译错误?Swift 又将如何减少这种情况的发生?Swift 核心团队成员 Slava Pestov 在这份路线图中给出了详细的解释与改进方向。
该问题源自约束求解(constraint solving)的指数时间复杂度。为防止编译器陷入无休止的回溯,Swift 设定了两项限制:析取选择次数上限(100 万次) 和 求解器内存上限(512 MB)。Swift 6.2 通过优化底层算法实现了初步提速,而 Swift 6.3 引入的新析取选择算法与内存优化带来了显著改进:同一测试项目的类型检查时间从 42 秒降至 10 秒。Swift 团队的目标是在不牺牲语言表达力的前提下,通过算法与实现层的持续优化,将“指数最坏情况”压缩到更少、更边缘的实际场景中。
iOS Sheet 的现代化演进 (Playing with Sheet (on iOS))
在 iOS 上,Sheet 曾经意味着“全屏接管”——从底部滑出、阻挡一切、等待用户点击“完成”。但如今,这种模式已经过去。Apple 重新定义了内容的呈现方式:在新的设计哲学下,Sheet 不再是中断,而是节奏的一部分——它可以滑动、漂浮、扩展、折叠,让界面保持连贯与呼吸感。Danny Bolella 从多个层面展示了现代 Sheet 的可定制特性,并通过实例演示了这些特性如何让 Sheet 从“流程中断”转变为“上下文扩展”。
针对 Sheet 无法自动适应内容高度的问题,开发者可以通过
GeometryReader测量内容高度并结合.id()刷新的技巧来实现动态高度调整。
iOS 性能优化:Apple 工程师问答精选 (Optimize Your App's Speed and Efficiency: Q&A)
Anton Gubarenko 整理了 Apple “Optimize your app’s speed and efficiency” 活动中的 Q&A 内容,涵盖 SwiftUI 性能(闭包捕获优化、Observable 使用、@Binding vs let)、Liquid Glass 容器(GlassEffectContainer 的最佳实践)、Foundation Models 框架(多语言支持、并发使用、延迟优化)以及 Instruments 工具(Hitch vs Hang、新增 Power Profiler)等关键领域。
本次 Q&A 展现了 Apple 工程团队在性能调优层面的实践取向:通过细节分析与工具驱动,让优化从“黑盒经验”转变为“可度量、可验证的工程流程”。
Swift 中禁用单个弃用警告的技巧 (Workaround: how to silence individual deprecation warnings in Swift)
开发中难免必须使用一些被软废弃(deprecated)的 API。在开启“警告即错误”(-warnings-as-errors)后,将面临无法编译的窘境。不同于 Objective-C 可以使用 #pragma clang diagnostic 针对特定代码段禁用警告,Swift 至今没有等效机制。
Jesse Squires 分享了一个巧妙的解决方案:定义一个协议包装弃用 API,在扩展中实现时标记为 @available(*, deprecated),然后通过类型转换为协议类型来调用。编译器会通过协议见证表查找方法,从而“隐藏”弃用警告。虽然方案略显冗长,但对于必须使用遗留 API 的纯 Swift 项目很实用。
深入 Swift 底层:从二进制优化到逆向工程
以下几篇文章都偏硬核,适合关注工具链、运行时与二进制层面细节的读者。
-
static const in header cost: dyld ships with redundant 2KB data copy by Kyle Ye
通过 dyld 中
static const头文件定义被多处编译单元重复包含的案例,揭示了二进制体积膨胀的成因与代价,并提出修复方案。 -
Reverse engineer a swift binary by Erk Ekin
通过一个简单的登录程序,演示如何反汇编 Swift 可执行文件、定位字符串与条件分支,并通过修改指令和重新签名实现“补丁式”运行。
-
How do researchers reverse-engineer private frameworks? By Jacob Bartlett
介绍如何借助 MachOSwiftSection 从 Simulator runtime 抽取 Mach-O 并还原私有框架接口,解析 Swift 元数据结构,甚至通过版本 diff 观察系统能力的演进。
工具
Swift Stream IDE - 跨平台 Swift 开发扩展
Swift Stream IDE 是由 Mikhail Isaev 开发的功能强大的 VS Code 扩展,旨在让开发者能够流畅地构建面向非 Apple 平台的 Swift 项目。它基于 VS Code 的 Dev Containers 扩展,将编译器、依赖与工具链完整封装在 Docker 容器中,实现了真正一致、可移植的跨平台 Swift 开发体验。目前,Swift Stream 已支持多种项目类型,包括 Web(WebAssembly)、服务器(Vapor/Hummingbird)、Android(库开发)、嵌入式(ESP32-C6、树莓派等)等多种项目类型。
在 The Swift Android Setup I Always Wanted 一文中,Mikhail 还演示了如何结合 Swift Stream IDE、swift-android-sdk 与 JNIKit,高效构建 Android 原生库。
容器化开发的优势包括:保持宿主机环境整洁、确保旧项目的可编译性、支持跨平台一致的开发体验,以及可通过 SSH 远程连接到更强大的机器进行开发。
AnyLanguageModel - 统一 Swift 大模型开发范式
或许许多开发者不会在第一时间使用 WWDC 2025 上推出的 Foundation Models 框架,但大多数人都对它的 API 印象深刻——这一框架显著简化了与大模型的交互流程,并充分发挥了 Swift 语言特性。
Mattt 开发的 AnyLanguageModel 实现了与 Foundation Models 框架完全兼容的 API:只需将 import FoundationModels 换成 import AnyLanguageModel,就能在不改动上层业务代码的前提下,接入多家云端及本地模型后端(OpenAI、Anthropic、Ollama、llama.cpp、Core ML、MLX 以及 Foundation Models)。此外,该库还利用 Swift 6.1 的 Traits 特性,可按需引入重依赖,从而显著降低二进制体积并提升构建速度。
Metal Lab - 基于 Apple Metal API 生态的中文图形学社区
在中文互联网上,关于 Metal 的教程资源非常稀少,尽管 Apple 官方提供了详尽的文档和示例代码,但这些文档对于国内开发者而言往往语言晦涩、结构复杂,缺乏通俗易懂的入门指导,初学者常常感到难以上手。Metal Lab 提供的教程文档旨在填补这一空白,为中文开发者提供一份系统性、易懂、循序渐进的 Metal 入门资料,帮助他们从零开始掌握 Metal 编程的精髓。无论是游戏开发、图形渲染,还是计算机视觉应用,这都是一份值得收藏的中文资源。
往期内容
- Swift 官方发布 Android SDK - #108
- 去 Apple Store 修手机 - #107
- 高通收购 Arduino:历史的轮回 - #106
- Sora 2:好模型,但未必是好生意 - #105
THANK YOU
如果你觉得这份周报或者我的文章对你有所帮助,欢迎 点赞 并将其 转发 给更多的朋友。
📮 想持续关注 Swift 技术前沿?
每周一期《肘子的 Swift 周报》,为你精选本周最值得关注的 Swift、SwiftUI 技术文章、开源项目和社区动态。
- 📬 在 weekly.fatbobman.com 免费订阅
- 💬 加入 Discord 与中文 Swift 开发者深入交流
- 📚 访问 fatbobman.com 查看数百篇深度原创教程
一起构建更好的 Swift 应用!🚀