الأجناس العامة (Generics) في Swift: لماذا تحتاجها وكيفية استخدامها للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

ما هي الأجناس العامة (Generics) في Swift ولماذا تحتاجها

\n\n

تخيل أنك تكتب دالة تقوم بتبديل رقمين. ثم تحتاج إلى نفس الدالة للنصوص. ثم للتواريخ. إذا لم تكن هناك أجناس عامة (Generics)، فسيتعين عليك كتابة ثلاث دوال متطابقة تختلف فقط في أنواع البيانات. هذا ينتهك مبدأ DRY (لا تكرر نفسك) ويجعل الكود ضخمًا.

\n\n

الأجناس العامة (Generics) (دجينيريكس أو الأنواع العامة) هي آلية قوية في Swift تسمح لك بكتابة كود مرن وقابل لإعادة الاستخدام يعمل مع أي نوع بيانات. بدلاً من تحديد نوع معين، تستخدم عنصرًا نائبًا (placeholder) يتم استبداله بالنوع الفعلي عند استدعاء الدالة أو إنشاء الهيكل.

\n\n

في هذه المقالة، سنشرح كيفية عمل الأجناس العامة في Swift، ونعرض أمثلة عملية لكيفية استخدامها في تطوير iOS.

\n\n

1. أساسيات الأجناس العامة: الدوال والأنواع

\n\n

لنبدأ بمثال بسيط. لنكتب دالة تقوم بتبديل قيمتين:

\n\n
// بدون Generics — يجب كتابة دالة منفصلة لكل نوع\nfunc swapInts(_ a: inout Int, _ b: inout Int) {\n    let temp = a\n    a = b\n    b = temp\n}\n\nfunc swapStrings(_ a: inout String, _ b: inout String) {\n    let temp = a\n    a = b\n    b = temp\n}\n\n// مع Generics — دالة واحدة لجميع الأنواع\nfunc swapValues<T>(_ a: inout T, _ b: inout T) {\n    let temp = a\n    a = b\n    b = temp\n}\n\nvar x = 10, y = 20\nswapValues(&x, &y) // تعمل مع Int\nprint("x: \\(x), y: \\(y)") // x: 20, y: 10\n\nvar str1 = "Hello", str2 = "World"\nswapValues(&str1, &str2) // تعمل مع String\nprint("str1: \\(str1), str2: \\(str2)") // str1: World, str2: Hello
\n\n

هنا <T> هو معامل نوع عام. الحرف T هو مجرد اتفاقية (من كلمة Type)، يمكنك استخدام أي اسم: <Element>، <ValueType>، إلخ.

\n\n

تعمل الأجناس العامة ليس فقط مع الدوال، بل مع الأنواع أيضًا. لننظر إلى هيكل حاوية بسيط:

\n\n
// هيكل عام لتخزين قيمة واحدة\nstruct Box<T> {\n    let value: T\n    \n    init(_ value: T) {\n        self.value = value\n    }\n}\n\nlet intBox = Box(42)        // النوع Box<Int>\nlet stringBox = Box("Swift") // النوع Box<String>\nlet arrayBox = Box([1, 2, 3]) // النوع Box<[Int]>\n\nprint(intBox.value)  // 42\nprint(stringBox.value) // Swift
\n\n

يقوم مترجم Swift تلقائيًا بتحديد النوع T بناءً على القيمة المُمررة. وهذا يُسمى استنتاج النوع (type inference).

\n\n

2. قيود الأنواع (Type Constraints)

\n\n

أحيانًا تحتاج إلى تقييد الأنواع التي يمكن استخدامها مع النوع العام الخاص بك. على سبيل المثال، تريد كتابة دالة تجد القيمة القصوى في مصفوفة. لكن العامل > لا يعمل مع جميع الأنواع — فقط مع تلك التي تتوافق (conform) مع البروتوكول Comparable.

\n\n
// قيد: يجب أن يتوافق T مع البروتوكول Comparable\nfunc findMax<T: Comparable>(in array: [T]) -> T? {\n    guard !array.isEmpty else { return nil }\n    \n    var maxValue = array[0]\n    for element in array {\n        if element > maxValue {\n            maxValue = element\n        }\n    }\n    return maxValue\n}\n\nlet numbers = [3, 7, 1, 9, 5]\nprint(findMax(in: numbers) ?? "No max") // 9\n\nlet words = ["apple", "zebra", "banana"]\nprint(findMax(in: words) ?? "No max") // "zebra" (حسب الترتيب الأبجدي)
\n\n

الصيغة <T: Comparable> تعني: "النوع T يجب أن يتوافق مع البروتوكول Comparable". يمكن إضافة عدة قيود بفاصلة أو استخدام where لشروط أكثر تعقيدًا:

\n\n
// عدة قيود باستخدام where\nfunc process<T>(_ value: T) where T: Numeric, T: Comparable {\n    if value > 0 {\n        print("موجب                

المدونات

توصيات الكتب