Swift 4 Sınıflar #19

Özcan Akkoca
Etiya
Published in
13 min readJun 29, 2017

--

Swift’te sınıf bildiriminin genel biçimi şöyledir:

class <sınıf ismi> {

//…

}

Swift’te sınıflar yapılara oldukça benzemektedir. Benzerlikler şöyle özetlenebilir:

- Sınıflar da property elemanlara ve metotlara sahiptir.

- Sınıfların da başlangıç metotları (init metotları) vardır.

- Sınıflarda da elemana erişim yine nokta operatörüyle yapılmaktadır.

- Sınıflar da static ve static olmayan elemanlara sahip olabilirler.

- Sınıflar da subscript elemanlara sahip olabilir.

- Sınıflar da yapılar gibi protokolleri destekleyebilirler.

Ancak Swift’te sınıflar yapılardan daha fazla özelliklere sahiptir ve sınıfların daha geniş bir kullanım alanı vardır.

Sınıflar kategori olarak referans türlerine ilişkindir. Yani bir sınıf türünden bir değişken (referans) bildirdiğimizde o değişken sınıf nesnesinin adresini tutar.

Örneğin:

class Sample {

var a: Int

var b: Int

init(a: Int, b: Int)

{

self.a = a

self.b = b

}

}

var s, t: Sample

s = Sample(a: 10, b: 20)

t = s

print(“s.a = \(s.a), s.b = \(s.b)”) // s.a = 10, s.b = 20

print(“t.a = \(t.a), t.b = \(t.b)”) // t.a = 10, t.b = 20

t.a = 30

t.b = 40

print(“s.a = \(s.a), s.b = \(s.b)”) // s.a = 30, s.b = 40

print(“t.a = \(t.a), t.b = \(t.b)”) // t.a = 30, t.b = 40

Aynı türden iki sınıf referansı birbirlerine atandığında onların içerisindeki adreslerin atandığına dikkat ediniz. Dolayısıyla iki referans da aynı nesneyi gösterir duruma gelmektedir. Yukarıdaki işlem yapıyla gerçekleştirilseydi şöyle bir sonuç oluşurdu:

struct Sample {

var a: Int

var b: Int

init(a: Int, b: Int)

{

self.a = a

self.b = b

}

}

var s, t: Sample

s = Sample(a: 10, b: 20)

t = s

print(“s.a = \(s.a), s.b = \(s.b)”) // s.a = 10, s.b = 20

print(“t.a = \(t.a), t.b = \(t.b)”) // t.a = 10, t.b = 20

t.a = 30

t.b = 40

print(“s.a = \(s.a), s.b = \(s.b)”) // s.a = 10, s.b = 20

print(“t.a = \(t.a), t.b = \(t.b)”) // t.a = 30, t.b = 40

Sınıflarda init metotları konusunda küçük bir farklılık vardır. Anımsanacağı gibi yapılarda init metodunun derleyici tarafından yazılma kuralı şöyleydi:

1) Programcı yapı için herhangi bir init metodu yazmışsa derleyici programcı için hiçbir init metodu yazmaz.

2) Programcı yapı için hiçbir init metodu yazmamışsa derleyici eleman ilkdeğerlemesi yapan init metodunu kendisi yazar. Ancak parametresiz init metodunu eğer yapının tüm elemanlarına bildirim sırasında ilkdeğer verilmişse yazmaktadır.

Halbuki sınıflar için derleyicinin init metodunu derleyicinin kendisinin yazma kuralı şöyledir:

1) Programcı sınıf için herhangi bir init metodu yazmışsa derleyici programcı için hiçbir init metodu yazmaz.

2) Programcı parametresiz init metodunu eğer sınıfın tüm elemanlarına bildirim sırasında ilkdeğer verilmişse yazmaktadır.

Aradaki farka dikkat ediniz: Sınıflarda derleyici hiçbir biçimde eleman ilkdeğerlemesi yapan init metodunu yazmamaktadır.

