Generic (البرمجة العامة) — هي آلية Swift تسمح بكتابة الأنواع والدوال بنوع معلم
النقاط الرئيسية
Generic (نوع عام) — هو تركيب نحوي في Swift يسمح بكتابة كود مرن قابل لإعادة الاستخدام مع تحديد الأنواع. بدلاً من نوع محدد، يكتب مكان منتظر في أقواس زاوية — عادة
بدون العامة، سيكون عليك كتابة دوال منفصلة لكل نوع: swapInts، swapStrings، swapDates. تستبدل دالة عامة swap الثلاث متغيرات بإعلان واحد. يقلل هذا من التكرار، يحسن القراءة ويقلل احتمالية الأخطاء عند نسخ الكود.
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapValues(&x, &y)
يولد مترجم Swift كوداً متخصصاً لكل استخدام محدد للعام. هذا يعني أن الدالة العامة swapValues
الدوال العامة هي دوال تقبل واحداً أو أكثر من معلمات الأنواع. تحدد معلمات الأنواع بعد اسم الدالة في أقواس زاوية:
يمكن أن تحتوي الدالة على معلمات عامة متعددة. على سبيل المثال، تأخذ الدالة findKey
func findKey<K: Hashable, V>(
for value: V,
in dictionary: [K: V]
) -> K? where V: Equatable {
for (key, dictValue) in dictionary {
if dictValue == value {
return key
}
}
return nil
}
تضمن القيود K: Hashable و where V: Equatable أنه يمكن استدعاء findKey فقط بقاموس مفاتيحه قابلة للتجزئة وقيمه قابلة للمقارنة. هذه القيود ليست بيروقراطية بل ضرورة: بدون Hashable لا يمكنك فهرسة البحث عن المفتاح، وبدون Equatable لا يمكنك مقارنة value مع dictValue.
where — هي كلمة مفتاحية في Swift تفرض قيوداً إضافياً على المعلمات العامة. خلافاً لتحديد بروتوكول بسيط في أقواس زاوية (
تكتب جملة where بعد توقيع الدالة أو النوع قبل القوس المفتوح. في where يمكنك تحديد أن T: Comparable & Hashable (متوافق مع بروتوكولين في نفس الوقت)، أو أن T.U == Int (تحديد نوع مرتبط).
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
}
extension Container where Item: Comparable {
func isSorted() -> Bool {
// Implementation available only if Item: Comparable
return true
}
}
الإمتداد بـ where يضيف طرقاً فقط للأنواع التي تستوفي الشرط. Container
Associated Types (الأنواع المرتبطة) — هي طريقة لجعل البروتوكول عاماً دون تحديد نوع محدد في مرحلة التصريح. بدلاً من
باستخدام where يمكنك إنشاء علاقات بين الأنواع المرتبطة لبروتوكولات مختلفة. على سبيل المثال، يمكنك المطالبة بأن يتطابق Item من بروتوكول مع Iterator.Element من آخر. هذا يضمن التوافق بين الأنواع على مستوى الترجمة.
protocol StackProtocol {
associatedtype Element
mutating func push(_ item: Element)
mutating func pop() -> Element?
}
struct IntStack: StackProtocol {
typealias Element = Int
private var items: [Int] = []
mutating func push(_ item: Int) { items.append(item) }
mutating func pop() -> Int? { items.popLast() }
}
IntStack يحدد Element كـ Int باستخدام typealias. يتحقق المترجم من أن جميع متطلبات البروتوكول تنجز بهذا النوع. بدون الأنواع المرتبطة، كان عليك كتابة StackProtocol
الإمتدادات العامة تسمح بإضافة طرق إلى نوع عام مع قيود إضافية. هذا هو نمط ـ“المطابقة الشرطية”، حيث يحصل النوع على وظائف فقط تحت ظروف معينة. يمكن أن تكون الفهارس عامة أيضاً.
يمكن أن يكون الفهرس عاماً: يقبل معلمات عامة في أقواس زاوية. هذا مفيد للوصول الآمن إلى المجموعات بفهارس عشوائية، حيث يمكن أن يكون الفهرس من أنواع مختلفة.
extension Array where Element: Numeric {
func sum() -> Element {
return reduce(0, +)
}
}
extension Array {
subscript<Indices: Sequence>(indices: Indices) -> [Element]
where Indices.Element == Int {
return indices.map { self[$0] }
}
}
هذا البناء يوفر تركيباً قوياً: طريقة sum تظهر فقط في المصفوفات الرقمية، والفهرس بفهارس عشوائية يعمل مع أي تسلسل من الفهارس الصحيحة. يدير المترجم رؤية هذه الأعضاء بناءً على شروط where.
الأخطاء عند استخدام العامة غالباً ما ترتبط بقيود غير صحيحة أو محاولة استخدام العامة حيث يكفي نوع محدد. دعنا نستعرض ثلاث سيناريوهات شائعة يواجهها المطورون.
إضافة معلمات عامة حيث يكفي نوع محدد هو نمط ضد. إذا كانت الدالة تعمل دائماً مع String، لا داعي لجعلها
استدعاء طريقة على معلم عام دون تحديد أن الطريقة موجودة هو خطأ.
// ❌ Error: value has no count method
func countElements<T>>(value: T) -> Int { value.count }
// ✅ Works: T: Collection guarantees count exists
func countElements<T: Collection>(value: T) -> Int { value.count }
الخطأ واضح: العام
الأسئلة الشائعة
Generic هو طريقة لكتابة كود يعمل مع أي نوع، دون تكرار نفس المنطق لـ Int، String وأنواع أخرى. بدلاً من نوع محدد، يستخدم مكان
Generic يحافظ على معلومات النوع المحدد عند الترجمة، مما يسمح للمترجم بتحقيق الأنواع وتوليد كود محسن. Any هو محو للأنواع: يمكن تمرير أي قيمة، ولكن يفقد النوع ويتطلب تحويلاً (as?).
استخدم التركيب T: SomeProtocol في أقواس زاوية أو جملة منفصلة where T: SomeProtocol. تضمن القيود أن المعلم العام يدعم طرقاً وخصائص محددة.
Associated Type (associatedtype) هو مكان لنوع داخل البروتوكول. البروتوكول لا يحدد نوعاً محدداً بل يصرح عن associatedtype، الذي يتم تحديده في التنفيذ: Array لديه Element، Dictionary لديه Key و Value.
Swift يترجم الكود العام إلى نسخ متخصصة لكل نوع محدد. في الممارسة، دالة عامة لـ Int ليست أبطأ من دالة مكتوبة يدوياً لـ Int. لا يوجد حمل زائد للتجريد.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.