سوئیفت 6.4: موارد جدید در همزمانی
اپل Swift 6.4 را در WWDC 2026 منتشر کرد و مجموعه ای از بهبودهای قابلیت استفاده را در زبان مشترک سوئیفت و API همزمان ارائه کرد. اگرچه سوئیفت منبع باز است، اما بسیاری از تغییرات ممکن است همچنان شما را شگفت زده کند. در همان زمان، جلسات WWDC همه تغییرات جدید را پوشش ندادند (اگر هنوز فرصت تماشای بسیاری از آنها را داشته اید).
من پیشنهادات Swift Evolution را هر هفته برای خبرنامهام دنبال میکنم تا توسعهدهندگان 30K+ iOS را در مورد آخرین تغییرات سوئیفت مطلع کنم، بنابراین میخواستم عمیقتر به تغییرات همزمانی بپردازم. در اینجا یک بررسی برای شما وجود دارد!

مقدمه ای سریع برای اینکه چگونه تمام تغییرات را پیگیری می کنم: Swift Evolution: خواندن و یادگیری از پیشنهادات. من از این تکنیک برای صرفه جویی در ردیابی شما با نشان دادن یک نمای کلی در خبرنامه هفتگی رایگان خود (30 هزار مشترک) استفاده می کنم. به هفته نامه SwiftLee بپیوندید.
من با خواندن پیشنهادات Swift Evolution هر هفته با تغییرات سوئیفت همراه هستم. این بهترین راه برای درک اینکه نه تنها چه چیزی تغییر کرده است، بلکه همچنین چرا یک ویژگی معرفی شده است و چه مبادلاتی مورد بحث قرار گرفته است.
من رویکردم را در Swift Evolution: Reading and Learning from Proposals توضیح دادم. من از همین عادت برای خلاصه کردن مهمترین بهروزرسانیهای Swift در SwiftLee Weekly استفاده میکنم، خبرنامه هفتگی رایگان من که توسط توسعهدهندگان 30K+ Swift خوانده میشود.
اکنون که داستان پشت این نمای کلی را می دانید، زمان آن رسیده است که به اولین تغییر همزمانی سوئیفت 6.4 بپردازید.
بیانیه های تعویق ناهمزمان
سوئیفت 6.4 SE-0493: پشتیبانی را پیاده سازی می کند async تماس می گیرد defer بدن ها این یکی از آن تغییراتی است که به محض اینکه به آن نیاز داشته باشید، احساس می کنید.
قبل از Swift 6.4، نمیتوانستید یک تابع async را در یک فراخوانی کنید defer بدن حتی اگر عملکرد اطراف قبلا وجود داشته باشد async. این برای کار تمیز کردن آزار دهنده بود defer دقیقاً همان جایی است که می خواهید اطمینان حاصل کنید که هر خروجی تمیز شده است.
در سوئیفت 6.4، کد زیر معتبر می شود:
func importArticles() async throws {
let importer = ArticleImporter()
await importer.open()
defer {
await importer.close() // We can now use await in here.
}
try await importer.importLatestArticles()
}
این defer بدن هنگام خروج از محدوده هنوز هم مانند قبل کار می کند. تفاوت این است که Swift اکنون به طور ضمنی منتظر پاکسازی ناهمزمان قبل از بازگشت تابع است.
من این تغییر را دوست دارم زیرا وسوسه نوشتن دستی پاکسازی در هر مسیر خروجی ممکن را از بین می برد. اگر تابعی بتواند پس از تنظیم پرتاب کند، defer راه اندازی و تخریب را نزدیک به هم نگه می دارد.
یک جزئیات که باید در نظر داشت: ناهمزمان defer لغو را پنهان نمی کند. اگر کار فعلی قبلاً لغو شده است، در داخل کد کد کنید defer بدن هنوز هم می تواند لغو را مشاهده کند. این ما را به تغییر بعدی در سوئیفت 6.4 می رساند.
دوره 5 روزه رایگان ایمیل: The Swift Concurrency Playbook
الف دوره 5 روزه رایگان ایمیل افشای 5 اشتباه بزرگ توسعهدهندگان iOS با async/wait کار میکنند، که منجر به رد کردن App Store و پروژههای مهاجرت به جای چند روز، ماهها طول میکشد (حتی اگر سالها در حال نوشتن Swift باشید).
سپرهایی برای لغو وظایف
Swift 6.4 SE-0504: Task override shields را اضافه می کند. سپر لغو کار به طور موقت مانع از مشاهده کد لغو در حین اجرای بسته می شود.
این کمی انتزاعی به نظر می رسد، بنابراین ابتدا به فرم نگاه می کنیم:
func closeDatabaseConnection() async {
await withTaskCancellationShield {
print(Task.isCancelled) // Always false inside the shield, even if outer task is cancelled.
await database.close()
}
}
اگر کار دور زدن قبلاً قبل از ورود به سپر لغو شده بود، آن لغو همچنان وجود دارد. سپر فقط باعث می شود که کد داخل در بسته مشاهده شود Task.isCancelled == falseکه اجازه می دهد تا کارهای نظافتی مانند database.close() برای اجرا پس از بازگشت بسته شدن، Task.isCancelled وضعیت لغو اولیه را دوباره گزارش می کند.
این به ویژه در رابطه با async مفید است defer:
func writeArticles(_ articles: [Article]) async throws {
let transaction = await database.beginTransaction()
defer {
await withTaskCancellationShield {
// We want this to always run, even if cancelled.
await transaction.rollbackIfNeeded()
}
}
try await transaction.insert(articles)
try await transaction.commit()
}
من هیچ جا از cancel shields استفاده نمی کنم. صادقانه بگویم، من تا به حال این ویژگی را در پروژه های خودم از دست نداده ام (اگرچه ممکن است داستان مرغ و تخم مرغ باشد). با این حال، برای یک کار پاکسازی کوتاه یا برگشتی که باید کامل شود، حتی اگر کار لغو شده باشد، ارزشمند است.
به عبارت دیگر: از سپرهای خنثی سازی برای پایان دادن به کاری که قبلاً شروع کرده اید استفاده کنید، نه برای پنهان کردن خنثی سازی از عملیات طولانی مدت.
هشدار در مورد وظایف پرتاب نادیده گرفته شده
Swift 6.4 مدیریت خطا را برای کارهای بدون ساختار از طریق SE-0520 بهبود می بخشد: استفاده از نتیجه رد در مقداردهی اولیه کار. این تغییر به شما کمک میکند تا یک اشکال ظریف را پیدا کنید: ایجاد یک کار پرتاب و سپس نادیده گرفتن کنترلکننده وظیفه بازگشتی آن.
قبل از این تغییر، کد زیر میتواند بیصدا خطاها را نادیده بگیرد:
Task {
try await importArticles()
}
این مشکل ساز است زیرا می توانید به طور غیرمنتظره ای اشکالات را در کد خود پنهان کنید. بنابراین، کار می تواند پرتاب شود، اما هیچ کس منتظر نیست task.valueو بدنه وظیفه خطا را مدیریت نمی کند. در سوئیفت 6.4، Xcode به شما هشدار می دهد:
// warning: Unstructured throwing task was not used,
// which may accidentally ignore errors thrown inside the task
Task {
try await importArticles()
}
میتوانید با مدیریت خطاها در کار، هشدار را برطرف کنید:
Task {
do {
try await importArticles()
} catch {
print("Importing articles failed with error: \(error)")
}
}
یا می توانید دسته کار را ذخیره کنید و بعداً منتظر مقدار باشید:
let importTask = Task {
try await importArticles()
}
do {
try await importTask.value
} catch {
print("Importing articles failed with error: \(error)")
}
این یک پیشرفت کوچک تشخیصی است، اما می تواند از خطاهای واقعی جلوگیری کند. من بسیاری از پایگاه های کد استفاده شده را دیده ام Task { ... } به عنوان یک دریچه فرار سریع از کد همزمان. اخطار جدید از شما می خواهد تصمیم بگیرید که در صورت شکست این کار چه اتفاقی بیفتد.
اگر واقعاً می خواهید کنترل کننده وظیفه را نادیده بگیرید، همچنان می توانید بنویسید:
_ = Task {
try await importArticles()
}
من فقط زمانی این کار را انجام می دهم که نادیده گرفتن شکست واقعاً عمدی باشد. در بیشتر موارد، رسیدگی به خطای کار ساده تر است.
مقداردهی اولیه وظایف قابل پرتاب را معرفی کرد
همین پیشنهاد مبتنی بر پشتیبانی از پرتاب های تایپ شده در مقداردهی اولیه وظایف است. این اجازه می دهد تا a Task برای حمل نوع خاصی از آسیب:
let task: Task = Task {
throw URLError(.badURL)
}
می توانید تفاوت را در فرم به روز شده اولیه ساز مشاهده کنید:
public init(
priority: TaskPriority? = nil,
operation: sending @escaping @isolated(any) () async throws(Failure) -> Success
)
بخش مهم این است throws(Failure). با همان الگوی پرتاب تایپ شده ای مطابقت دارد که می توانید در توابع عادی همگام استفاده کنید.
در عمل، زمانی که نوع خطا را میدانید، این کار میتواند مدیریت خطا را سادهتر کند. من هر کار را فقط برای اشاره به یک اشتباه تایپی بازنویسی نمیکنم، اما داشتن این گزینه هنگام ایجاد APIهایی که کنترلکنندههای وظیفه را نشان میدهند، خوب است.
پشتیبانی از نتایج ناهمزمان
Swift 6.4 همچنین SE-0530: Async Result Support را پیاده سازی می کند. این Result نوع قبلاً یک داشت catching اولیه برای پرتاب همزمان، اما هیچ معادل داخلی برای پرتاب ناهمزمان وجود نداشت.
اکنون می توانید بنویسید:
let result = await Result {
try await importArticles()
}
بدون این اولیه ساز، شما معمولاً تبدیل را خودتان می نویسید:
let result: Result
do {
result = .success(try await importArticles())
} catch {
result = .failure(error)
}
آغازگر جدید تغییر بزرگی در زبان نیست، اما الگو را در مکانهایی حذف میکند که شما عمداً میخواهید نتیجه را به جای پرتاب فوری ذخیره کنید.
رسیدن به Result اکنون که async/wait وجود دارد کمتر رایج است، اما زمانی که باید موفقیت یا شکست را به عنوان یک ارزش در نظر بگیرید، همچنان مفید است. به عنوان مثال، هنگامی که نتایج را در یک مدل view ذخیره می کنید، خطاها را ثبت می کنید یا چندین نتیجه را جمع آوری می کنید.
مراجع ضعیف
در حالی که ما در این مقاله بر روی Swift 6.4 تمرکز کردهایم، من همچنین میخواهم کمی در مورد Swift 6.3 که SE-0481 را معرفی کرد، توضیح دهم: weak let. این تغییر به همزمانی محدود نمی شود، بلکه کمک می کند Sendable چک کردن
قبل از سوئیفت 6.3، مراجع ضعیف باید با استفاده از varحتی اگر خودتان هرگز ارزش جدیدی تعیین نکنید:
protocol ArticlePreviewDelegate: AnyObject, Sendable {
func didSelectArticle()
}
final class ArticlePreviewController: Sendable {
weak var delegate: ArticlePreviewDelegate?
init(delegate: ArticlePreviewDelegate?) {
self.delegate = delegate
}
}
این مشکل ایجاد می کند Sendable کلاس ها به این دلیل که ویژگی ذخیره شده متغیر مجاز نیست:
// Stored property 'delegate' of 'Sendable'-conforming class
// 'ArticlePreviewController' is mutable
با Swift 6.3 می توانید بنویسید:
final class ArticlePreviewController: Sendable {
weak let delegate: ArticlePreviewDelegate?
init(delegate: ArticlePreviewDelegate?) {
self.delegate = delegate
}
}
هنوز می توان مرجع ضعیف را انجام داد nil وقتی نماینده ناپدید می شود این بدان معنا نیست که شما خودتان نماینده دیگری را تعیین کنید. فقط به این معنی است که مرجع ضعیف پس از رها شدن جسم دیگر قادر به مشاهده آن نیست.
این تمایز پیامدهایی برای Sendable چک کردن پس از مقداردهی اولیه، دیگر نمی توانید نماینده را جایگزین کنید، بنابراین ویژگی ذخیره شده از نظر کد شما مانند یک مرجع تغییرناپذیر رفتار می کند.
انواع صریح غیر قابل ارسال
سوئیفت 6.4 SE-0518 را پیاده سازی می کند: ~Sendable علامت گذاری صریح غیرSendable گونه ها به شما امکان می دهد گزارش دهید که یک نوع حسابرسی شده است و نباید مطابقت داشته باشد Sendable.
به عنوان مثال:
enum ExecutionResult: ~Sendable {
case success
case failure(NonSendable)
}
tilde به سویفت میگوید که خودکار را سرکوب کند Sendable استنتاج برای این نوع همچنین قصد را به توسعه دهندگان دیگر منتقل می کند: این لیست ممیزی شده است و عمداً قابل ارسال نیست زیرا یکی از نمونه های آن حاوی یک مقدار مرتبط است که قابل ارسال نیست.
این به ویژه برای API های عمومی مفید است. اگر یک نوع عمومی با آن مطابقت ندارد Sendableکاربران ممکن است تعجب کنند که آیا فراموش کرده اید مطابقت را اضافه کنید. با ~Sendableشما می توانید به صراحت مشخص کنید که این نوع عمداً قابل حمل نیست.
چند قانون وجود دارد که باید به خاطر بسپارید:
- تو بنویس
~Sendableدر خود اعلان نوع، نه در پسوند. - شما نمی توانید ترکیب کنید
Sendableو~Sendableاز همان نوع - ممکن است هنوز یک زیر کلاس با آن مطابقت داشته باشد
Sendableاگر از نظر نخ ایمن ساخته شود.
این نکته آخر مهم است. قبل از این پیشنهاد، برخی از کتابخانه ها در دسترس نبودند Sendable مطابقت برای بیان “این نوع ارسال نمی شود”. نکته منفی این است که زیر کلاسها نیز این تطابق غیرقابل دسترس را به ارث بردهاند. ~Sendable به نویسندگان کتابخانه راه تمیزتری برای مستندسازی قصد بدون مسدود کردن زیر کلاسهای ایمن میدهد.
بهروزرسانیهای هفتگی Swift Evolution را دریافت کنید
Swift Concurrency به تکامل خود ادامه میدهد و پیگیری هر پیشنهاد پذیرفتهشده، تغییر Xcode و جزئیات WWDC ممکن است دشوار باشد. من این بهروزرسانیها را هر سهشنبه در SwiftLee Weekly پوشش میدهم که توسط توسعهدهندگان 30K+ Swift و iOS خوانده میشود.
یک ایمیل 5 دقیقهای حاوی مهمترین تغییرات سوئیفت، تکههای کد کاربردی و مقالاتی که ارزش خواندن دارند دریافت خواهید کرد. اگر میخواهید بدون چک کردن Swift Evolution در هر هفته جلوتر بمانید، به SwiftLee Weekly بپیوندید.
نتیجه گیری
Swift 6.4 چندین پیشرفت عملی را برای Swift Concurrency به ارمغان می آورد. ناهمزمان defer پاکسازی را آسانتر میکند، خنثی کردن سپرها به سناریوهای بازگشتی نادر اما مهم کمک میکنند، و هشدار وظیفه جدید از نادیده گرفتن بیصدا خطاها جلوگیری میکند. علاوه بر آن، weak let و ~Sendable انجام دهید Sendable پذیرش گویاتر
اگر یک پایگاه کد را به سویفت 6 منتقل می کنید، این به روز رسانی ها ارزش دانستن دارند. همه ویژگیها نحوه نوشتن کد هر روز را تغییر نمیدهند، اما با هم اصطکاک را از اتخاذ همزمانی دقیق حذف میکنند.
اگر می خواهید دانش خود را در مورد Swift Concurrency حتی بیشتر کنید، دوره Swift Concurrency را بررسی کنید. در صورت داشتن هر گونه راهنمایی یا بازخورد اضافی، با من تماس بگیرید یا با من توییت کنید.
متشکرم