Sınıfın property elemanları let ile bildirilmişse tıpkı var ile bildirildiğinde olduğu gibi ona ya bildirim sırasında ya da init metotları içerisinde ilkdeğer verme zorunluluğu vardır (yapılarda da böyleydi). Bunun dışında let property’lere başka yerde bir daha değer atanamaz. Ayrıca eğer let property’lerine bildirim sırasında ilkdeğer verilmişse artık bunlara init metodu içerisinde de değer atanamamaktadır. Burada bir noktaya dikkat etmek çekmek gerekir. Sınıfın başka sınıf türünden property elemanı varsa property elemanının kendisi let duru mdadır, onun gösterdiği yerdeki nesne let durumda değildir.

Örneğin:

class A {

var x: Int

init()

{

x = 10

}

}

class B {

let a: A = A()

}

var b: B = B()

b.a.x = 10 // geçerli

b.a = A() // error

Burada B nesnesinin a isimli property elemanı let durumdadır. Yani biz a’nın kendisine artık başka bir değer atayamayız. a’nın gösterdiği yerdeki nesnenin elemanlarına atama yapabiliriz. A’nın bir yapı olması durumunda a property’sinin tamamı let durumdadır. Dolayısıyla biz onun x parçasına da değer atayamayız.

Örneğin:

struct A {

var x: Int

init()

{

x = 10

}

}

class B {

let a: A = A()

}

var b: B = B()

b.a.x = 10 // error

Türetme İşlemleri

Anımsanacağı gibi Swift’te değer türleri (yani struct, enum türleri) türetmeye kapalıdır. Yani bir yapı ya da enum türünden biz bir sınıf, yapı ya da enum türü, bir sınıf türünden de bir yapı ya da enum türü türetemeyiz. Ancak sınıflardan sınıflar türetilebilir.

Swift’te türetme işleminin genel biçimi şöyledir:

class <türemiş sınıf ismi> : <taban sınıf ismi> {

//…

}

Swift’in türetme sentaksının C++ ve C#’takine benzediğine dikkat ediniz.

Diğer dillerde de olduğu gibi türetme durumunda taban sınıfın elemanları sanki türemiş sınıfın elemanlarıymış gibi işlem görmektedir. Yani biz türemiş sınıf türünden bir referansla hem türemiş sınıfın hem de taban sınıfın elemanlarını kullanabiliriz.

Örneğin:

class A {

var x: Int = 0

func foo()

{

print(“A.foo”)

}

}

class B : A {

var y: Int = 0

func bar()

{

print(“B.bar”)

}

}

var b = B();

b.foo() // geçerli

b.bar() // geçerli

print(b.x) // geçerli

print(b.y) // geçerli

Yine Swift’te de türemiş sınıf türünden bir nesne hem taban sınıf property’lerini i(yani veri alamanlarını) hem de türemiş sınıfın kendi property’lerini (yani veri elemanlarını) içerir. sınıfların ve yapıların metotları nesne içerisinde değildir. Programın kod alanı içerisindedir. Örneğin:

Yine Swift’te de türemiş sınıf nesnesinin taban sınıf kısmı ile türemiş sınıf kısmı ardışıl bir blok oluşturur. Nesnenin taban sınıf kısmı bellekte düşük adreste bulunmaktadır.

Programların Kod, Stack, Data ve Heap Alanları

Her uygulamanın bellek alanı kabaca dört kısma ayrılmaktadır: Kod, stack, data ve heap. Bir programdaki bütün global fonksiyonlar ve metotlar (yani yapıların ve sınıfların fonksiyonları) biraraya getirilerek kod bölümünde toplanmaktadır. Yani yapıların ya da sınıfların metotları nesnenin içerisinde değildir. Onlar uygulamanın adres alanının kod bölümündedir. Metotların sınıflarla ilişkisi mantıksal düzeydedir.

