Opaque Type (نوع ناپیدا) — مکانیزمی در Swift است که به تابع اجازه میدهد مقداری از یک نوع را برگرداند بدون اینکه نوع مشخص را به کد فراخواننده فاش کند. کلمه کلیدی some در نوع برگشتی — معروفترین مثال: some View در SwiftUI به معنای «یک نوع منطبق بر View برگردانده میشود، اما دقیقاً کدام — جزئیات پیادهسازی است» میباشد. Opaque type هویت نوع را حفظ میکند (برخلاف پروتکل به عنوان نوع)، که به کامپایلر اجازه میدهد کد را بهینهسازی کند و سازگاری نوع برگشتی را تضمین میکند. به نقل از Swift Book, 2025، opaque types مشکل پروتکلهای دارای associated types را حل میکنند و امکان بازگرداندن مقادیر این پروتکلها را از توابع فراهم میکنند.
نکات اصلی
Opaque Type — نوع برگشتی است که با کلمه کلیدی some اعلام شده و پیادهسازی مشخص را از کد فراخواننده پنهان میکند. طرف فراخواننده فقط میداند که مقدار برگشتی با یک پروتکل خاص مطابقت دارد، اما نمیداند دقیقاً چه نوعی پشت some قرار دارد. در عین حال کامپایلر نوع دقیق را میداند و از آن برای توزیع ایستا و بهینهسازی استفاده میکند.
قبل از ظهور opaque types در Swift 5.1 (SE-0244) بازگرداندن پروتکل دارای associated types از یک تابع بدون جعبه پیچی (boxing) غیرممکن بود. به عنوان مثال، پروتکل Equatable دارای associated type است و تابع نمیتوانست به سادگی Equatable را برگرداند — کامپایلر خطای «protocol can only be used as a generic constraint» میداد. Opaque type این مشکل را حل کرد.
func makeInt() -> some Equatable {
return 42
}
func makeString() -> some Equatable {
return "Hello"
}
// کامپایلر میداند که makeInt Int برمیگرداند
// makeInt() == makeString() — ❌ خطا، انواع متفاوت
هر دو تابع some Equatable برمیگردانند، اما انواع مشخص متفاوت هستند: Int و String. تلاش برای مقایسه آنها با == باعث خطای کامپایل میشود، زیرا opaque type تضمین میکند که از یک فراخوانی خاص همان نوع برگردانده میشود، اما نه بین توابع مختلف. این یک ویژگی است نه باگ: opaque type هویت نوع را جایی که پروتکل به عنوان نوع (any Equatable) آن را از دست میدهد حفظ میکند.
Generic و Opaque Type — دو روی یک سکه هستند. Generic به کد فراخواننده اجازه میدهد نوع را انتخاب کند، در حالی که opaque type به تابع اجازه میدهد نوع را از کد فراخواننده پنهان کند. تفاوت در جهت کنترل است.
| ویژگی | Generic <T> | Opaque some |
|---|---|---|
| چه کسی نوع را انتخاب میکند | کد فراخواننده | تابع/متد |
| هویت نوع | حفظ میشود (پایدار) | حفظ میشود (پایدار) |
| تعداد شاخههای return | یک (از طریق generic) | همان نوع در همه شاخهها |
| کاربرد | الگوریتمها، ساختارهای داده | SwiftUI، روشهای کارخانه |
در تابع generic، caller تصمیم میگیرد از چه نوعی استفاده کند. تابع باید با هر T که محدودیتها را برآورده میکند کار کند. برای opaque type، caller نوع مشخص را نمیداند — تصمیم را پیادهسازی میگیرد.
// Generic: caller نوع را انتخاب میکند
func identity<T>(_ value: T) -> T { value }
let x: Int = identity(42)
// Opaque: تابع نوع را پنهان میکند
func makeSomeEquatable() -> some Equatable { 42 }
let y = makeSomeEquatable()
انتخاب بین generic و opaque type به نیت بستگی دارد. اگر کد فراخواننده باید نوع را انتخاب کند — از generic استفاده کنید. اگر تابع باید جزئیات پیادهسازی را پنهان کند — از some استفاده کنید. SwiftUI some View را انتخاب کرد دقیقاً به این دلیل که body باید در داخل منعطف اما در خارج پایدار باشد.
some — کلمه کلیدی Swift است که در Swift 5.1 (SE-0244) معرفی شد. در موقعیت برگشتی برای اعلام opaque type و همچنین در پارامترها (SE-0341) و ویژگیها استفاده میشود. some تضمین میکند که نوع مشخص پایدار و برای کامپایلر شناخته شده، اما از کد خارجی پنهان است.
از Swift 5.7، some نه تنها در موقعیت برگشتی، بلکه در پارامترها نیز قابل استفاده است. some Equatable در پارامتر به معنای «این تابع هر نوع Equatable را میپذیرد، اما همه فراخوانیهای درون بدنه مشخص همان نوع را میبینند» است.
func areEqual(_ a: some Equatable, _ b: some Equatable) -> Bool {
// a و b — به طور پتانسیل انواع متفاوت، == مستقیماً کار نخواهد کرد
return isEqual(a, b)
}
func isEqual<T: Equatable>(_ a: T, _ b: T) -> Bool {
return a == b
}
استفاده از some در پارامترها نحو فشردهتری نسبت به <T> ارائه میدهد. به ویژه در پروتکلها و طراحی پروتکلمحور مفید است، جایی که هر استفاده از پروتکل به یک پارامتر generic جداگانه نیاز ندارد. کامپایلر در پشت صحنه some-پارامترها را به generic تبدیل میکند، بنابراین عملکرد یکسان است.
any — کلمه کلیدی Swift 5.6+ برای اعلام صریح انواع وجودی (پروتکل به عنوان نوع) است. برخلاف some، any هویت نوع را پاک میکند: کامپایلر نمیداند چه نوع مشخصی پشت پروتکل پنهان شده است. این انعطافپذیری میدهد (میتوان انواع مختلف را در یک آرایه ذخیره کرد)، اما به قیمت عملکرد.
some — چندریختی ایستا: کامپایلر نوع مشخص را میداند، از توزیع مستقیم استفاده میکند و میتواند کد را درونخطی کند. any — چندریختی پویا: از جدول متدهای مجازی (existential container) استفاده میشود که غیرمستقیمی اضافه میکند.
protocol Drawable {
func draw()
}
// some: نوع ایستا شناخته شده است
func makeDrawable() -> some Drawable {
return Circle() // نوع برگشتی واحد
}
// any: پویا، میتواند انواع مختلف را ذخیره کند
var shapes: [any Drawable] = [Circle(), Square()]
shapes.append(Triangle())
انتخاب بین some و any سازش بین عملکرد و انعطافپذیری است. Some سریعتر است اما به یک پیادهسازی محدود میکند. Any انعطافپذیرتر است (میتوان انواع را مخلوط کرد) اما به دلیل توزیع پویا کندتر است. در SwiftUI برای body همیشه از some View استفاده میشود، زیرا body هر View یک نوع مشخص است.
Opaque Type مشکل اساسی Swift را حل میکند: پروتکلهای دارای associated types (PAT) نمیتوانند مستقیماً به عنوان نوع استفاده شوند. یک تابع نمیتواند به سادگی Collection برگرداند — کامپایلر نیاز به مشخص کردن Element دارد. some Collection این را حل میکند و associated type را پنهان میکند.
بدون opaque type برای بازگرداندن Collection باید از نوع مشخص (Array<Int>) یا پاک کردن نوع (AnyCollection<Int>) استفاده میشد. some Collection حد وسط طلایی میدهد: کامپایلر پیادهسازی مشخص را میداند، کد فراخواننده — نه.
func makeReversedCollection<T>(
of array: [T]
) -> some Collection {
return array.reversed()
}
let result = makeReversedCollection(of: [1, 2, 3])
// result — ReversedCollection>, hidden from caller
for item in result {
print(item)
}
result قابل پیمایش است، اما نمیتوان مستقیماً به ویژگیهای ReversedCollection دسترسی داشت. این از کپسولهسازی محافظت میکند: اگر بعداً reversed() با متد دیگری با پیادهسازی متفاوت جایگزین شود، کد فراخواننده خراب نمیشود. Opaque type آزادی تغییر پیادهسازی را بدون تغییر API میدهد.
some View — معروفترین کاربرد opaque type است. هر View در SwiftUI body را به عنوان some View اعلام میکند. این به این معنی است که body یک نوع View مشخص را برمیگرداند، اما برنامهنویس نیازی به فکر کردن درباره اینکه دقیقاً چه چیزی است — TupleView، Group، ModifiedContent یا هر نوع دیگر از فریمورک — ندارد.
بدون opaque type، body باید یک نوع مشخص را برمیگرداند، مثلاً ModifiedContent<Button<Text>, Padding>، که غیرعملی است. some View این پیچیدگی را پنهان میکند. کامپایلر نوع دقیق body را به طور خودکار هنگام کامپایل استخراج میکند.
struct ContentView: View {
var body: some View {
VStack {
Text("سلام")
.font(.title)
Button("مرا لمس کن") {
print("لمس شد")
}
}
.padding()
}
}
کامپایلر body را به عنوان ModifiedContent<VStack<TupleView<(Text, Button<Text>)>>, Padding> استخراج میکند. برنامهنویس some View میبیند. اگر layout از VStack به HStack تغییر کند، کامپایلر به طور خودکار نوع را دوباره استخراج میکند — هیچ ویرایش دستی لازم نیست. این جادوی opaque type است: برنامهنویس روی منطق رابط تمرکز میکند، نه روی انواع ترکیب.
سوالات متداول
Opaque Type — نوعی است که با کلمه کلیدی some اعلام شده و پیادهسازی مشخص را از کد فراخواننده پنهان میکند. کامپایلر نوع دقیق را میداند، اما برنامهنویسی که از تابع استفاده میکند فقط پروتکل را میبیند.
some — opaque type با هویت ایستا: کامپایلر نوع مشخص را میداند. any — نوع وجودی با توزیع پویا: هویت نوع پاک شده است. Some کارآمدتر است، any انعطافپذیرتر است.
some View نوع مشخص پیچیده body را که کامپایلر به طور خودکار استخراج میکند پنهان میکند. این برنامهنویس را از نیاز به نوشتن نوع دقیق متشکل از Generic-لفافها (VStack, Group, ModifiedContent) آزاد میکند.
بله، از Swift 5.7. some در پارامترها شکر نحوی بر روی پارامتر generic است. اعلام توابع را به ویژه هنگام کار با پروتکلها ساده میکند، جایی که هر some-پارامتر به <T> جداگانه نیاز ندارد.
کامپایلر خطا میدهد: opaque type نیاز دارد که همه شاخههای return همان نوع مشخص را برگردانند. این عمداً برای حفظ هویت نوع انجام شده است. اگر نیاز به بازگرداندن انواع مختلف دارید، از any استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید