Swift, karmaşık veri tipleri oluşturmak için Class (sınıf) adında bir başka yola daha sahiptir. Struct'lara (yapı) benzer, ama şunları da içeren bazı önemli farklılıkları vardır:
Bu üçü de büyük farklardır. Ben de devam etmeden önce, bunları derinlemesine ele alacağım.
Eğer bizim Person
struct'ını Person
class'ına döndürseydik, Swift şu şekilde yazmamıza izin vermezdi:
class Person {
var clothes: String
var shoes: String
}
Bunun sebebi, hatırlarsanız daha önce söylediğimiz gibi, mutlaka bir değere sahip olması gereken iki String
tipinde değer tanımladığımız içindir. Struct için bir sorun yok, çünkü Swift bu iki özelliğe değer vermemiz için bizi zorlayarak, üyelik başlatıcıyı otomatik olarak üretiyor. Ama class'larda durum böyle değil, o yüzden Swift verilecek değerden emin olamıyor.
Bunun için üç tane çözüm var: İki değeri de optional String yapmak, veya onlara birer başlangıç değeri atamak, ya da kendi başlatıcımızı yazmak. İlk seçenek, tüm kod geneline gerek olmasa da optional'ları tanıtıyor. İkinci seçenek işe yarar, ama bu başlangıç değerleri gerçekten kullanılmadıkça ziyan edilmiş olurlar. Geriye kalan üçüncü seçenek, kesinlikle doğru olan: Kendi başlatıcımızı yazmak.
Bunu yapmak için, class içerisinde bizi ilgilendiren iki parametreyi alan ve init()
adında bir metod oluşturun:
class Person {
var clothes: String
var shoes: String
init(clothes: String, shoes: String) {
self.clothes = clothes
self.shoes = shoes
}
}
Bu kodda dikkatinizi çekebilecek iki şey var:
İlki, init()
metodundan önce func
yazmayacaksınız, çünkü bu özel bir metoddur. İkincisi ise, parametre adları ile atamak istediğiniz özellik adları aynı olduğu için self.
kullanın. Kolayca kavramanız için şöyle söyleyebiliriz: "Bu nesnenin clothes
özelliği, değerini aktaracağı clothes
parametresiyle aynı olması gerekli". İstediğiniz isimleri verebilirsiniz onlara, tamamen size kalmış.
Önemli: Swift, başlatıcının sonuna kadar optional olmayan tüm özelliklerin bir değere sahip olmasını gerektirir veya o zamana kadar başlatıcı, herhangi bir diğer metodu çağırır; artık hangisi önce gelirse.
Struct'lar ve class'lar arasındaki ikinci önemli fark, sınıf kalıtsallığı olarak da bilinen daha büyük şeyler üretmek için birbiri üzerine kurulabilir. Bu teknik, en basit programlarda bile yoğun bir şekilde Cocoa Touch içinde kullanılır, o yüzden iyi bir şekilde anlamanız gerekli.
Basit birşeyle başlayalım: name (isim) ve age (yaş) özellikleri olan bir Singer
class'ı oluşturalım. Metodlar olarak, özelliklere değer atayabilmek için basit bir başlatıcı olacak, bir de birkaç sözcük yazdıracak olan sing()
metodu:
class Singer {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func sing() {
print("La la la la")
}
}
Başlatıcıyı çağırarak bu nesnenin bir örneğini oluşturalım şimdi. Ardından da özelliklerini yazdıralım ve metodunu çağıralım:
var taylor = Singer(name: "Taylor", age: 25)
taylor.name
taylor.age
taylor.sing()
İşte bizim basit class'ımız, ama onun üzerinde çalışmaya devam edeceğiz: Singer
class'ının yaptığı her şeyi yapan, ama sing()
metodunu çağırdığımda "Trucks, guitars, and liquor" yazmasını istediğim bir CountrySinger
sınıfı tanımlamak istiyorum.
Orjinal Singer
class'ını yeni oluşturduğun CountrySinger
class'ına tabiki de kopyalayabilirdin. Ama bu programlamanın tembel bir şekli. Hem sonrasında Singer
class'ında bir değişiklik yapıp, diğerinde yapmayı unutursan bu değişiklikler geri gelip seni bulur. Bunun yerine, Swift'in daha akıllıca bir çözümü var: Singer
class'ını baz alarak CountrySinger
sınıfını oluşturabiliriz. Böylece oluşturmamız gereken tüm bu özellikler ve metodlar bize hazır gelmiş olur:
class CountrySinger: Singer {
}
Bu iki nokta üst üste sihrin oluştuğu şeydir: "CountrySinger
class'ı Singer
class'ını genişletir" anlamına gelir. Artık yeni CountrySinger
class'ı (subclass –alt sınıf– olarak adlandırılır), Singer
class'ına (parent class veya superclass –ana sınıf veya süper sınıf– olarak adlandırılır) hiçbir şey eklemez. Biz de zaten kendi has sing()
metodu olsun istiyoruz, ama Swift dilinde yeni bir anahtar sözcük daha öğrenmeniz gerekli: override
. Bunun anlamı "Bu metodun parent class'ımda tanımlandığını biliyorum, ama bu subclass için bunu değiştirmek istiyorum"dur.
override
anahtar sözcağüne sahip olmak faydalı bir şey, çünkü ne yapmak istediğinizi açıklığa kavuşturuyor. Aynı zamanda Swift'in kodunuzu kontrol etmesini sağlıyor: Eğer override
kullanmasaydınız, Swift superclass'ınızdan gelen metodu değiştirmenize izin vermeyecekti veya override
kullansaydınız ama geçersiz kılacağınız (override) birşey olmasaydı, Swift hatanızı size işaret edecekti.
O yüzden, aşağıdaki gibi bir override func
kullanmamız gerekiyor:
class CountrySinger: Singer {
override func sing() {
print("Trucks, guitars, and liquor")
}
}
Şimdi taylor
nesnesinin oluşturulma şeklini değiştirin:
var taylor = CountrySinger(name: "Taylor", age: 25)
taylor.sing()
Eğer CountrySinger
nesnesini sadece Singer
nesnesine değiştirirseniz, sonuç panelinde beliren farklı mesajları görebilmeniz gerekiyor.
Şimdi birşeyleri karmaşıklaştıralım ve HeavyMetalSinger
adıyla yeni bir class tanımlayalım. Ama bu kez, heavy metal şarkıcının mikrofonundan geçen çığlığın ne kadar gürültülü olduğunu belirleyen noiseLevel
(gürültü seviyesi) adını verdiğimiz bir özelliği saklayacağız.
Bu bir soruna sebep olur ve çok özel bir şekilde çözülmeyi gerektirir:
Singer
class'ımız noiseLevel
özelliğine sahip değildir.HeavyMetalSinger
için ısmarlama bir başlatıcı oluşturmamız gerekiyor. name
ve age
özelliklerini bilmeye ihtiyacı var. Bunu Singer
superclass'ı aracılığıyla değer geçebilir.noiseLevel
), ardından da kullanılacağı superclass için diğer parametreleri geçersiniz.Bu size korkunç bir şekilde karışık gelebilir, ama kodun içinde doğrudan oluyor. Burada HeavyMetalSinger
class'ını, kendi sing()
metoduyla tamamlayın:
class HeavyMetalSinger: Singer {
var noiseLevel: Int
init(name: String, age: Int, noiseLevel: Int) {
self.noiseLevel = noiseLevel
super.init(name: name, age: age)
}
override func sing() {
print("Grrrrr rargh rargh rarrrrgh!")
}
}
Başlatıcının nasıl üç parametre aldığına ve ardından Singer
superclass'ındaki name
ve age
değişkenlerine değer geçmek için super.init()
çağrısına dikkat edin; ama tabi sadece kendi özellikleri ayarlandıktan sonra. Nesnelerle çalışırken super
ifadesini çoğu kez göreceksiniz. Bu sadece "türetiğim sınıfın metodunu çağır" anlamına geliyor. Genellikle "önce parent class'ımın ihtiyaç duyduğu her şeyi yapmasına izin ver, sonrasında ben fazladan birşeyler yapacağım" anlamında kullanılır.
Sınıf kalıtsallığı büyük bir konudur, o yüzden henüz tam olarak anlamadıysanız üzülmeyin. Zaten, bilmeniz gereken bir şey daha var: Sınıf kalıtsallığı sıklıkla birçok aşamaya yayılır. Örneğin A, B'den türemiş olabilir ve B ise C'den türemiş olabilir, hatta C de D'den türemiş olabilir. Bu böyle gider. Bu, yazdığınız kodun modüler ve anlamasının kolay olmasına yardımcı olan class'ları fonksiyonel bir şekilde kurmanıza ve yeniden kullanmanıza izin verir.
Eğer Swift class metodlarınızı çağıran Apple'ın işletim sisteminin bazı parçalarına sahip olmak istiyorsanız, özel bir nitelik ile onu işaretlemeniz gerekir: @objc
. Bu, “Objective-C”nin kısaltmasıdır ve çoğunlukla tüm iOS, macOS, watchOS ve tvOs için olan daha eski Objective-C kodunu çalıştıran metodu etkin bir şekilde işaretleyen niteliktir. Örneğin, bir saniye geçtikten sonra metodunuzun çağrılmasını sisteme söylemek için, onu @objc
ile işaretlemeniz gerekli.
Şimdilik @objc
konusuna çok takılmayın; sadece devam eden konular içinde onu açıklamaya çalışmayacağım, aynı zamanda Xcode da her zaman bunu sana gerektiğinde söyleyecek. Alternatif olarak, eğer bireysel metodlar için @objc
kullanmak istemezseniz, otomatik olarak Objective-C için class'ınız tüm metodlarını uygun hale getirmeden önce @objcMembers
koyabilirsiniz.
Bir struct'ı kopyaladığınızda, tüm değerleriyle birlikte her şey çoğaltılır. Bu, bir struct'ın bir kopyasını değiştirerek, diğer kopyaların değiştiremeyeceğiniz anlamına gelir; hepsi bireyseldir. Class'larla, bir nesnenin her bir kopyası aynı orjinal nesneyi işaret eder. Yani birini değiştirirseniz, hepsini değiştirmiş olursunuz. Swift struct'ları "değer tipleri" olarak tanımlar, çünkü onlar sadece bir değeri işaret eder. Ama class'lar "referans tipleri"ndendir, çünkü nesneler gerçek değerlerin paylaşımlı referanslarıdır.
Bu önemli bir farktır ve struct'lar ile class'lar arasında seçimin önemli olduğu anlamına gelir:
Struct'lar ve Class'lar arasındaki anahtar farkı özetlemem gerekseydi, şunu söylerdim: Class'lar daha esnek, ama struct'lar daha güvenli. Basit bir kural olarak, belirli bir neden dolayı ihtiyaç duyana kadar, her zaman struct'ları kullanmalısınız.
SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
Link copied to your pasteboard.