Fonksiyonların ve metotların parametre değişkenleri ve onların blokları içerisinde bildirilmiş olan yerel değişkenler stack’te yaratılmaktadır. Stack doldur-boşalt tarzı bir alandır. Bir değişken programın akışı fonksiyon ya da metotta o değişkenin bildirildiği yere geldiğinde stack’te yaratılır. Fonksiyon ya da metot bittiğinde stack’ten yok edilir. Örneğin:

Görüldüğü gibi bir fonksiyon ya da metot çağrılmadığı sürece onların içerisindeki yerel değişkenler zaten bellekte yer kaplıyor durumda değildir. Stack’te değişkenlerin yaratılması ve yok edilmesi çok hızlı bir biçimde yapılmaktadır.

Swift’te fonksiyonların ve sınıfların dışında bildirilen değişkenler uygulamanın data bölümünde yaratılırlar. Data bölümü programın yaşamı boyunca (yani run time boyunca) bellekte kalmaktadır.

Sınıf nesneleri heap’te yaratılır. Sınıf nesnelkeri ile onların adreslerinin tutulduğu değişkenleri (referansları) birbirine karıştırmayınız. Örneğin:

Görüldüğü gibi aşağıdaki gibi bir işlemde a (eğer fonksiyonun içerisindeyse) stack’te yratılır. Ancak A nesnesi heap’te yaratılmaktadır:

var a = A()

Heap’te yaratılan sınıf nesneleri onu hiçbir referans göstermediği zaman sistem tarafından otomatik silinmektedir. Heap’teki nesnelerin yok edilmesi stack’e göre çok dsaha yavaş yapılmaktadır. Belli bir anda bir sınıf nesnesini n tane referans gösteriyor durumdadır. Buna nesnenin referans sayacı denir. Nesnenin referans sayacı sıfıra düştüğünde artık nesne çöp durumuna gelir. Swift’teki ARC (ya da C# ve Java’daki garbage collector) nesneyi heap’ten siler.

Türetme Durumunda init Metotlarının Çağrılması

Bilindiği gibi C++’ C# ve Java gibi dillerde türemiş sınıf türünden bir nesne yaratıldığında türemiş sınıfın başlangıç metotları (constructors) çağrılmaktadır. Bu dillerde türemiş sınıfın başlangıç metotları taban sınıfın başlangıç metotlarını çağırmaktadır. Böylece nesnenin taban sınıf kısmına taban sınıfın başlangıç metotları ilkdeğerlerini atamaktadır. Halbuki Swift’te diğer nesne yönelimli dillerin çoğunda olduğu gibi türemiş sınıf başlangıç metodu (yani init metodu) otomatik olarak taban sınıfın başlangıç metodunu (yani init metodunu) çağırmaz. Örneğin:

class A {

var x: Int

init(x: Int)

{

self.x = x

}

}

class B : A {

var y: Int = 20

init(y: Int) // error taban sınıfın x property’si değer almamış

{

self.y = y

}

}

var b = B(y: 100);

Swift’te türemiş sınıfın taban sınıf kısmındaki property’lere ilkdeğerlerinin verilmesi için başvurulan normal yol türemiş sınıfın taban sınıfın init metodunu super.init(…) ifadesi ile çağırmasıdır. Bu bakımdan Swift’te türemiş sınıflar için iki farklı init metodu tanımlanmıştır: “designated init metodu (designated initializer)” ve “convenience init metodu (convenience initializer)”. Eğer türemiş sınıfın init metodunda convenience anahtar sözcüğü belirtilmemişse bu init metodu “designated” init metodu, convenience anahtar sözcüğü belirtilmişse bu init metodu da “convenience” init metodudur. Örneğin:

class A {

//…

}

class B : A {

init(a: Int) // designated init metodu

{

//…

}

convenience init() // convenience init metodu

{

//…

}

//…

}

Swift’te sınıfın bir init metodu kendi sınıfının init metodunu self anahtar sözcüğüyle, taban sınıfın init metodunu ise super anahtar sözcüğüyle çağırmak zorundadır.

Türemiş sınıf ve taban sınıf init metotlarının yazımı sırasında şu kurallara uyulmak zorundadır:

1) Türemiş sınıfın “designated init” metodu taban sınıfın “designated init” metotlarından birini çağırmak zorundadır. Türemiş sınıfın “designated init” metotları kendi sınıflarının init metotlarını çağıramaz.

