مقدمة: الجدل الأبدي بين static و class في Swift
\nإذا كنت قد كتبت كودًا بلغة Swift من قبل، فبالتأكيد واجهت موقفًا: تحتاج إلى إنشاء دالة أو متغير ينتمي إلى النوع نفسه، وليس إلى نسخة منه. وهنا يظهر السؤال — هل تستخدم الكلمة المفتاحية static أم class؟ على Stack Overflow، حظي هذا السؤال بآلاف المشاهدات، وليس من دون سبب: الفرق دقيق ولكنه بالغ الأهمية لتصميم بنية التطبيق بشكل صحيح.
في هذا الدليل، سنوضح مرة واحدة وإلى الأبد الفرق بين static و class في Swift، ومتى تستخدم كلًا منهما، وكيف لا تقع في الفخ عند الوراثة. هيا بنا!
ما هي خصائص وطرق النوع؟
\nقبل أن نتعمق في التفاصيل، دعنا نتذكر الأساسيات. في Swift، لكل كلاس أو هيكل أو تعداد نوعان من الأعضاء:
\n- \n
- أعضاء النسخة (Instance) — تنتمي إلى كائن معين. لاستخدامها، تحتاج إلى إنشاء نسخة:
let obj = MyClass(). \n - أعضاء النوع (Type) — تنتمي إلى النوع نفسه. يتم استدعاؤها عبر اسم النوع:
MyClass.someMethod(). \n
لإنشاء خصائص وطرق النوع في Swift، تُستخدم الكلمات المفتاحية static و class. وهنا يبدأ الجزء المثير للاهتمام.
Static: صارم وغير قابل للتغيير
\nالكلمة المفتاحية static هي صديقك الموثوق عندما تريد إنشاء خاصية أو دالة لا يمكن تجاوزها في الفئة الفرعية. تعمل بنفس الطريقة مع الكلاسات والهياكل والتعدادات.
لنلقِ نظرة على مثال. تخيل أننا نكتب تطبيقًا لتسجيل الطلاب ونريد تخزين العدد الإجمالي للكائنات التي تم إنشاؤها:
\n\nclass Student {\n static var totalCount = 0\n \n let name: String\n \n init(name: String) {\n self.name = name\n Student.totalCount += 1\n }\n \n static func printTotal() {\n print("إجمالي الطلاب: \\(totalCount)")\n }\n}\n\nlet s1 = Student(name: "إيفان")\nlet s2 = Student(name: "ماريا")\nStudent.printTotal() // سيطبع: إجمالي الطلاب: 2\n\nلاحظ: نصل إلى totalCount و printTotal() عبر اسم الكلاس Student، وليس عبر نسخة. هذا مفيد للعدادات، الإعدادات الافتراضية، أو تخزين البيانات مؤقتًا.
Static في الهياكل والتعدادات
\nفي الهياكل والتعدادات، static هو الطريقة الوحيدة للإعلان عن أعضاء النوع. على سبيل المثال:
enum HTTPMethod {\n case get\n case post\n case put\n \n static let defaultMethod: HTTPMethod = .get\n}\n\nstruct AppConfig {\n static let apiURL = "https://api.example.com"\n static let timeout: Double = 30.0\n}\n\nهنا، يعمل static بشكل لا تشوبه شائبة لأن الهياكل والتعدادات لا تدعم الوراثة، وبالتالي لا حاجة للتجاوز.
Class: مرونة الوراثة
\nأما الكلمة المفتاحية class فهي أداة لمن يعملون مع الكلاسات (وفقط معها) ويريدون إعطاء الفئات الفرعية القدرة على تجاوز الدوال أو الخصائص. لا يمكن استخدام class في الهياكل أو التعدادات.
لنفترض أن لدينا كلاسًا أساسيًا Vehicle ونريد أن توفر كل فئة فرعية نسختها الخاصة من الوصف:
class Vehicle {\n class func description() -> String {\n return "هذه مركبة"\n }\n}\n\nclass Car: Vehicle {\n override class func description() -> String {\n return "هذه سيارة"\n }\n}\n\nclass Bicycle: Vehicle {\n override class func description() -> String {\n return "هذه دراجة هوائية"\n }\n}\n\nprint(Vehicle.description()) // هذه مركبة\nprint(Car.description()) // هذه سيارة\nprint(Bicycle.description()) // هذه دراجة هوائية