
摘要 :当对象的生命走到尽头,是曝尸荒野还是落叶归根?Swift 6.2 引入的
isolated deinit就像是一道"归元令",让 Actor 隔离的类在销毁时也能体面地访问隔离状态。本文通过大熊猫侯佩与杨不悔的奇遇,为您揭秘 SE-0371 的奥义。
0️⃣ 🐼 序章:光明顶的内存泄漏
光明顶,Server 机房。
这里是中土明教的代码总坛,无数条线程如蜿蜒的巨龙般穿梭在服务器之间。
大熊猫侯佩正对着一块发烫的屏幕发呆。他摸了摸自己圆滚滚的肚皮,又习惯性地用爪子去探了探头顶------那里的黑毛依然茂密,绝对没有秃 。他松了一口气,但这口气还没叹完,屏幕上那红色的 Compiler Error 就像明教的圣火令一样刺眼。
"奇怪,明明是在销毁对象,为什么就像是还没断气就诈尸了一样?"侯佩嘟囔着,嘴里还叼着半截没吃完的量子竹笋。
"因为你不仅是个路痴,还是个法盲。"
一个清脆的声音从机架后方传来。一位绿衣少女缓步走出,她眉目如画,眼神中却透着一股倔强与决绝。她是杨逍与纪晓芙之女,杨不悔。

"不悔妹子!"侯佩眼睛一亮(主要是看到了不悔手里提着的食盒),"你怎么在这?听说你在维护'倚天屠龙'分布式系统?"
在本次大冒险中,您将学到如下内容:
- 0️⃣ 🐼 序章:光明顶的内存泄漏
- 1️⃣ 💔 销魂时刻的尴尬:为何 Deinit 总是"身不由己"?
- 2️⃣ 🛡️ 绝处逢生:Isolated Deinit 的"归元令"
- 3️⃣ 🔌 实战演练:脆弱的 Session 与非 Sendable 的状态
- 4️⃣ 🐼 熊猫的哲学思考与黑色幽默
- 5️⃣ 🛑 尾声:突如其来的警报
杨不悔冷哼一声,将食盒放在服务器机柜上:"我娘给我取名'不悔',便是要我行事无愧于心。可现在的 Swift 代码,对象死的时候(deinit)乱七八糟,连最后一点体面都没有,还怎么谈'不悔'?我正为此事烦恼。"
侯佩凑过去一看,原来是一个 Actor 隔离的类在 deinit 里试图访问属性时崩溃了。
1️⃣ 💔 销魂时刻的尴尬:为何 Deinit 总是"身不由己"?
在 Swift 的江湖里,对象的诞生(init)通常都有明确的归属,但对象的死亡(deinit)却往往充满了不确定性。
杨不悔指着屏幕上的代码说道:"你看,这是一个被 @MainActor 保护的类。按理说,它的属性都应该在主线程安全访问。但是,当它的引用计数归零时,也就是它该死的时候 ,系统并不保证 deinit 会在主线程执行。"
侯佩恍然大悟:"你是说,这就像一个人明明是中原人士,死的时候却可能莫名其妙被扔到了西域荒漠,连句遗言都传不回来?"

"话糙理不糙。"杨不悔叹了口气,"如果没有 Swift 6.2 的新特性,我们在 deinit 里根本无法安全地访问那些被 Actor 隔离的数据。这叫'死不瞑目'。"
这就引出了 SE-0371:Isolated synchronous deinit。
2️⃣ 🛡️ 绝处逢生:Isolated Deinit 的"归元令"

Swift 6.2 引入了一个关键能力:允许我们将 Actor 隔离类的析构函数(deinitializer)标记为 isolated。
这意味着什么?这意味着当对象销毁时,系统会像拥有"乾坤大挪移"一般,确保代码跳转到该 Actor 的执行器(Executor)上运行。
杨不悔敲击键盘,写下了一段范例:
swift
@MainActor
class DataController {
func cleanUp() {
// 这里的逻辑需要在大威天龙...哦不,是 MainActor 上执行
print("正在清理门户...")
}
// 注意这个 isolated 关键字,这就是杨不悔的"不悔"令牌
isolated deinit {
cleanUp()
}
}
侯佩瞪大了眼睛:"你是说,加上 isolated,这遗言就能准时传达了?"