2) Türemiş sınıfın “convenience init” metodu aynı sınıfın başka bir init metodunu çağırmak zorundadır. Taban sınıfın herhangi bir init metodunu çağıramaz.

3) Türemiş sınıfın “convenience init” metodu sınıfın başka bir “convenience init” metodunu çağırıyor olabilir. Ancak bu zincirin en sonunda türemiş sınıfın “convenience init” metodunun türemiş sınıfın “designated init” metodunu çağırıyor olması gerekir.

Örneğin:

class A {

var x: Int

init(x: Int)

{

self.x = x

}

}

class B : A {

var y: Int

init(x: Int, y: Int)

{

self.y = y

super.init(x: x)

}

convenience init()

{

self.init(x: 10, y: 20)

}

}

class C : B {

var z: Int

init(x: Int, y: Int, z: Int)

{

self.z = z

super.init(x: x, y: y)

}

}

let c = C(x:10, y: 20, z: 30)

4) Türemiş sınıfın “designated init metodu” ancak kendi sınıfının tüm property elemanlarına değer atandıktan sonra taban sınıfın “designated init” metodunu çağırabilir. Yani taban sınıfın init metodu çağrılmadan önce türemiş sınıfın tüm property elemanlarına değer atanmış olmak zorundadır. Örneğin:

class A {

var x: Int

init(x: Int)

{

self.x = x

}

}

class B : A {

var y: Int

init(x: Int, y: Int)

{

super.init(x: x) // error! henüz y’ye değer atanmamış

self.y = y

}

}

Burada B sınıfının y elemanına henüz değer atanmadan A sınıfının init metodu çağrılmıştır. Bu durum error oluşturur. Bu kural C++, Java ve C# gibi dillerdeki olağan akışla çelişkili gibi görünebilir. Gerçekten de o dillerde her zaman önce nesnenin taban sınıf kısmı değerlenmektedir. Swift’te init metotları da override edildiği için böyle bir kuarla gereksinim duyulmuştur.

5) Türemiş sınıfın “designated init” metodu taban sınıfın designated init metodunu çağırmadan önce taban sınıfın property elemanlarını ya da metotlarını kullanamaz. Kullanırsa error oluşur. Örneğin:

class A {

var x: Int

init(x: Int)

{

self.x = x

}

func foo()

{

//…

}

}

class B : A {

var y: Int

init(x: Int, y: Int)

{

self.y = y

super.x = 50 // error! taban sınıfın init metodundan önce property’si kullanılmış

super.init(x: x)

}

}

6) Türemiş sınıfın “convenience init” metodu kendi sınıfının başka bir init metodunu çağırmadan önce kendi sınıfının ya da taban sınıfın bir property elemanını ya da metodunu kullanamaz. Örneğin:

class A {

var x: Int

init(x: Int)

{

self.x = x

}

}

class B : A {

var y: Int

init(x: Int, y: Int)

{

self.y = y

super.init(x: x)

}

convenience init()

{

self.y = 50 // error!

self.init(y: 10)

}

convenience init(y: Int)

{

self.init(x: 100, y: y)

}

}

7) Türemiş sınıf için hiçbir init metodu yazılmamışsa taban sınıfın init metotları sanki türemiş sınıfın init metotlarıymış gibi kullanılır. Örneğin:

class A {

var x: Int

init(x: Int)

{

self.x = x

}

}

class B : A {

var y: Int = 20

}

var b: B = B(x: 10) // geçerli

