Class เป็นชนิดอ้างอิง (reference type) ในภาษา Swift ซึ่งอินสแตนซ์ของมันจะถูกส่งผ่านด้วยการอ้างอิงแทนที่จะถูกคัดลอกเมื่อกำหนดค่า ต่างจาก struct ตรงที่คลาสรองรับการสืบทอด การดีอินิเชียลไลเซชัน และการนับการอ้างอิงอัตโนมัติ ARC สำหรับการจัดการหน่วยความจำ ตาม Swift Programming Language Guide, 2026 class จำเป็นสำหรับการทำงานกับเฟรมเวิร์ก UI ของ Apple (UIKit, AppKit) และการนำรูปแบบที่ต้องการอัตลักษณ์ร่วมของวัตถุไปใช้ การเลือกระหว่าง class และ struct เป็นหนึ่งในการตัดสินใจทางสถาปัตยกรรมที่สำคัญใน Swift
ประเด็นสำคัญ
Class — ชนิดอ้างอิงเชิงประกอบใน Swift ที่จัดกลุ่มคุณสมบัติและเมธอดเป็นเอนทิตีเดียวพร้อมการรองรับการสืบทอดและการส่งผ่านแบบไดนามิก ต่างจาก struct ตรงที่อินสแตนซ์คลาสถูกสร้างใน ฮีป และตัวแปรเก็บการอ้างอิงไปยังอินสแตนซ์นี้ ไม่ใช่ข้อมูลตัวมันเอง
เมื่อคุณกำหนดตัวแปรคลาสให้กับตัวแปรอื่น ทั้งคู่อ้างอิงถึงวัตถุเดียวกันในหน่วยความจำ การเปลี่ยนแปลงผ่านการอ้างอิงหนึ่งจะมองเห็นได้ผ่านอีกการอ้างอิงหนึ่ง — นี่คือคุณสมบัติพื้นฐานของชนิดอ้างอิง ที่ใช้ในรูปแบบการมอบหมาย การสังเกต และสถานะร่วม
ตาม เอกสารประกอบ Apple Swift คลาสเป็นวิธีเดียวในการทำงานกับ UIKit และ AppKit ซึ่งส่วนประกอบ UI ทั้งหมดสืบทอดจาก UIView และ UIViewController นอกจากนี้ คลาสยังจำเป็นสำหรับการนำรูปแบบที่ต้องการอัตลักษณ์ของวัตถุ (การอ้างอิงสองครั้งไปยังวัตถุเดียว) และวงจรชีวิตที่ควบคุมได้
ความหมายของชนิดอ้างอิง — คุณสมบัติหลักของคลาส เมื่อกำหนดคลาสให้กับตัวแปรใหม่ Swift จะคัดลอก การอ้างอิง ไม่ใช่ข้อมูล ตัวแปรทั้งหมดที่อ้างอิงถึงอินสแตนซ์เดียวกันจะเห็นสถานะปัจจุบันของมันและสามารถแก้ไขได้ พฤติกรรมนี้แตกต่างโดยพื้นฐานจากชนิดค่า ซึ่งตัวแปรแต่ละตัวจะได้รับสำเนาอิสระ
// ตัวอย่างความหมายของชนิดอ้างอิง
class User {
var name: String
init(name: String) { self.name = name }
}
let user1 = User(name: "Alice")
let user2 = user1 // user2 — การอ้างอิงเดียวกัน
user2.name = "Bob"
print(user1.name) // "Bob" — เปลี่ยนผ่าน user2!
// ตรวจสอบอัตลักษณ์ด้วยตัวดำเนินการ ===
print(user1 === user2) // true — วัตถุเดียวกัน
ตัวดำเนินการ === (อัตลักษณ์) และ !== ตรวจสอบว่าตัวแปรสองตัวอ้างอิงถึงอินสแตนซ์คลาสเดียวกันหรือไม่ ซึ่งแตกต่างจาก == (ความเท่ากัน) ที่เปรียบเทียบค่าคุณสมบัติ ตัวดำเนินการ === ไม่สามารถใช้ได้กับ struct — ชนิดค่าไม่มีอัตลักษณ์
การสืบทอด — กลไกที่คลาสสามารถรับคุณสมบัติและเมธอดของคลาสแม่ ใน Swift คลาสสามารถสืบทอดจากแม่ได้เพียงคนเดียว (การสืบทอดเดี่ยว) แต่สามารถใช้โปรโตคอลหลายตัวได้ คำสำคัญ override อนุญาตให้เขียนทับเมธอดหรือคุณสมบัติที่สืบทอดมา
คลาสใดที่ไม่สืบทอดจากคลาสอื่นจะกลายเป็นคลาส พื้นฐาน โดยอัตโนมัติ (อย่าสับสนกับ NSObject) คลาสย่อยระบุแม่ด้วยเครื่องหมายทวิภาคหลังชื่อ หากคลาสย่อยเขียนทับเมธอดของแม่ จะต้องเรียก super.เมธอด() เพื่อรักษาพฤติกรรมของแม่ — นี่คือข้อกำหนดของคอมไพเลอร์
คำสำคัญ final ก่อน class ป้องกันการสืบทอด คอมไพเลอร์สามารถปรับการเรียกเมธอดของคลาส final ให้เหมาะสมผ่านการส่งแบบคงที่ ซึ่งช่วยเพิ่มประสิทธิภาพ ใช้ final สำหรับคลาสที่ไม่ได้มีไว้สำหรับขยาย — ซึ่งเป็นการบันทึกความตั้งใจของคุณและทำให้โค้ดเร็วขึ้น
// ตัวอย่างการสืบทอดคลาสใน Swift
class Vehicle {
var speed: Double = 0
func description() -> String {
"Speed: \(speed) km/h"
}
}
// Car สืบทอด Vehicle
class Car: Vehicle {
var brand: String = "Unknown"
override func description() -> String {
"\(brand) - \(speed) km/h"
}
}
// คลาส final — ป้องกันการสืบทอด
final class ElectricCar: Car {
var batteryLevel: Double = 100
}
let tesla = ElectricCar()
tesla.brand = "Tesla"
tesla.speed = 120
print(tesla.description()) // "Tesla - 120.0 km/h"
การสืบทอดเป็นกลไกที่ทรงพลังแต่มีความรับผิดชอบ ลำดับชั้นที่ลึก (มากกว่า 5 ระดับ) ทำให้การบำรุงรักษาและการทดสอบซับซ้อน สำหรับการนำฟังก์ชันการทำงานกลับมาใช้ใหม่โดยไม่ต้องสืบทอด ให้ใช้ โปรโตคอล พร้อมส่วนขยายและการเขียนโปรแกรมเชิงโปรโตคอล — แนวทางที่ Apple ส่งเสริมเป็นทางเลือกแทนลำดับชั้นคลาสที่ลึก
Swift ใช้ ARC (การนับการอ้างอิงอัตโนมัติ) สำหรับการจัดการหน่วยความจำคลาส อินสแตนซ์คลาสแต่ละตัวมีตัวนับการอ้างอิงแบบแข็ง เมื่อมีการสร้างการอ้างอิงแบบแข็งใหม่ ตัวนับจะเพิ่มขึ้น เมื่อถูกทำลายจะลดลง เมื่อตัวนับถึงศูนย์ หน่วยความจำจะถูกปล่อย
Deinit — เมธอดที่ถูกเรียกโดยอัตโนมัติก่อนปล่อยอินสแตนซ์คลาส มันปล่อยทรัพยากร: ปิดไฟล์ ยกเลิกการสมัครรับการแจ้งเตือน หยุดตัวจับเวลา Deinit มีเฉพาะในคลาสเท่านั้น — struct และ enum ไม่มี
เพื่อป้องกัน วงจรการอ้างอิงแบบแข็ง (retain cycles) Swift มีการอ้างอิงแบบ weak และ unowned Weak เป็นการอ้างอิงแบบเลือกได้ที่กลายเป็น nil โดยอัตโนมัติเมื่อวัตถุถูกปล่อย Unowned ไม่ใช่แบบเลือกได้แต่อ้างอิงถึงวัตถุที่รับประกันว่ามีอายุยืนกว่าบริบทปัจจุบัน retain cycle ทั่วไปเกิดขึ้นในความสัมพันธ์แม่-ลูก: ลูกเก็บการอ้างอิงแบบแข็งไปยังแม่
// ตัวอย่าง ARC และการอ้างอิงแบบอ่อน
class Parent {
var name: String
var child: Child?
init(name: String) { self.name = name }
deinit { print("\(name) ถูกปล่อย") }
}
class Child {
var name: String
weak var parent: Parent? // weak ป้องกัน retain cycle
init(name: String) { self.name = name }
deinit { print("\(name) ถูกปล่อย") }
}
var parent: Parent? = Parent(name: "Anna")
parent?.child = Child(name: "Mia")
parent?.child?.parent = parent
parent = nil // วัตถุทั้งสองถูกปล่อย!
// ไม่มี weak จะสร้าง retain cycle
ใช้ weak เสมอสำหรับการอ้างอิงจากวัตถุลูกไปยังแม่และสำหรับรายการจับใน closure ใช้ unowned เฉพาะเมื่อคุณแน่ใจว่าวัตถุจะมีอายุยืนกว่าบริบทปัจจุบัน — การใช้ unowned อย่างไม่ถูกต้องอาจทำให้เกิดการขัดข้องเมื่อเข้าถึงหน่วยความจำที่ถูกปล่อย
การเลือกระหว่าง class และ struct เป็นการตัดสินใจทางสถาปัตยกรรมที่ส่งผลต่อประสิทธิภาพ ความปลอดภัยของเธรด และการออกแบบ API พิจารณาตารางความแตกต่างหลักเพื่อช่วยในการตัดสินใจที่ถูกต้องในแต่ละกรณี
| คุณลักษณะ | class | struct |
|---|---|---|
| ชนิด | ชนิดอ้างอิง | ชนิดค่า (สำเนา) |
| หน่วยความจำ | ฮีป + ARC | สแต็ก / แบบอินไลน์ |
| การสืบทอด | รองรับ (แม่เดียว) | ไม่รองรับ |
| Deinit | มี | ไม่มี |
| อัตลักษณ์ (===) | รองรับ | ไม่รองรับ |
| Mutating | ไม่จำเป็น (mutating เสมอ) | เฉพาะกับ mutating |
| Memberwise init | ไม่ถูกสร้าง | ถูกสร้างโดยอัตโนมัติ |
| ความปลอดภัยของเธรด | ไม่รับประกัน (สถานะร่วม) | รับประกัน (การคัดลอก) |
ใช้คลาสเมื่อคุณต้องการ อัตลักษณ์ร่วม (โค้ดหลายส่วนทำงานกับวัตถุเดียว) การสืบทอด หรือการโต้ตอบกับ Objective-C runtime สำหรับอย่างอื่นทั้งหมด struct เป็นที่นิยม — เร็วกว่า ปลอดภัยกว่าในโค้ดหลายเธรด และไม่ต้องการการจัดการหน่วยความจำ
แม้คำแนะนำของ Apple ให้ใช้ struct เป็นค่าเริ่มต้น แต่คลาสก็จำเป็นในหลายสถานการณ์เฉพาะ มาดูแต่ละสถานการณ์พร้อมตัวอย่างปฏิบัติ
ส่วนประกอบ UI ทั้งหมดใน iOS และ macOS เป็นคลาสที่สืบทอดจาก UIView (iOS) หรือ NSView (macOS) คุณไม่สามารถแทนที่ UIViewController ด้วย struct — มันต้องการการสืบทอดและ deinit เพื่อปล่อยทรัพยากร เมื่อทำงานกับ UIKit ให้ใช้คลาสสำหรับตัวควบคุม มุมมอง และตัวแทนของพวกมัน
รูปแบบ ซิงเกิลตัน (อินสแตนซ์คลาสเดียวสำหรับทั้งแอปพลิเคชัน) ต้องการความหมายของการอ้างอิง ผู้จัดการ: NetworkManager, SettingsManager, AnalyticsService — โดยทั่วไปถูกนำมาใช้เป็นคลาสพร้อมคุณสมบัติ static ที่ใช้ร่วมกัน Struct ไม่เหมาะสมเพราะแต่ละสำเนาจะเป็นอินสแตนซ์อิสระ
เมื่อวัตถุต้องเป็น แหล่งความจริงเดียว และถูกส่งระหว่างโมดูลด้วยการอ้างอิง — ให้ใช้ class สิ่งนี้ใช้กับการจัดการสถานะ ซึ่งการเปลี่ยนแปลงวัตถุในที่เดียวจะต้องมองเห็นได้ในส่วนประกอบที่พึ่งพาทั้งหมด สำหรับ ObservableObject ใน SwiftUI คลาสเป็นสิ่งจำเป็น
// ObservableObject — จำเป็นต้องใช้ class สำหรับ SwiftUI
import SwiftUI
class AppViewModel: ObservableObject {
@Published var isLoggedIn: Bool = false
@Published var username: String = ""
func login(user: String) {
isLoggedIn = true
username = user
}
}
// การใช้งานใน SwiftUI View
struct ContentView: View {
@StateObject var viewModel = AppViewModel()
var body: some View {
Text(viewModel.isLoggedIn ? "ยินดีต้อนรับ" : "เข้าสู่ระบบ")
}
}
กฎ: หากวัตถุต้องมี อัตลักษณ์ (สองการอ้างอิง -> หนึ่งวัตถุ) เป็นอินสแตนซ์เดียว หรือทำงานกับ UIKit/Objective-C — เลือก class หากวัตถุเพียงแค่เก็บข้อมูล — เลือก struct
คำถามที่พบบ่อย
Class เป็นชนิดอ้างอิงใน Swift ที่รองรับการสืบทอด การดีอินิเชียลไลเซชัน และ ARC สำหรับการจัดการหน่วยความจำ อินสแตนซ์คลาสถูกเก็บในฮีป และตัวแปรมีการอ้างอิงไปยังวัตถุ ไม่ใช่สำเนาของมัน
Class — ชนิดอ้างอิง (ส่งผ่านด้วยการอ้างอิง) รองรับการสืบทอดและ deinit Struct — ชนิดค่า (ถูกคัดลอก) ไม่รองรับการสืบทอด แต่ใช้โปรโตคอลและได้รับ memberwise init โดยอัตโนมัติ Swift แนะนำ struct เป็นชนิดเริ่มต้น
ARC (การนับการอ้างอิงอัตโนมัติ) — กลไกการจัดการหน่วยความจำสำหรับคลาส Swift แต่ละอินสแตนซ์มีตัวนับการอ้างอิงแบบแข็ง เมื่อตัวนับถึงศูนย์ หน่วยความจำจะถูกปล่อย การอ้างอิง weak และ unowned ป้องกัน retain cycles ระหว่างวัตถุ
Deinit — เมธอดคลาสที่ถูกเรียกโดยอัตโนมัติก่อนปล่อยหน่วยความจำ ใช้สำหรับปิดไฟล์ ยกเลิกการสมัครรับการแจ้งเตือน และการดำเนินการทำความสะอาดอื่นๆ Deinit มีเฉพาะในคลาส — struct ไม่มี
ใช้ class สำหรับส่วนประกอบ UI ของ UIKit/AppKit, ซิงเกิลตัน, ObservableObject ใน SwiftUI, วัตถุที่มีอัตลักษณ์ร่วม (delegate, observer) และเมื่อทำงานกับ Objective-C runtime สำหรับโมเดลข้อมูล DTO และการกำหนดค่า struct เป็นที่นิยม
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