"没错。"杨不悔解释道,"如果没有 isolated 关键字,析构器就不会隔离到 @MainActor。全局 Actor 的工作机制决定了这一点。但一旦加上它,你的代码在运行前就会自动切换到 Actor 的执行器。这才是真正的落叶归根,安全无痛。"
3️⃣ 🔌 实战演练:脆弱的 Session 与非 Sendable 的状态

"光说不练假把式。"侯佩虽然爱吃,但对技术还是很较真的(尤其是涉及到能不能早点下班吃竹笋的问题),"有没有更具体的场景?比如...我也能听懂的?"
杨不悔微微一笑,想起了当年母亲纪晓芙的教诲,那种对誓言的执着。
"假设我们有一个 User 类,它就像武当派的张翠山,虽然正直,但并不是线程安全的(非 Sendable )。我们还有一个管理会话的 Session 类。"
swift
// 一个普通的、非 Sendable 的用户类
// 就像是一个不懂武功的凡人,经不起多线程的撕扯
class User {
var isLoggedIn = false
}
@MainActor
class Session {
let user: User
init(user: User) {
self.user = user
// 登录时,我们在 MainActor 上把状态改为 true
user.isLoggedIn = true
}
// 重点来了:必须加上 isolated
isolated deinit {
// 销毁会话时,我们要把用户登出。
// 如果没有 isolated,编译器会认为你在非隔离环境下访问了
// 属于 MainActor 的 user 属性,直接给你报个错!
user.isLoggedIn = false
}
}
侯佩若有所思地点点头:"这下我明白了。Session 是在 @MainActor 上的,它持有的 user 状态也归它管。如果 deinit 随便在哪个后台线程跑,去修改 user.isLoggedIn 就会导致数据竞争,也就是走火入魔!"

"正是。"杨不悔目光坚定,"加上 isolated,就是告诉编译器:'即使我要死了,我也要在我该在的地方,干干净净地把事情做完。' 这便是我杨不悔的道。"
4️⃣ 🐼 熊猫的哲学思考与黑色幽默
侯佩看着屏幕上编译通过的绿色对勾,心中不禁生出一丝感慨。
"其实,写代码和做熊一样。"侯佩抓起一根竹笋,咔嚓咬了一口,"生(Init)的时候要风风光光,死(Deinit)的时候也要体体面面。以前我们为了在 deinit 里做点清理工作,还得用 Task 搞异步,结果对象都销毁了,任务还在那飘着,像孤魂野鬼。"
杨不悔白了他一眼:"你那是代码写得烂。现在的 isolated deinit 是同步的,它保证了逻辑的原子性和顺序性。"
"同步好啊!"侯佩拍着胸脯,"我就喜欢同步,像我吃饭,必须嘴巴动肚子就饱,要是嘴巴动了三天肚子才饱(异步),那我不饿瘦了?虽然我看起来很壮,但我这是虚胖,而且头绝对不秃,经不起折腾。"

杨不悔看着这只明明胖得像球、却还在担心秃顶的大熊猫,忍不住噗嗤一笑。这笑声如冰雪消融,让机房里冰冷的服务器都似乎有了温度。
5️⃣ 🛑 尾声:突如其来的警报
就在两人以为 Bug 已除,准备去吃一顿正宗的"明教火锅"时,机房深处突然传来了一阵急促的蜂鸣声!

🚨 WEE-WOO-WEE-WOO 🚨
"不好!"杨不悔脸色一变,"是'乾坤大挪移'心法模块的后台任务卡住了!"
侯佩吓得竹笋都掉了:"怎么回事?不是已经 Isolated 了吗?"
杨不悔飞快地敲击着键盘,屏幕上的日志疯狂滚动:"不是线程安全问题!是有个非常重要的清理任务,被系统判定为'低优先级',一直被其他杂鱼任务插队,导致卡死在队列里出不来!"

"那怎么办?"侯佩急得团团转,"有没有办法给它打一针兴奋剂?或者给它一块免死金牌?让它插队先走?"
杨不悔眼神凝重,手指悬在回车键上:"这就需要用到一种禁术了......能够手动提升任务优先级的禁术。"
侯佩咽了口唾沫:"你是说......"

杨不悔转过头,看着侯佩,嘴角露出一丝神秘的微笑:"准备好了吗?下一章,我们要逆天改命。"
(欲知后事如何,且看下回分解:Task Priority Escalation APIs ------ 当熊猫学会了插队)