8) Eğer türemiş sınıf taban sınıfın tüm designated init metotlarını override etmişse taban sınıfın tüm convenience init metotları sanki türemiş sınıfın convenience init metotlarıymış gibi kullanılabilir. Örneğin:

class A {

var x: Int

init(x: Int)

{

self.x = x

}

convenience init()

{

self.init(x: 10)

}

}

class B : A {

var y: Int = 20

override init(x: Int)

{

super.init(x: 10)

}

}

var b: B = B() // geçerli

Swift’te init metotları da override edilebilir. Bu durum override işleminin ele alındığı kısımda açıklanmaktadır.

Pekiyi convenience ve designated init metotlarının ayrı ayrı olmasının anlamı nedir? Aslında şu durum sağlanmaya çalışılmıştır: Sınıfın bir init metodu diğer bir init metodunu çağırabilsin, fakat taban sınıfın init metodu toplamda yalnızca bir kez çağrılsın. İşte bunun derleme zamanında garanti alınması için bunlar uydurulmuştur. Aslında örneğin aynı sorun C#’ta benzer biçimde çözülmüştür. Şöyle ki: C#’ta başlangıç metotlarının kapanış parantezinden sonra ya this(…) ifadesi ya da base(…) ifadesi getirilir. Ancak iki ifade bir arada kullanılamaz. İşte aslında C#’ın bu anlamdaki this(…) sentaksı Swift’teki “convenience init” metoduna, base(…) sentaksı da “designated init” metoduna karşılık gelmektedir. Tabii burada belli bir “Objective-C” uyumu da aynı zamanda korunmaya çalışılmıştır.

Başarısız Olabilen init Metotları (Failable Initializers)

Bilindiği gibi nesne yönelimli pek çok dilde başlangıç metotlarının geri dönüş değerleri yoktur. Bu nedenle başlangıç metotlarında başarısızlıkla karşılaşıldığında genel olarak exception fırlatılmaktadır. Oysa Swift’te başarısız olabilen (failable) init metotları da vardır. Böyle init metotları init? ismiyle bildirilir.

class Sample {

var msg: String = “”

init?(msg: String)

{

if msg.isEmpty {

return nil

}

self.msg = msg

}

}

var s : Sample? = Sample(msg: “”)

if let ss = s {

print(ss)

}

else {

print(“nil”)

}

Görüldüğü gibi başarısız olabilen init metotları ilgili sınıf T türündense aslında bize T? biçiminde seçenksel değer vermektedir. Örneğimizde init metoduyla yaratılan nesne referansının Sample? türüne atandığına dikkat ediniz.

Başarısız olabilen convenience ya da designated init metotları kendi sınıflarının ya da taban sınıflarının normal init metotlarını (non-failable init methods) çağırabilir. Ancak normal init metotları kendi sınıflarının ya da taban sınıflarının başarısız olabilen init metotlarını çağıramaz.

Türemiş Sınıftan Taban Sınıfa Referans Dönüştürmeleri

Nense yönelimli dillerin hepsinde türemiş sınıf türünden referans (ya da adres) taban sınıf türünden referansa doğrudan atanabilmektedir. Bunun nedeni türemiş sınıf nesnesinin tabanm sınıf elemanlarını da içeriyor olmasındandı. Örneğin:

Türemiş sınıf referansını taban sınıf referansına atadıktan sonra artı bu taban sınıf referansı bağımsız bir taban sınıf nesnesini gösteriyor durumda değildir. Türemiş sınıf nesnesinin taban sınıf kısmını gösteriyor durumdadır. Örneğin:

class A {

var x: Int

init(_ x: Int)

{

self.x = x

}

}

class B : A {

var y: Int

init(_ x: Int, _ y: Int)

{

self.y = y

super.init(x)

}

}

var b: B = B(10, 20)

var a: A

a = b

print(a.x) // 10

a.x = 100

print(b.x) // 100

