Swift เป็นภาษาโปรแกรมของ Apple ที่รวมพลังของ Objective-C เข้ากับความปลอดภัยของภาษาสมัยใหม่ แตกต่างจากภาษาอื่นๆ มากมาย Swift มีโครงสร้างที่เป็นเอกลักษณ์: ชนิดค่า ที่มีการคัดลอกอัตโนมัติ, Actors ที่มีการแยกในระดับคอมไพเลอร์, Property Wrappers สำหรับการใช้ตรรกะคุณสมบัติซ้ำ และการเขียนโปรแกรมเชิงโปรโตคอลเป็นทางเลือกแทนการสืบทอดแบบคลาสสิก ตามเอกสาร Apple Swift คุณสมบัติเหล่านี้ทำให้โค้ดปลอดภัยและมีประสิทธิภาพมากขึ้น
ประเด็นสำคัญ
ใน Swift, struct เป็นชนิดค่า ในขณะที่ class เป็นชนิดอ้างอิง ความแตกต่างพื้นฐานนี้กำหนดพฤติกรรมระหว่างการกำหนด การส่งไปยังฟังก์ชัน และการจัดการหน่วยความจำ Struct ถูกคัดลอกทั้งหมดทุกครั้งที่กำหนด class ถูกส่งโดยการอ้างอิง — เฉพาะตัวชี้ไปยังออบเจกต์บนฮีปเท่านั้นที่ถูกคัดลอก
Struct ใช้ความหมายค่าโดยอัตโนมัติ — แต่ละอินสแตนซ์เป็นอิสระ การกลายพันธุ์ของคุณสมบัติ struct ทำได้ผ่าน var เท่านั้น และเมธอดที่ปรับเปลี่ยนคุณสมบัติต้องถูกทำเครื่องหมายเป็น mutating Struct ไม่สนับสนุนการสืบทอดแต่สามารถทำตามโปรโตคอลได้ ไลบรารีมาตรฐานของ Swift ใช้ struct สำหรับ String, Array, Dictionary, Int, Bool — ชนิดพื้นฐานทั้งหมดเป็นชนิดค่า
struct User {
let id: Int
var name: String
mutating func updateName(_ newName: String) {
name = newName
}
}
var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// คัดลอกทั้งหมด — อินสแตนซ์อิสระ
Class ถูกเก็บในฮีปและสนับสนุนความเท่าเทียมแบบอ้างอิง ตัวแปรหลายตัวสามารถอ้างอิงถึงออบเจกต์เดียวกันได้ การเปลี่ยนแปลงผ่านการอ้างอิงหนึ่งจะมองเห็นผ่านการอ้างอิงอื่นทั้งหมด Class สนับสนุนการสืบทอด อนุญาตให้แทนที่เมธอด (override) มี deinit สำหรับทำความสะอาดทรัพยากร ใน Swift class ถูกใช้น้อยกว่า struct — Apple แนะนำให้เริ่มต้นด้วย struct และเปลี่ยนเป็น class เฉพาะเมื่อต้องการการสืบทอดหรือความหมายอ้างอิง
class Car {
let model: String
var speed: Int
init(model: String, speed: Int) {
self.model = model
self.speed = speed
}
func accelerate(_ amount: Int) {
speed += amount
}
}
let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// การอ้างอิงเดียวกัน — การเปลี่ยนแปลงมองเห็นได้ทุกที่
การเลือกระหว่าง struct และ class ขึ้นอยู่กับความหมายของข้อมูล สำหรับ การกำหนดค่า แบบจำลองข้อมูล และสถานะ ใช้ struct — การคัดลอกปลอดภัยและคาดเดาได้มากกว่า สำหรับออบเจกต์ที่มีเอกลักษณ์ (เซสชันผู้ใช้ การเชื่อมต่อฐานข้อมูล) และเมื่อต้องการการสืบทอด ให้ใช้ class
Actor เป็นชนิดอ้างอิงที่ปกป้องสถานะที่เปลี่ยนแปลงได้จากการเข้าถึงพร้อมกัน คอมไพเลอร์ Swift รับประกันว่าตัวแปร actor สามารถเข้าถึงได้จากบริบท actor เดียวกันเท่านั้น — การเข้าถึงภายนอกใดๆ ทำให้เกิดข้อผิดพลาดในการคอมไพล์ ซึ่งป้องกันการแข่งข้อมูลในระดับภาษา โดยไม่ต้องล็อกด้วยตนเอง
คุณสมบัติและเมธอดทั้งหมดของ actor ถูกแยกตามค่าเริ่มต้น การเรียกเมธอด actor จากภายนอกต้องใช้ await — Swift สลับการดำเนินการไปยัง actor และกลับ Actor ซิงโครไนซ์การเข้าถึงตามลำดับ: คำขอจากงานต่างๆ ถูกจัดคิว ซึ่งรับประกันสถานะที่สอดคล้องภายใน actor เมธอดที่ไม่แยก (nonisolated) สามารถเรียกได้โดยไม่ต้อง await แต่ไม่สามารถเข้าถึงคุณสมบัติที่เปลี่ยนแปลงได้
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func withdraw(amount: Double) throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
}
nonisolated func accountInfo() -> String {
return "บัญชีธนาคาร"
}
}
let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — อะตอมมิก ไม่มีสภาพแข่ง
Actor มีประโยชน์สำหรับ ผู้จัดการสถานะ แคช และบริการเครือข่าย ที่งานหลายงานอ่านและเขียนข้อมูลพร้อมกัน ต่างจากการซิงโครไนซ์ด้วยตนเองผ่าน NSLock หรือ DispatchQueue actor ถูกตรวจสอบในเวลาคอมไพล์ — เป็นไปไม่ได้ที่จะทำผิดพลาด ข้อผิดพลาดการแยกปรากฏในเวลาคอมไพล์ ไม่ใช่เวลารันไทม์
Property Wrapper คือคำอธิบายประกอบที่เพิ่มพฤติกรรมให้กับคุณสมบัติโดยไม่เปลี่ยนแปลงการประกาศ SwiftUI และ Combine มี wrapper ในตัวสำหรับการจัดการสถานะของวิว คอมไพเลอร์สร้างโค้ดที่ห่อหุ้มการเข้าถึงคุณสมบัติ เพิ่มตรรกะการสังเกต การซิงโครไนซ์ หรือการแปลง
@Published ใช้ในคลาสที่ทำตาม ObservableObject เพื่อเผยแพร่การเปลี่ยนแปลงโดยอัตโนมัติ ผู้ติดตาม Combine ได้รับการแจ้งเตือนทุกครั้งที่อัปเดต @State คือสถานะท้องถิ่นของวิว SwiftUI SwiftUI จะวาดวิวใหม่โดยอัตโนมัติเมื่อ @State เปลี่ยนแปลง @Binding สร้างการเชื่อมต่อสองทางระหว่างวิวแม่และวิวลูกโดยไม่เป็นเจ้าของข้อมูล
import SwiftUI
import Combine
@main
struct CounterApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct ContentView: View {
@State private var greeting: String = "สวัสดี"
@StateObject private var viewModel = CounterViewModel()
var body: some View {
VStack {
CounterDisplay(count: $viewModel.count)
Text(greeting)
Button("เพิ่ม", action: viewModel.increment)
}
}
}
struct CounterDisplay: View {
@Binding var count: Int
var body: some View {
Text("นับ: \(count)")
}
}
Property Wrappers ที่กำหนดเองถูกสร้างขึ้นโดยใช้ @propertyWrapper และโครงสร้างที่มีฟิลด์ wrappedValue ซึ่งช่วยให้ใช้ตรรกะซ้ำ: การตรวจสอบ การทำให้เป็นปกติ การจัดเก็บ UserDefaults Property Wrappers ลดโค้ดเทมเพลตอย่างมากและทำให้เจตนาของนักพัฒนาชัดเจนผ่านคำอธิบายประกอบ
@propertyWrapper
struct Clamped<T: Comparable> {
private var value: T
let range: ClosedRange<T>
init(wrappedValue: T, range: ClosedRange<T>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: T {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct Settings {
@Clamped(range: 0...100) var volume: Int = 50
}
var s = Settings()
s.volume = 150
// s.volume === 100 — ค่าถูกจำกัดโดยอัตโนมัติ
Generics เป็นกลไกทรงพลังของ Swift สำหรับเขียนโค้ดที่ยืดหยุ่นโดยไม่สูญเสียความปลอดภัยของชนิด ฟังก์ชันและชนิดทั่วไปทำงานกับชนิดใดๆ ที่ตรงตามข้อจำกัด การเขียนโปรแกรมเชิงโปรโตคอล (POP) เป็นกระบวนทัศน์ของ Swift ที่ protocol + extension แทนที่การสืบทอดคลาสแบบคลาสสิก ให้การประกอบพฤติกรรม
Swift อนุญาตให้จำกัดพารามิเตอร์ทั่วไปด้วยโปรโตคอล (ประโยค where) โปรโตคอลพร้อมชนิดที่เกี่ยวข้อง เป็นสิ่งที่เทียบเท่าทั่วไปสำหรับโปรโตคอล: ชนิดที่เป็นรูปธรรมถูกกำหนดในเวลาที่ทำตาม นี่คือพื้นฐานสำหรับคอลเล็กชันและอัลกอริทึมที่ปลอดภัยชนิด ไลบรารีมาตรฐานของ Swift ใช้ Generics อย่างแข็งขัน: Array, Dictionary, Optional — ล้วนเป็นชนิดทั่วไป
protocol Cacheable {
associatedtype Key: Hashable
associatedtype Value
func get(_ key: Key) -> Value?
mutating func set(_ value: Value, for key: Key)
}
struct MemoryCache<K: Hashable, V>: Cacheable {
private var storage: [K: V] = [:]
typealias Key = K
typealias Value = V
func get(_ key: K) -> V? {
return storage[key]
}
mutating func set(_ value: V, for key: K) {
storage[key] = value
}
}
func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
for (index, item) in array.enumerated() {
if item == target {
return index
}
}
return nil
}
let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — หนึ่งเมธอดทั่วไปสำหรับทุกชนิด Equatable
Extension โปรโตคอลเป็นกลไกสำคัญของ POP ใน extension สามารถให้การใช้งานเริ่มต้นของเมธอดโปรโตคอลได้ ชนิดที่ทำตามโปรโตคอลได้รับการใช้งานนี้ฟรีและสามารถแทนที่ได้หากจำเป็น ซึ่งช่วยให้สร้างลำดับชั้นพฤติกรรมโดยไม่ต้องสืบทอดคลาส — ทางเลือกแทนการสืบทอดหลายแบบ ซึ่งไม่มีใน Swift
protocol Drivable {
var speed: Double { get set }
func drive()
}
extension Drivable {
func drive() {
if speed > 0 {
print("ขับที่ความเร็ว \(speed) กม./ชม.")
}
}
}
struct Bicycle: Drivable {
var speed: Double = 15
}
struct Plane: Drivable {
var speed: Double = 900
func drive() {
print("บินที่ความเร็ว \(speed) กม./ชม.")
}
}
let bike = Bicycle()
bike.drive() // "ขับที่ความเร็ว 15.0 กม./ชม." — เริ่มต้น
let plane = Plane()
plane.drive() // "บินที่ความเร็ว 900.0 กม./ชม." — แทนที่
POP พร้อม extension ช่วยให้ ประกอบผ่านหลายโปรโตคอล — ชนิดสามารถทำตามโปรโตคอลหลายตัวและได้รับการใช้งาน ยืดหยุ่นกว่าการสืบทอดคลาสเดี่ยวที่ลำดับชั้นทั้งหมดถูกกำหนดไว้ ไลบรารีมาตรฐานของ Swift สร้างบน POP: CustomStringConvertible, Equatable, Hashable, Codable — ล้วนเป็นโปรโตคอลที่มี extension
Enum ใน Swift เป็นโครงสร้างที่ทรงพลังกว่าภาษาอื่นๆ อย่างมาก แต่ละเคสสามารถมีค่าที่เกี่ยวข้อง — ชุดข้อมูลตามอำเภอใจของชนิดใดก็ได้ เมื่อรวมกับ extension และการจับคู่รูปแบบ enum กลายเป็นพื้นฐานสำหรับการสร้างแบบจำลองสถานะ ผลลัพธ์การดำเนินการ และเครื่องจักรสถานะจำกัด
ค่าที่เกี่ยวข้องช่วยให้แต่ละเคสเก็บข้อมูลที่ไม่ซ้ำกัน ตัวอย่างเช่น เคส loading ไม่มีข้อมูล success มีผลลัพธ์ failure มีข้อผิดพลาด การจับคู่รูปแบบผ่าน switch ดึงค่าที่เกี่ยวข้องและจัดการแต่ละเคส คอมไพเลอร์ตรวจสอบความครอบคลุม (exhaustive switch) — ซึ่งกำจัดสถานะที่ถูกลืม
enum NetworkResult<T> {
case loading
case success(T)
case failure(Error)
}
enum MediaFile {
case image(url: URL, width: Int, height: Int)
case video(url: URL, duration: Double)
case audio(url: URL, bitrate: Int)
}
func handle(_ result: NetworkResult<String>) {
switch result {
case .loading:
print("กำลังโหลด...")
case .success(let data):
print("ได้รับ: \(data)")
case .failure(let error):
print("ข้อผิดพลาด: \(error.localizedDescription)")
}
}
let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)
if case .video(let url, let duration) = file {
print("วิดีโอ \(url) ระยะเวลา \(duration)วินาที")
}
Swift อนุญาตให้เพิ่มเมธอด คุณสมบัติที่คำนวณได้ และการทำตามโปรโตคอลผ่าน extension สำหรับ enum ไม่จำเป็นต้องเปลี่ยนการประกาศดั้งเดิม — โค้ดยังคงสะอาดและเป็นโมดูลาร์ เมธอดใน extension สามารถทำงานกับค่าที่เกี่ยวข้อง คำนวณข้อมูลที่ได้รับ และใช้ตรรกะทางธุรกิจ
extension MediaFile {
var fileName: String {
switch self {
case .image(let url, _, _),
.video(let url, _),
.audio(let url, _):
return url.lastPathComponent
}
}
var description: String {
switch self {
case .image(_, let w, let h):
return "รูปภาพ \(w)x\(h)"
case .video(_, let d):
return "วิดีโอ \(d)ว"
case .audio(_, let b):
return "เสียง \(b) kbps"
}
}
}
extension MediaFile: Equatable {
static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
return lhs.fileName == rhs.fileName
}
}
let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "วิดีโอ 120.0s"
Enum พร้อมค่าที่เกี่ยวข้อง + extension คือสิ่งทดแทนคลาสปิดผนึกจาก Kotlin หรือยูเนียนจำแนกจากภาษาอื่น Swift ใช้การผสมผสานนี้สำหรับ Result (ชนิดมาตรฐานในไลบรารีมาตรฐาน), Optional (Optional คือ enum ที่มี .none และ .some) และการจัดการสถานะใน SwiftUI การจับคู่รูปแบบรับประกันว่าทุกสถานะถูกจัดการ — ข้อผิดพลาดเวลารันไทม์เป็นไปไม่ได้
คำถามที่พบบ่อย
Struct — ชนิดค่า ถูกคัดลอกเมื่อกำหนด ไม่สนับสนุนการสืบทอด Class — ชนิดอ้างอิง ถูกส่งโดยการอ้างอิง สนับสนุนการสืบทอด deinit และการตรวจสอบเอกลักษณ์ (===) Apple แนะนำให้เริ่มต้นด้วย struct และเปลี่ยนเป็น class เฉพาะเมื่อต้องการความหมายอ้างอิง
Actor — ชนิดอ้างอิงที่ปลอดภัยต่อเธรด คอมไพเลอร์ป้องกันการเข้าถึงสถานะที่เปลี่ยนแปลงได้พร้อมกันจากบริบทต่างๆ ใช้ Actor สำหรับผู้จัดการสถานะ แคช และบริการที่เข้าถึงโดยหลายงาน การเรียกเมธอด actor ต้องใช้ await
หลักๆ: @State — สถานะวิวท้องถิ่น; @Binding — การเชื่อมต่อข้อมูลโดยไม่เป็นเจ้าของ; @Published — เผยแพร่การเปลี่ยนแปลงใน ObservableObject; @StateObject — เป็นเจ้าของ ObservableObject; @EnvironmentObject — การพึ่งพาจากสภาพแวดล้อม Wrapper ที่กำหนดเองสร้างผ่าน @propertyWrapper
POP — กระบวนทัศน์ที่ protocol + extension กำหนดพฤติกรรมแทนการสืบทอดคลาส ชนิดทำตามโปรโตคอลหลายตัวและได้รับการใช้งานฟรี นี่คือการประกอบแทนการสืบทอด — ยืดหยุ่นและปลอดภัยกว่า ไลบรารีมาตรฐานของ Swift (Equatable, Codable) สร้างบน POP
Enum ใน Swift สนับสนุนค่าที่เกี่ยวข้อง — แต่ละเคสสามารถเก็บข้อมูลตามอำเภอใจของชนิดต่างๆ เมื่อรวมกับการจับคู่รูปแบบ (switch) ให้แบบจำลองทรงพลังสำหรับสถานะ ข้อผิดพลาด และการออกแบบที่ขับเคลื่อนด้วยโดเมน Optional และ Result เป็น enum มาตรฐานของ Swift พร้อมค่าที่เกี่ยวข้อง
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