Opaque Type (النوع غير الشفاف) هي آلية في Swift تسمح للدالة بإرجاع قيمة من نوع ما دون الكشف عن النوع المحدد للكود المستدعي. الكلمة المفتاحية some في نوع الإرجاع هي المثال الأكثر شهرة: some View في SwiftUI تعني «تُرجع الدالة نوعاً ما يمتثل لـ View، لكن تحديد أي نوع هو تفصيل تنفيذي». يحافظ opaque type على هوية النوع (على عكس البروتوكول كنوع)، مما يسمح للمترجم بتحسين الكود ويضمن اتساق النوع المُعاد. وفقاً لـ Swift Book, 2025، تحل الأنواع غير الشفافة مشكلة البروتوكولات ذات الأنواع المرتبطة، مما يسمح بإرجاع قيم هذه البروتوكولات من الدوال.
الخلاصة
Opaque Type هو نوع إرجاع يُعلن بالكلمة المفتاحية some ويخفي التنفيذ المحدد عن الكود المستدعي. الطرف المستدعي يعرف فقط أن القيمة المُعادة تمتثل لبروتوكول معين، لكنه لا يعرف بالضبط أي نوع يقف وراء some. في الوقت نفسه، يعرف المترجم النوع الدقيق ويستخدمه للتوزيع الثابت والتحسين.
قبل ظهور الأنواع غير الشفافة في Swift 5.1 (SE-0244)، كان من المستحيل إرجاع بروتوكول ذي أنواع مرتبطة من دالة دون غلاف تغليف. على سبيل المثال، بروتوكول Equatable له نوع مرتبط، ولم تستطع الدالة ببساطة إرجاع Equatable — كان المترجم يظهر خطأ «لا يمكن استخدام البروتوكول إلا كقيد عام». حل 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 وجهان لعملة واحدة. تسمح الأنواع العامة (generics) للكود المستدعي باختيار النوع، بينما يسمح opaque type للدالة بإخفاء النوع عن الكود المستدعي. الفرق في اتجاه التحكم.
| الخاصية | Generic | Opaque some |
|---|---|---|
| من يختار النوع | الكود المستدعي | الدالة/الطريقة |
| هوية النوع | محفوظة (مستقرة) | محفوظة (مستقرة) |
| عدد فروع الإرجاع | واحد (عبر العام) | نفس النوع في جميع الفروع |
| الاستخدام | الخوارزميات، هياكل البيانات | SwiftUI، طرق المصنع |
في دالة عامة، المستدعي يقرر أي نوع يستخدم. يجب أن تعمل الدالة مع أي T يحقق القيود. بالنسبة لـ opaque type، لا يعرف المستدعي النوع المحدد — التنفيذ هو من يتخذ القرار.
// Generic: المستدعي يختار النوع
func identity<T>(_ value: T) -> T { value }
let x: Int = identity(42)
// Opaque: الدالة تخفي النوع
func makeSomeEquatable() -> some Equatable { 42 }
let y = makeSomeEquatable()
الاختيار بين generic و opaque type يعتمد على القصد. إذا كان الكود المستدعي يجب أن يختار النوع، استخدم الأنواع العامة. إذا كانت الدالة يجب أن تخفي تفاصيل التنفيذ، استخدم 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 في المعاملات يوفر صيغة أكثر إيجازاً مقارنة بـ
any هي كلمة مفتاحية في Swift 5.6+ للإعلان صراحة عن الأنواع الوجودية (البروتوكول كنوع). على عكس some، يمحو any هوية النوع: لا يعرف المترجم أي نوع محدد يختبئ وراء البروتوكول. هذا يوفر مرونة (يمكنك تخزين أنواع مختلفة في مصفوفة واحدة)، لكن على حساب الأداء.
some — تعدد أشكال ثابت: يعرف المترجم النوع المحدد، يستخدم التوزيع المباشر ويمكنه تضمين الكود. any — تعدد أشكال ديناميكي: يُستخدم جدول الطرق الافتراضية (حاوية وجودية)، مما يضيف غير مباشرة.
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: البروتوكولات ذات الأنواع المرتبطة (PAT) لا يمكن استخدامها مباشرة كنوع. لا تستطيع الدالة ببساطة إرجاع Collection — يطلب المترجم تحديد Element. some Collection تحل هذا بإخفاء النوع المرتبط.
بدون opaque type، إرجاع Collection كان يتطلب استخدام نوع محدد (Array
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. إذا غيرت التخطيط من VStack إلى HStack، سيعيد المترجم استنتاج النوع تلقائياً — بدون أي تعديلات يدوية. هذا هو سحر opaque type: يركز المطور على منطق الواجهة، وليس على أنواع التركيب.
الأسئلة الشائعة
Opaque Type هو نوع يُعلن بالكلمة المفتاحية some يخفي التنفيذ المحدد عن الكود المستدعي. يعرف المترجم النوع الدقيق، لكن المطور الذي يستخدم الدالة يرى فقط البروتوكول.
some هو opaque type بهوية ثابتة: يعرف المترجم النوع المحدد. any هو نوع وجودي بتوزيع ديناميكي: هوية النوع ممحاة. Some أكثر أداءً، any أكثر مرونة.
some View يخفي النوع المحدد المعقد لـ body الذي يستنتجه المترجم تلقائياً. هذا يحرر المطور من كتابة النوع الدقيق المكون من أغلفة عامة (VStack, Group, ModifiedContent).
نعم, اعتباراً من Swift 5.7. Some في المعاملات هو سكر نحوي على معامل عام. يبسط إعلان الدوال، خاصة عند العمل مع البروتوكولات حيث كل معامل some لا يتطلب
سيظهر المترجم خطأ: يتطلب opaque type أن جميع فروع الإرجاع تُرجع نفس النوع المحدد. هذا مقصود للحفاظ على هوية النوع. إذا كنت بحاجة لإرجاع أنواع مختلفة، استخدم any.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.