النوع المعتم (Opaque Type) في Swift، المعروف أيضًا باسم نوع الإرجاع المعتم (Opaque Return Type)، هو مفهوم تم تقديمه في Swift 5.1. يساعد النوع المعتم على تبسيط استخدام البروتوكولات (Protocols) والأنواع العامة (Generics)، خاصةً مع البروتوكولات التي تحتوي على أنواع مرتبطة (Associated Types). ستشرح هذه المقالة ماهية النوع المعتم وكيفية استخدامه.
مشكلة استخدام البروتوكولات والأنواع العامة
عندما تُرجع دالة بروتوكولًا يحتوي على نوع مرتبط، يكون من الصعب تحديد نوع البيانات المحدد لقيمة الإرجاع. على سبيل المثال:
protocol P {
associatedtype Value
var value: Value { get }
init(value: Value)
}
struct S1 : P {
var value: Int
}
struct S2: P {
var value: String
}
// خطأ: لا يمكن استنتاج نوع الإرجاع
// func foo() -> P { return S1() }
يحتوي استخدام الأنواع العامة أيضًا على قيود لأنه يتطلب تحديد نوع البيانات للمعامل أو قيمة إرجاع الدالة:
func foo<T: P>(value: T.Value) -> T { return T(value: value) }
let s1: S1 = foo(value: 10)
let s2: S2 = foo(value: "ahihi")
الحل مع النوع المعتم
النوع المعتم، الذي يتم الإعلان عنه باستخدام الكلمة الأساسية some
، يسمح للدالة بإرجاع نوع بيانات “مخفي” يلتزم فقط بالبروتوكول، دون الحاجة إلى تحديده بشكل صريح.
func foo() -> some P { return S1(value: 11) }
في المثال أعلاه، يمثل some P
نوع بيانات محددًا يفي بالبروتوكول P
، وسيقوم المترجم باستنتاجه تلقائيًا.
مع ذلك، يسمح النوع المعتم بإرجاع نوع بيانات واحد فقط في نفس الدالة:
// خطأ: لا يمكن إرجاع أنواع بيانات متعددة
// func bar(_ x: Int) -> some P {
// if x > 10 { return S1(value: 12) }
// else { return S2(value: "ahihi") }
// }
النوع المعتم والبروتوكولات مع الأنواع المرتبطة (PATs)
النوع المعتم مفيد بشكل خاص عند العمل مع PATs. فهو يسمح باستخدام PATs كنوع إرجاع دون معرفة نوع البيانات المحدد، مما يجعل الكود أكثر مرونة وغير مقيد بإصدار المكتبة.
func giveMeACollection() -> some Collection { return [1, 2, 3] }
let collection = giveMeACollection()
print(collection.count) // 3
النوع المعتم والنوع المحدد
عندما تُرجع دالة ذات نوع معتم نوعًا محددًا، يضمن المترجم أن استدعاءات هذه الدالة تُرجع دائمًا نفس النوع.
func fooo() -> some Equatable { return 5 }
let f1 = fooo()
let f2 = fooo()
print(f1 == f2) // true
لكن هذا لا ينطبق على PATs. ستُرجع كل دالة ذات نوع معتم و PATs نوع بيانات منفصلًا، حتى لو كانت تتوافق مع نفس البروتوكول.
النوع المعتم ومساحبات الأنواع العامة
يسمح دمج النوع المعتم مع مساحبات الأنواع العامة بإنشاء دوال متعددة الاستخدامات مع أنواع بيانات متعددة، دون الحاجة إلى إرجاع نوع محدد.
لماذا استخدام النوع المعتم؟
يوفر النوع المعتم مرونة عند العمل مع البروتوكولات، وخاصة PATs. فهو يساعد على تجنب الاضطرار إلى تحديد نوع بيانات معين، ويقلل من القيود المفروضة على إصدار المكتبة، ويزيد من إمكانية إعادة استخدام الكود.
النوع المعتم في SwiftUI
يستخدم النوع المعتم على نطاق واسع في SwiftUI. خاصية body
في بروتوكول View لها نوع some View
، مما يسمح بإرجاع أي نوع بيانات يتوافق مع بروتوكول View، مما يُبسط عملية بناء واجهة المستخدم. يساعد استخدام some View
على تجنب نوع البيانات المعقد عندما تحتوي View على مستويات متعددة متداخلة.