دليل مطور iOS: من Objective-C إلى Learning Swift
نشرت: 2022-03-11في عام 2008 ، أعلنت شركة Apple عن إصدار iPhone SDK 2.0. بدأ هذا الحدث ثورة أخرى في تطوير البرمجيات ، وولد جيل جديد من المطورين. يتم التعرف عليهم الآن كمطورين iOS.
لم يستخدم العديد من هؤلاء المطورين Objective-C من قبل ، وكان هذا هو التحدي الأول الذي أطلقته Apple عليهم. على الرغم من بناء الجملة غير المألوف وإدارة الذاكرة اليدوية ، فقد كان ناجحًا للغاية ، حيث ساعد في ملء متجر التطبيقات بعشرات الآلاف من التطبيقات. تعمل Apple باستمرار على تحسين Objective-C مع كل إصدار ، وإضافة كتل وحرفية ، وإدارة ذاكرة مبسطة مع حساب مرجعي تلقائي ، والعديد من الميزات الأخرى التي تدل على لغة برمجة حديثة.
وبعد 6 سنوات من التحسين والعمل على Objective-C ، قررت Apple طرح تحدٍ آخر للمطورين. مرة أخرى ، سيحتاج مطورو iOS إلى تعلم لغة برمجة جديدة: Swift . يزيل Swift إدارة المؤشر غير الآمنة ويقدم ميزات جديدة قوية ، مع الحفاظ على التفاعل مع كل من Objective-C و C.
يعد Swift 1.0 بالفعل منصة تطوير قوية ومستقرة ، والتي من المؤكد أنها ستتطور بطرق مثيرة للاهتمام خلال السنوات القادمة. إنها لحظة مثالية لبدء استكشاف هذه اللغة الجديدة حيث من الواضح أنها مستقبل تطوير iOS.
الغرض من هذا البرنامج التعليمي هو إعطاء مطوري Objective-C نظرة عامة سريعة على ميزات لغة Swift الجديدة ، مما يساعدك على اتخاذ الخطوة التالية والبدء في استخدام Swift في عملك اليومي. لن أقضي الكثير من الوقت في شرح Objective-C ، وسأفترض أنك على دراية بتطوير iOS.
تجربة Swift مقابل Objective-C
لبدء استكشاف Swift ، كل ما عليك فعله هو تنزيل XCode من App Store وإنشاء ملعب للتجربة. كل الأمثلة المذكورة في هذه المقالة تتم بهذه الطريقة.
الصفحة الرئيسية لـ Swift من Apple هي أفضل مرجع لتعلم برمجة Swift. سوف تجد أنه لا يقدر بثمن ، وإلى أن تصبح على دراية كاملة بالسرعة في تطوير Swift ، أعتقد أنك ستعود إلى هنا كثيرًا.
المتغيرات والثوابت
يتم الإعلان عن متغير في Swift باستخدام الكلمة الأساسية var
.
var x = 1 var s = "Hello"
ستلاحظ أن المتغيرين x
و s
من نوعين مختلفين. x
هو عدد صحيح ، بينما s
هي سلسلة. Swift هي لغة آمنة من النوع ، وسوف تستنتج أنواع المتغيرات من القيمة المخصصة. إذا كنت ترغب في جعل الكود الخاص بك أكثر قابلية للقراءة ، يمكنك اختياريًا إضافة تعليق توضيحي لنوع المتغير:
var y: Int y = 2
الثوابت متشابهة ، لكنك تعلنها باستخدام let
بدلاً من var
. لا يلزم معرفة قيمة الثابت في وقت الترجمة ، ولكن يجب عليك تعيين قيمة لها مرة واحدة بالضبط.
let c1 = 1 // Constant known at compile time var v = arc4random() let c2 = v // Constant known only at run time
كما يوحي اسمها ، فهي غير قابلة للتغيير ، لذا فإن الكود التالي سيتسبب في حدوث خطأ في وقت الترجمة.
let c = 1 c = 3 // error
يمكن أيضًا إعلان الأنواع الأخرى على أنها ثابتة. على سبيل المثال ، يعلن الكود التالي عن مصفوفة على أنها ثابتة ، وإذا حاولت تعديل أي عنصر من العناصر ، فسيقوم مترجم Swift بالإبلاغ عن خطأ:
var arr2 = [4, 5, 6] arr2[0] = 8 print (arr2) // [8, 5, 6] let arr = [1, 2, 3] a[0] = 5 // error
خيارات
يجب تهيئة الثوابت عند التصريح عنها ، كما يجب تهيئة المتغيرات قبل الاستخدام. إذن أين هو الهدف- C nil
مكافئ؟ يقدم Swift قيمًا اختيارية . يمكن أن يكون للقيم الاختيارية قيمة أو nil
. إذا ألقيت نظرة على الكود التالي ، ستلاحظ أنه تم تعيين قيمة Optional
لـ x
لعام 2014
. هذا يعني أن مترجم Swift كان على علم بأن x
قد يكون nil
أيضًا.
var s = "2014" var x = s.toInt() print(x) // Optional(2014)
إذا قمت بإجراء تغيير في هذا الرمز وقمت بتعيين القيمة "abc"
إلى s
، والتي لا يمكن تحويلها إلى عدد صحيح ، فستلاحظ أن x
أصبح الآن nil
.
var s = "abc" var x = s.toInt() print(x) // nil
نوع الإرجاع toInt()
هو Int?
، وهو Int. اختياري . دعنا نحاول استدعاء دالة قياسية على x
:
var x = "2014".toInt() print(x.successor()) // error
يشير المترجم إلى خطأ ، لأن x
اختياري ويمكن أن يكون لا شيء . علينا اختبار x
أولاً ، والتأكد من استدعاء الدالة successor
على رقم حقيقي ، وليس قيمة nil
:
var x = "2014".toInt() if x != nil { print(x!.successor()) // 2015 }
لاحظ أنه يتعين علينا فك x
عن طريق إلحاق علامة تعجب (!) . عندما نتأكد من احتواء x
على قيمة ، يمكننا الوصول إليها. وإلا فإننا سوف تحصل على خطأ وقت التشغيل. يمكننا أيضًا القيام بما تسميه Swift الربط الاختياري ، وتحويل الاختياري إلى متغير غير اختياري
let x = "123".toInt() if let y = x { print(y) }
لن يتم تنفيذ الكود الموجود في عبارة if
إلا إذا كانت قيمة x
، وتعيينها إلى y
. لاحظ أننا لسنا بحاجة إلى فك غلاف y
، فنوعه ليس اختياريًا لأننا نعلم أن x
ليس nil
.
تحقق من برنامج Apple التعليمي Swift لقراءة المزيد من التفاصيل حول الاختيارات والميزات الرائعة مثل التسلسل الاختياري
سلسلة الاستيفاء
في Objective-C ، يتم إجراء سلاسل التنسيق عادةً باستخدام stringWithFormat:
method:
NSString *user = @"Gabriel"; int days = 3; NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];
يحتوي Swift على ميزة تسمى الاستيفاء بالسلسلة للقيام بالشيء نفسه ، لكنها أكثر إحكاما وأسهل في القراءة:
let user = "Gabriel" let days = 3 let s = "posted by \(user) \(days) ago"
يمكنك أيضًا استخدام التعبيرات:
let width = 2 let height = 3 let s = "Area for square with sides \(width) and \(height) is \(width*height)"
لمعرفة المزيد حول استيفاء سلسلة Swift والميزات الجديدة الأخرى ، انتقل هنا.
المهام
يختلف تعريف الوظيفة في Swift عن C.
func someFunction(s:String, i: Int) -> Bool { ... // code }
وظائف Swift هي أنواع من الدرجة الأولى . هذا يعني أنه يمكنك تعيين وظائف للمتغيرات ، أو تمريرها كمعلمات إلى وظائف أخرى ، أو جعلها ترجع الأنواع:
func stringLength(s:String) -> Int { return countElements(s) } func stringValue(s:String) -> Int { if let x = s.toInt() { return x } return 0 } func doSomething(f:String -> Int, s:String) -> Int { return f(s).successor() } let f1 = stringLength let f2 = stringValue doSomething(f1, "123") // 4 doSomething(f2, "123") // 124
مرة أخرى ، يستنتج Swift أنواع f1
و f2
( String
-> Int
) ، على الرغم من أنه كان بإمكاننا تعريفهما بوضوح:
let f1:String -> Int = stringLength
يمكن للوظائف أيضًا إرجاع وظائف أخرى:
func compareGreaterThan(a: Int, b: Int) -> Bool { return a > b } func compareLessThan(a: Int, b: Int) -> Bool { return a < b } func comparator(greaterThan:Bool) -> (Int, Int) -> Bool { if greaterThan { return compareGreaterThan } else { return compareLessThan } } let f = comparator(true) println(f(5, 9))
يمكن العثور على دليل للوظائف في Swift هنا.
التعداد
تعد عمليات التعداد في Swift أقوى بكثير مما كانت عليه في Objective-C. بصفتها هياكل Swift ، يمكن أن يكون لها طرق ، ويتم تمريرها حسب القيمة:
enum MobileDevice : String { case iPhone = "iPhone", Andro, WP8 = "Windows Phone8", BB = "BlackBerry" func name() -> String { return self.toRaw() } } let m = MobileDevice.Android print(m.name()) // "Android"
على عكس Objective-C ، يمكن لتعدادات Swift تعيين سلاسل أو أحرف أو عوامات كقيم لكل عضو ، إلى جانب الأعداد الصحيحة. ترجع طريقة toRaw()
الملائمة القيمة المعينة لكل عضو.
يمكن أيضًا تحديد معلمات التعداد:
enum Location { case Address(street:String, city:String) case LatLon(lat:Float, lon:Float) func description() -> String { switch self { case let .Address(street, city): return street + ", " + city case let .LatLon(lat, lon): return "(\(lat), \(lon))" } } } let loc1 = Location.Address(street: "2070 Fell St", city: "San Francisco") let loc2 = Location.LatLon(lat: 23.117, lon: 45.899) print(loc1.description()) // "2070 Fell St, San Francisco" print(loc2.description()) // "(23.117, 45.988)"
مزيد من المعلومات حول التعداد متاحة هنا.

مجموعات
تقوم المجموعات بتجميع القيم المتعددة في قيمة مركبة واحدة. يمكن أن تكون القيم داخل المجموعة من أي نوع ولا يلزم أن تكون من نفس النوع مثل بعضها البعض.
let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel
يمكنك أيضًا تسمية عناصر المجموعة الفردية:
let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)
تعتبر Tuples ملائمة للغاية كأنواع إرجاع للوظائف التي تحتاج إلى إرجاع أكثر من قيمة واحدة:
func intDivision(a: Int, b: Int) -> (quotient: Int, remainder: Int) { return (a/b, a%b) } print(intDivision(11, 3)) // (3, 2) let result = intDivision(15, 4) print(result.remainder) // 3
بخلاف ما في Objective-C ، يدعم Swift مطابقة النمط في بيان التبديل:
let complex = (2.0, 1.1) // real and imaginary parts switch complex { case (0, 0): println("Number is zero") case (_, 0): println("Number is real") default: println("Number is imaginary") }
في الحالة الثانية ، لا نهتم بالجزء الحقيقي من الرقم ، لذلك نستخدم _
لمطابقة أي شيء. يمكنك أيضًا التحقق من وجود شروط إضافية في كل حالة. لذلك نحتاج إلى ربط قيم النمط بالثوابت:
let complex = (2.0, 1.1) switch complex { case (0, 0): println("Number is zero") case (let a, 0) where a > 0: println("Number is real and positive") case (let a, 0) where a < 0: println("Number is real and negative") case (0, let b) where b != 0: println("Number has only imaginary part") case let (a, b): println("Number is imaginary with distance \(a*a + b*b)") }
لاحظ كيف نحتاج إلى ربط القيم التي سنستخدمها فقط في المقارنة أو في بيان الحالة.
لقراءة المزيد عن المجموعات ، اذهب هنا.
الطبقات والهياكل
على عكس Objective-C ، لا يتطلب Swift منك إنشاء واجهة منفصلة وملفات تنفيذ للفئات والهياكل المخصصة. أثناء تعلمك لـ Swift ، ستتعلم كيفية تحديد فئة أو هيكل في ملف واحد ، وستتوفر الواجهة الخارجية لتلك الفئة أو الهيكل تلقائيًا لاستخدام رمز آخر.
تحديد الفئات
تعريفات الفئات بسيطة للغاية:
class Bottle { var volume: Int = 1000 func description() -> String { return "This bottle has \(volume) ml" } } let b = Bottle() print(b.description())
كما ترى ، فإن الإعلان والتنفيذ موجودان في نفس الملف . لم يعد Swift يستخدم ملفات الرأس والتنفيذ. دعنا نضيف تسمية إلى مثالنا:
class Bottle { var volume: Int = 1000 var label:String func description() -> String { return "This bottle of \(label) has \(volume) ml" } }
سيشتكي المترجم لأن الملصق متغير غير اختياري ولن يحتفظ بقيمة عند إنشاء زجاجة. نحتاج إلى إضافة مُهيئ:
class Bottle { var volume: Int = 1000 var label:String init(label:String) { self.label = label } func description() -> String { return "This bottle of \(label) has \(volume) ml" } }
أو يمكننا استخدام النوع Optional
لخاصية لا يجب تهيئتها. في المثال التالي ، جعلنا volume
Optional Integer
:
class Bottle { var volume: Int? var label:String init(label:String) { self.label = label } func description() -> String { if self.volume != nil { return "This bottle of \(label) has \(volume!) ml" } else { return "A bootle of \(label)" } } }
الهياكل
تحتوي لغة Swift أيضًا على structs
، لكنها أكثر مرونة من Objective-C. يحدد البرنامج التعليمي التالي للشفرة struct
:
struct Seat { var row: Int var letter:String init (row: Int, letter:String) { self.row = row self.letter = letter } func description() -> String { return "\(row)-\(letter)" } }
مثل الفئات في Swift ، يمكن أن تحتوي الهياكل على طرق وخصائص ومُهيِّئات وتتوافق مع البروتوكولات. يتمثل الاختلاف الرئيسي بين الفئات والهياكل في أن الفئات يتم تمريرها عن طريق المرجع ، بينما يتم تمرير البنى بالقيمة .
يوضح هذا المثال تمرير الفئات حسب المرجع:
let b = Bottle() print(b.description()) // "b" bottle has 1000 ml var b2 = b b.volume = 750 print(b2.description()) // "b" and "b2" bottles have 750 ml
إذا جربنا حالة مماثلة مع struct
، ستلاحظ أن المتغيرات يتم تمريرها بالقيمة:
var s1 = Seat(row: 14, letter:"A") var s2 = s1 s1.letter = "B" print(s1.description()) // 14-B print(s2.description()) // 14-A
متى يجب استخدام struct
ومتى يجب استخدام class
؟ كما هو الحال في Objective-C و C ، استخدم الهياكل عندما تحتاج إلى تجميع بعض القيم ، وتوقع نسخها بدلاً من الرجوع إليها. على سبيل المثال ، الأرقام المركبة أو النقاط ثنائية أو ثلاثية الأبعاد أو ألوان RGB.
يُعرف مثيل الفئة تقليديًا باسم كائن. ومع ذلك ، فإن فئات وهياكل Swift أقرب في الوظائف منها في اللغات الأخرى ، ويمكن تطبيق الكثير من الوظائف على مثيلات فئة أو نوع هيكل. لهذا السبب ، فإن المصطلح الأكثر عمومية المستخدم في مرجع Swift هو instance
، والذي ينطبق على أي من هذين.
تعلم أساسيات فصول وهياكل Swift هنا.
الخصائص
كما رأينا سابقًا ، يتم الإعلان عن الخصائص في Swift بالكلمة الأساسية var
داخل فئة أو تعريف هيكل. يمكننا أيضًا إعلان الثوابت ببيان let
.
struct FixedPointNumber { var digits: Int let decimals: Int } var n = FixedPointNumber(digits: 12345, decimals: 2) n.digits = 4567 // ok n.decimals = 3 // error, decimals is a constant
ضع في اعتبارك أيضًا أن خصائص الفئة تمت الإشارة إليها بشدة ، ما لم تسبقها بكلمة أساسية weak
. ومع ذلك ، هناك بعض التفاصيل الدقيقة ذات الخصائص الضعيفة غير الاختيارية ، لذا اقرأ فصل حساب المرجع التلقائي في دليل Swift الخاص بشركة Apple.
الخصائص المحسوبة
الخصائص المحسوبة لا تخزن قيمة في الواقع. وبدلاً من ذلك ، فإنها توفر دالة getter ومُعيِّنًا اختياريًا لاسترداد الخصائص والقيم الأخرى وتعيينها بشكل غير مباشر.
يوفر الكود التالي عينة من sign
القيمة المحسوبة:
enum Sign { case Positive case Negative } struct SomeNumber { var number:Int var sign:Sign { get { if number < 0 { return Sign.Negative } else { return Sign.Positive } } set (newSign) { if (newSign == Sign.Negative) { self.number = -abs(self.number) } else { self.number = abs(self.number) } } } }
يمكننا أيضًا تحديد خصائص القراءة فقط من خلال تنفيذ دالة getter:
struct SomeNumber { var number:Int var isEven:Bool { get { return number % 2 == 0 } } }
في Objective-C ، عادةً ما يتم دعم الخصائص بواسطة متغير حالة ، يتم الإعلان عنه صراحةً أو تلقائيًا بواسطة المترجم. في Swift ، من ناحية أخرى ، لا تحتوي الخاصية على متغير مثيل مطابق . بمعنى ، لا يمكن الوصول إلى مخزن الدعم الخاص بالممتلكات مباشرة. لنفترض أن لدينا هذا في Objective-C
// .h @interface OnlyInitialString : NSObject @property(strong) NSString *string; @end // .m @implementation OnlyInitialString - (void)setString:(NSString *newString) { if (newString.length > 0) { _string = [newString substringToIndex:1]; } else { _string = @""; } } @end
نظرًا لأن الخصائص المحسوبة في Swift لا تحتوي على مخزن دعم ، فنحن بحاجة إلى القيام بشيء مثل هذا:
class OnlyInitialString { var initial:String = "" var string:String { set (newString) { if countElements(newString) > 0 { self.initial = newString.substringToIndex(advance(newString.startIndex, 1)) } else { self.initial = "" } } get { return self.initial } } }
يتم شرح الخصائص بمزيد من التفاصيل هنا
يتبع
هناك العديد من الأشياء الجديدة الأكثر أهمية للتعرف عليها في Swift ، مثل Generics والتفاعل مع مكتبات Objective-C والإغلاق والتسلسل الاختياري وتحميل المشغل الزائد. لا يمكن أن يصف برنامج تعليمي واحد لغة جديدة تمامًا ، لكن ليس لدي شك في أنه سيتم كتابة المزيد حول تعلم برمجة Swift. ومع ذلك ، أعتقد أن هذه القراءة السريعة ستساعد العديد من مطوري Objective-C ، الذين لم يتمكنوا من إيجاد الوقت وتعلم تفاصيل لغة Swift ، على المسار الصحيح والسماح للطائر Swift بنقلهم إلى آفاق جديدة.