Türemis sınıf referansının taban sınıf referansına atanabilmesinin önemli sonuçları vardır. Bu sayede bir türetme şeması üzerinde genel işlemler yapan fonksiyonlar ya da metotlar yazılabilir. Örneğin:

Biz burada displayInfo fonksiyonunu Employee türünden argümanla çağırmak zorunda değiliz. Worker, Manaher, Executive, SalesPerson, … türünden argümanlarla da çağırabiliriz. Böylece displayInfo genel işlem yapan bir fonksiyon durumuna gelmiştir. Tüm çalışan sınıflarının bir Employee kısmının olduğuna dikkat ediniz. Tabii burada biz displayInfo fonksiyonunu örneğin Executive argümanı ile çağırsak displayInfo Executive’in ancak Employee bilgilerini yazdırabilir. Taban sınıf referansıyla türemiş sınıfın elemanlarına erişmek (orada onlar olsa bile) mümkün değildir.

Türemiş sınıf referansının taban sınıf referansına atanabilmesi çokbiçimlilik (polymorphism) için de gerekmektedir.

Yukarıdaki işlemin tersi yani taban sınıf referansının türemiş sınıf referansına atanması geçerli değildir. Eğer böyle bir atama yapılabilseydi bu durumda olmayan veri elemanlarına erişme gibi bir durum oluşurdu. Örneğin:

Referansların Statik ve Dinamik Türleri

Referansların statik ve dinamik türleri vardır. (Ancak değer türlerinin bu biçimde iki türü yoktur) Bir referansın statik türü bildirimde belirtilen türüdür. Dinamik türü ise referans bir nesneyi gösteriyorsa gösterdiği nesnenin bütünün türüdür. Örneğin:

var x: A

var y: B = B()

x = y

Burada x referansının static türü A’dır, dinamik türü B’dir. Çünkü x’in gösterdiği yerde bütünü B olan bir nesne vardır. x onun A parçasını göstermektedir.

Burada y referansının statik türü de dibnamik türü de B’dir. Örneğin:

Burada x’in statik türü A’dır. Fakat dinamik türü C’dir. Çünkü x’in gösterdiği yerdeki nesnenin bütünün türü (yani en geniş halinin türü) C’dir. Benzer biçimde y’nin statik türü B’dir fakat dinamik türü C’dir. z’in ise hem statik hem de dinamik türü C’dir.

Referansın statik türü hiç değişmez. Fakat dinamik türü onun nereyi gösterdiğine bağlı olarak değişebilir. Örneğin:

Burada ok ile gösterilen üç yerde de x’in statik türü A’dır. Ancak dinamik türü sırasıyla A, B ve C olacak biçimde değişmektedir. Örneğin:

Burada foo’nun her çağrımında parametre değişkeni olan a’nın dinamik türü değişmektedir.

Taban Sınıftan Türemiş Sınıfa Referans Dönüştürmeleri (Downcating)

Bazen taban sınıftan türemiş sınıfa dönüştürme de yapılmak istenebilir. Örneğin bir fonksiyon ya da metot bazı gerekçelerden dolayı türmeiş sınıf nesnesinin adresini bize taban sınıf referansıymış gibi veriyor olabilir. Bizim orijinal nesneyi tam olarak kullanabilmemiz için onu türemiş sınıfa dönüştürmemiz gerekir.

Taban sınıftan türemiş sınıfa yapılan dönüştürmeler haklı ya da haksız olabilir. Eğer dönüştürülmek istenen referansın dinamik türü dönüştürülmek istenen sınıfı kapsıyorsa dönüştürme haklıdır, kapsamıyorsa haksızdır. Örneğin:

aşağıdan yukarıya doğru dönüştürmelerin (upcasts) haksız olma olasılığı yoktur. O nedenle biz türemiş sınıf referansını doğrudan taban sınıf referansına atayabiliriz.

