== و === في Swift: ما الفرق وما وراء الكواليس

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

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

ابدأ الدورة

لماذا يوجد علامتا مساواة في Swift وماذا تخفيان؟

\n\n

هل سبق لك أن كتبت كودًا بلغة Swift وفجأة صادفت العامل الغريب ===؟ أول فكرة: "خطأ مطبعي؟ أم أنه نوع من المساواة الفائقة؟" في الحقيقة، هذا ليس خطأ مطبعيًا، بل آلية قوية تغير منطق المقارنة جذريًا. إذا كنت تعمل مع الكلاسات أو الهياكل أو الإغلاقات، فإن فهم الفرق بين == و === ليس مجرد نظرية، بل ضرورة. بدون ذلك، تخاطر بظهور أخطاء يصعب تتبعها.

\n\n

في هذه المقالة، سننظر "تحت غطاء" Swift، ونشرح كيفية عمل كلا العاملين، ونتعلم اختيار الأداة الصحيحة لكل مهمة. هل أنت مستعد؟ لنبدأ.

\n\n

ما هو عامل المساواة ==؟

\n\n

العامل == هو "صديقك" القياسي لمقارنة القيم. يتحقق مما إذا كان كائنان متساويين من حيث محتواهما. ولكن هناك فارق: Swift لا يعرف ماذا يعني "متساوٍ" لنوع البيانات المخصص لديك. لذلك، لاستخدام ==، يجب أن يتوافق نوعك مع البروتوكول Equatable.

\n\n

هذا ما يبدو عليه الأمر مع الأنواع المضمنة:

\n\n
let a = 5\nlet b = 5\nprint(a == b) // true، لأن القيم متطابقة\n\nlet name1 = "Alice"\nlet name2 = "Alice"\nprint(name1 == name2) // true، السلاسل النصية متطابقة
\n\n

بالنسبة للكلاسات والهياكل، تحتاج إلى تنفيذ Equatable بنفسك. على سبيل المثال:

\n\n
struct User {\n    let id: Int\n    let name: String\n}\n\nextension User: Equatable {\n    static func == (lhs: User, rhs: User) -> Bool {\n        return lhs.id == rhs.id && lhs.name == rhs.name\n    }\n}\n\nlet user1 = User(id: 1, name: "John")\nlet user2 = User(id: 1, name: "John")\nprint(user1 == user2) // true، المحتوى متطابق
\n\n

لاحظ أن == يقارن القيم وليس المراجع. بالنسبة للهياكل، هذا واضح، أما مع الكلاسات فيبدأ الأمر المثير للاهتمام.

\n\n

عامل التطابق ===: عندما يكون كائنان هما نفس الشيء

\n\n

العامل === (ثلاث علامات مساواة) لا يتحقق من المحتوى، بل من تطابق المراجع. يجيب على السؤال: "هل يشير متغيران إلى نفس الكائن في الذاكرة؟"

\n\n

هذا العامل يعمل فقط مع الأنواع المرجعية: الكلاسات والإغلاقات (الإغلاقات هي أيضًا أنواع مرجعية). بالنسبة للهياكل والتعدادات، فهو غير موجود ببساطة - سيصدر المترجم خطأ.

\n\n

انظر إلى المثال:

\n\n
class Car {\n    var model: String\n    init(model: String) {\n        self.model = model\n    }\n}\n\nlet car1 = Car(model: "Tesla")\nlet car2 = Car(model: "Tesla")\nlet car3 = car1\n\nprint(car1 == car2) // false، إذا لم يتم تنفيذ Equatable\nprint(car1 === car2) // false، كائنان مختلفان\nprint(car1 === car3) // true، نفس المرجع
\n\n

هنا car1 و car2 هما كائنان مختلفان لهما نفس المحتوى. أما car3 فهو مجرد اسم آخر لنفس الكائن مثل car1. === يوضح ذلك بوضوح.

\n\n

كيف يعمل هذا تحت الغطاء: الذاكرة والمؤشرات

\n\n

لفهم الفرق، تحتاج إلى النظر في الذاكرة. عندما تنشئ مثيلًا لكلاس، يخصص Swift له مساحة في الكومة (heap) ويعيد مؤشرًا إلى هذه المساحة. المتغير يخزن المؤشر وليس الكائن نفسه.

\n\n
    \n
  • == يقارن المحتوى عبر المؤشر: يستدعي تنفيذك لـ Equatable، والذي يمكنه النظر إلى حقول الكائن.
  • \n
  • === يقارن المؤشرات نفسها: إذا كان مؤشران يشيران إلى نفس العنوان في الذاكرة، تكون النتيجة true.
  • \n
\n\n

بالنسبة للهياكل، الأمر أبسط: يتم تخزينها في المكدس (stack) وتُنسخ عند التعيين. لذلك، === لا معنى له بالنسبة لها - هيكلان مختلفان سيكونان دائمًا كائنين مختلفين في الذاكرة، حتى لو كان محتواهما متطابقًا.

المدونات

توصيات الكتب