คู่มือนักพัฒนา iOS: จาก Objective-C สู่การเรียนรู้ Swift

เผยแพร่แล้ว: 2022-03-11

ในปี 2008 Apple ได้ประกาศและเปิดตัว iPhone SDK 2.0 เหตุการณ์นี้เริ่มต้นการปฏิวัติอีกครั้งในการพัฒนาซอฟต์แวร์ และเกิดนักพัฒนาสายพันธุ์ใหม่ ตอนนี้พวกเขาได้รับการยอมรับว่าเป็นนักพัฒนา iOS

นักพัฒนาเหล่านี้หลายคนไม่เคยใช้ Objective-C มาก่อน และนั่นเป็นความท้าทายแรกที่ Apple เสนอให้พวกเขา แม้ว่ารูปแบบไวยากรณ์ที่ไม่คุ้นเคยและการจัดการหน่วยความจำด้วยตนเอง แต่ก็ประสบความสำเร็จอย่างมาก โดยช่วยให้ App Store เต็มไปด้วยแอปนับหมื่น 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 iOS

ลองใช้ Swift กับ Objective-C

เพื่อเริ่มสำรวจ Swift สิ่งที่คุณต้องทำคือดาวน์โหลด XCode จาก App Store และสร้าง Playground เพื่อทดลอง ตัวอย่างทั้งหมดที่กล่าวถึงในบทความนี้ทำในลักษณะนี้

หน้าแรกของ 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

ตัวเลือก

ค่าคงที่จำเป็นต้องเริ่มต้นเมื่อประกาศ และต้องเตรียมข้อมูลเบื้องต้นตัวแปรก่อนใช้งาน แล้ว Objective-C nil มีค่าเท่ากันตรงไหน? Swift แนะนำ ค่าทางเลือก ค่าทางเลือกสามารถมีค่าหรือเป็น nil หากคุณดูรหัสต่อไปนี้ คุณจะสังเกตเห็นว่า x ถูกกำหนดเป็นค่า Optional ของ 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

ตรวจสอบบทช่วยสอน Swift ของ Apple เพื่ออ่านรายละเอียดเพิ่มเติมเกี่ยวกับตัวเลือกและคุณสมบัติที่ดีเช่นการต่อสายโซ่เสริม

การแก้ไขสตริง

ในสตริงการจัดรูปแบบ Objective-C มักจะทำด้วย stringWithFormat: วิธีการ:

 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)"

ข้อมูลเพิ่มเติมเกี่ยวกับการแจงนับมีอยู่ที่นี่

ทูเปิลส์

Tuples จัดกลุ่มค่าหลายค่าเป็นค่าผสมเดียว ค่าภายในทูเพิลสามารถเป็นชนิดใดก็ได้ และไม่จำเป็นต้องเป็นค่าชนิดเดียวกัน

 let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel

คุณยังสามารถตั้งชื่อองค์ประกอบ tuple แต่ละรายการได้:

 let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)

ทูเปิลสะดวกมากเนื่องจากประเภทการส่งคืนสำหรับฟังก์ชันที่ต้องการส่งคืนค่ามากกว่าหนึ่งค่า:

 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 รองรับการจับคู่รูปแบบในคำสั่ง switch:

 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)") }

สังเกตว่าเราจำเป็นต้องผูกเฉพาะค่าที่เราจะใช้ในการเปรียบเทียบหรือในคำสั่ง case อย่างไร

หากต้องการอ่านเพิ่มเติมเกี่ยวกับทูเพิล ไปที่นี่

ชั้นเรียนและโครงสร้าง

ต่างจาก 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" } }

คอมไพเลอร์จะบ่นเพราะเลเบลเป็นตัวแปรที่ไม่ใช่ตัวเลือกและจะไม่เก็บค่าไว้เมื่อมีการสร้างอินสแตนซ์ของ Bottle เราจำเป็นต้องเพิ่มตัวเริ่มต้น:

 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 type สำหรับคุณสมบัติที่ไม่ต้องเตรียมข้อมูลเบื้องต้น ในตัวอย่างต่อไปนี้ เราทำให้ 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 โครงสร้างสามารถมีเมธอด คุณสมบัติ ตัวเริ่มต้น และสอดคล้องกับโปรโตคอลได้ ความแตกต่างหลัก ระหว่างคลาสและโครงสร้างคือ คลาสถูกส่งโดยการอ้างอิง ในขณะที่ struct ถูกส่งผ่าน value

ตัวอย่างนี้แสดงให้เห็นถึงคลาสที่ผ่านโดยการอ้างอิง:

 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 ให้ใช้ struct เมื่อคุณต้องการจัดกลุ่มค่าสองสามค่า และคาดหวังให้พวกมันถูกคัดลอกแทนที่จะอ้างอิง ตัวอย่างเช่น ตัวเลขเชิงซ้อน จุด 2D หรือ 3D หรือสี 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 พาพวกเขาไปสู่อีกระดับ