Swift’te aşağıya doğru dönüştürmeler as! ve as? operatörleriyle yapılmaktadır. Bu iki operatör de iki operandlı araek operatörlerdir. Bu operatörlerle aşağıya doğru dönüştürme yapıldığında her zaman derleme aşamasından başarıyla geçilir. Ancak as! operatörü programın çalışma zamanı sırasında da kontrol uygulamaktadır. as! ve as? operatörlerinin sol tarafındaki operand bir sınıf türünden referans belirtmek zorundadır. Bunların sağ tarafındaki operand bir sınıf ismi olmak zorundadır. Örneğin:

class A {

var x: Int

init(_ x: Int)

{

self.x = x

}

}

class B : A {

var y: Int

init(_ x: Int, _ y: Int)

{

self.y = y

super.init(x)

}

}

var x: A

var y: B = B(10, 2)

var z: B

// Burada x referansının dinamik türü B’dir

x = y // geçerli (upcast)

z = x // error: downcat as! ya da as? operatörü ile yapılmalı

z = x as! B // geçerli

as! operatörü eğer dönüştürme haksızsa dönüştürmenin yapıldığı yerde exception oluşturur. Yani program çöker.

as? operatörü as! operatöyle aynı biçimde kullanılır. Ancak bu operatör dönüştürme haksızsa exception fırlatmaz nil referans üretir. Swift’te biz Java ve C#’ta olduğu gibi referanslara nil (onlarda nul) yerleştiremeyiz. Referanslara dahi Swift’te nil değerin yerleştirilebilmesi için o türün seçeneksel (optional) olması gerekir. Dolayısıyla as? operatörü de sağ tarafındaki tür (yani dönüştürülmek istenen tür) T ise bize T? türünden bir değer üretmektedir. Örneğin:

Örneğin:

import Foundation

class A {

var x: Int

init(_ x: Int)

{

self.x = x

}

}

class B : A {

var y: Int

init(_ x: Int, _ y: Int)

{

self.y = y

super.init(x)

}

}

class C : B {

var z: Int

init(_ x: Int, _ y: Int, _ z: Int)

{

self.z = z

super.init(x, y)

}

}

var x: A = B(10, 20)

var y: B

var z: C?

z = x as? C

if z != nil {

print(“\(z!.x), \(z!.y), \(z!.z)”)

}

else {

print(“haksız dönüştürme!”)

}

Swift’te (tıpkı Java ve C#’ta olduğu gibi) aralarında türetme ilişkisi olmayan iki sınıf arasında as! ya da as! operatörü ile dönüştürme yapılamaz. Örneğin:

Burada biz B’den C’ye dönüştürme yapamayız. Yapmaya çalışırsak derleme zamanında error oluşur. Çünkü aslında hiçbir zaman örneğin B türünden bir referans C’yi gösterir duruma gelmemektedir. Tabii eğer böyle birşey B’den A’ya sonra A’dan C’ye dönüştürme yoluyla yapılamaya çalışılabilir. Ancak bu dönüştürme hiçbir zaman zaten haklı olamayacaktır.

is Operatörü

is operatörü iki operandlı araek bir operatördür. Bu opeeratörün sol tarafındaki operand bir referans, sağ tarafındaki opereand bir sınıf ismi olmak zorundadır. Operatör sol taraftaki referansın dinamik türünün sağ taraftaki türü içerip içermediğine bakar. Eğer sol taraftaki referansın dinamik türü sağ taraftaki türü içeriyor sa true değeri, içermiyorsa false değeri üretir. Bu operatörün ürettiği değer Bool türündendir. Örneğin:

import Foundation

class A {

var x: Int

init(_ x: Int)

{

self.x = x

}

}

class B : A {

var y: Int

init(_ x: Int, _ y: Int)

{

self.y = y

super.init(x)

}

}

var x: A = B(10, 20)

if x is B {

var y = x as! B

print(“\(y.x), \(y.y)”)

}

else {

print(“x’in dinamik türü B’yi içermiyor”)

}

--

--