class ها از مباحث بسیار مهم نه تنها در سوئیفت بلکه در اکثر زبان های برنامه نویس هستند.
از class ها برای ذخیره کردن مجموعه ای از داده ها استفاده میشود، که ازین جهت تا حده زیادی شبیه به structure ها هستند. اما در مجموع تفاوت های مهمی با استراکچر ها دارند که به آن اشاره میکنیم.
برای تعریف یک کلاس از کلمه کلیدی class استفاده میکنیم، و درون آن متغیر ها و تابع هایی که نیاز داریم را مینویسیم.
اما به یک نکته مهم باید دقت کرد، بر خلاف struct ها، در کلاس امکان تعریف یک متغیر بدون مقداردهی اولیه را نداریم. برای حل این مشکل سه راه حل وجود دارد:
? که متغیر ها را بصورت optional قرار میدهد. (که بنده این روش را پیشنهاد نمیکنم.)init هست که مقدار دهی اولیه متغیر ها را موقع تعریف یک نسخه از class انجام میدهد.حالا هر سه تا روشی که بالا گفتیم را در مثال زیر میتوانید ببینید.
// first method:
class Apple {
var product: String?
var price: Int?
}
// second method:
class Apple {
var product: String = "Mac Book air M1"
var price: Int = 999
}
// third method:
class Apple {
var product: String
var price: Int
init(product: String, price: Int) {
self.product = product
self.price = price
}
}
ساخت یک نسخه از کلاس هایی که در بالا تعریف کردیم (به ترتیب) :
// first method:
var device = Apple()
// second method:
var device = Apple()
// third method:
var device = Apple(product: "iPhone", price: 1199)
init مثل یک دیگر هستند، برای این که مشخص کنیم کدام اسم به کدام متغیر اشاره میکند از self استفاده میکنیم. در واقع self به متغیری که درون class تعریف شده اشاره میکند.init با نام متغیر های درون کلاس متفاوت باشد دیگر نیازی به استفاده از self نیست. (اما معمول هست که اسم ها یک جور باشند.)از این به بعد از روش سوم برای ساخت class استفاده میکنیم.
تعریف تابع درون کلاس تا حد زیادی شبیه به استراکچر ها هست، در مثال زیر میتوانید نمونه ای از تعریف تابع درون کلاس را ببینید.
class Point {
var x: Double
var y: Double
init(x: Double, y: Double) {
self.x = x
self.y = y
}
func locate() {
print("location: X = \(x) and Y = \(y) ")
}
}
let location = Point(x: 343.32, y: 1199.21)
print(location.x)
location.locate()
خروجی:
343.32
location: X = 343.32 and Y = 1199.21
در مورد ارث بری در سوئیفت بعدا بطور مفصل صحبت میکنیم، اما از آنجایی که یادگیری آن ها در مبحث کلاس ها بسیار مهم هست، میخواهیم در این جا هم در مورد ارث بری صحبت کنیم.
به طور خلاصه ارث بری یعنی ویژگی های یک کلاس (کلاس پدر یا supper class) را به یک کلاس دیگر (کلاس فرزند یا subclass) بدهیم.
دقت کنید که مقداردهی کردن متغیر های مربوط به supper class یا کلاس پدر را با استفاده از super.init انجام میدهیم، به مثال زیر دقت کنید.
class Apple {
var name: String
var price: Double
init(name: String, price: Double) {
self.name = name
self.price = price
}
}
class IPhone: Apple {
var camera: String
init(camera: String, name: String, price: Double) {
self.camera = camera
super.init(name: name, price: price)
}
}
class MacBook: Apple {
var gpu: String
init(gpu: String, name: String, price: Double) {
self.gpu = gpu
super.init(name: name, price: price)
}
}
super.init مقدار دهی میکنیم.حالا میخواهیم یک نسخه از کلاس MacBook و IPhone بسازیم:
let iPhone = IPhone(camera: "12 MP", name: "iPhone 12 Pro Max", price: 1099)
let macBook = MacBook(gpu: "7 Core", name: "MacBook Air M1", price: 999)
print(iPhone.price)
print(macBook.gpu)
خروجی:
1099
7 Core
گاهی اوقات کلاس فرزند میخواهد رفتار یکی از تابع هایی که در کلاس پدر تعریف شده را تغییر دهد. در این مواقع از کلمه کلیدی override استفاده میکنیم.
در واقع با override میگوییم که این تابع قبلا در کلاس پدر وجود داشته، اما ما میخواهیم نسخه جدیدی از آن را در کلاس فرزند تعریف کنیم.
به مثال زیر دقت کنید:
class Animal {
func makeSound() {
print("Some sound")
}
}
class Dog: Animal {
override func makeSound() {
print("Woof!")
}
}
let dog = Dog()
dog.makeSound()
خروجی:
Woof!
override بازنویسی کردیم.در مثال ارث بری دیدیم که برای مقداردهی متغیر های کلاس پدر از super.init استفاده میکنیم.
اما super فقط برای init نیست. با استفاده از آن میتوانیم به تابع ها و ویژگی های کلاس پدر هم دسترسی داشته باشیم.
فرض کنید یک تابع در کلاس پدر داریم و میخواهیم همان تابع در کلاس فرزند اجرا شود، اما در کنارش یک رفتار جدید هم اضافه کنیم.
class Vehicle {
func start() {
print("Vehicle started")
}
}
class Car: Vehicle {
override func start() {
super.start()
print("Car is ready to move")
}
}
let car = Car()
car.start()
خروجی:
Vehicle started
Car is ready to move
super.start() ابتدا تابع کلاس پدر اجرا میشود.تمام تابع هایی که تا الان نوشتیم مربوط به یک نسخه از کلاس بودند. یعنی باید اول از کلاس یک instance بسازیم، بعد تابع را اجرا کنیم.
اما گاهی لازم داریم تابعی داشته باشیم که به خود کلاس مربوط باشد، نه به یک نسخه از آن. در این مواقع از static استفاده میکنیم.
class MathHelper {
static func square(number: Int) -> Int {
return number * number
}
}
let result = MathHelper.square(number: 5)
print(result)
خروجی:
25
گاهی اوقات میخواهیم وقتی مقدار یک متغیر تغییر کرد، یک عمل خاص انجام شود.
برای این کار میتوانیم از willSet و didSet استفاده کنیم.
willSet قبل از تغییر مقدار اجرا میشود.didSet بعد از تغییر مقدار اجرا میشود.class BankAccount {
var balance: Int = 0 {
willSet {
print("Balance will change to \(newValue)")
}
didSet {
print("Balance changed from \(oldValue) to \(balance)")
}
}
}
var account = BankAccount()
account.balance = 500
خروجی:
Balance will change to 500
Balance changed from 0 to 500
willSet میتوانیم به مقدار جدید با newValue دسترسی داشته باشیم.didSet میتوانیم مقدار قبلی را با oldValue ببینیم.