Swift Any & AnyObject Types

Bu yazımda şu konuları anlatmaya çalışacağım:

  • Any nedir? Hangi amaçla kullanılmaktadır?
  • Any & AnyObject Kullanım Alanları
  • Swift compiler arka planda neler yapıyor?
  • Any türünden bir değişkenin doğru kullanımı
  • Any bir referans mıdır? Compiler burada nasıl davranır?
  • Any türden diziler tanımlayabilir miyiz?
  • Any ile AnyObject arasında ki fark nedir?

Swift dili geliştirilirken birçok programlama dilinden yararlanılmış. Diğer dillerin dizaynından, sentaks yapısından, iyi yönlerinden faydanılarak oluşturulmuş.

Swift programlama dilinin multi paradigm olduğunu söylemiştik. Yani prosedural, object oriented, functional programlama teknikleri ile uygulama geliştirebiliyoruz.

Java, C# programlama dillerinde herşey en üst sınıf Object’ten oluşmaktadır. C++ dilinde ise böyle bir teknik yoktur. Swift dili burada C++ diline benzemektedir.


Any nedir? Hangi amaçla kullanılmaktadır?

  • Any öncelikle bir türdür.
  • Herhangi türden bir nesneye Any isimli bir türe atayabiliriz.
  • Any türünden nesne yaratamayız. Burada ki çözüm yolu referans bildirmektir çünkü her türden nesne any türünden referansa atanabilir. Hemen örneğe bakalım:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
var ival: Int = 10
var dval: Double = 9.9
var sval: String = "Kerem"

struct ProgLang{
    // KODLAR
}

class FavLang{
    // KODLAR
}


var anyTypes: Any

anyTypes = ival
anyTypes = dval
anyTypes = sval
anyTypes = ProgLang()
anyTypes = FavLang()

Kodu derlediğimiz zaman hiçbir hata almadığınızı göreceksiniz. Burada dikkatinizi çekmek istediğim bir nokta var. anyTypes isimli Any türüne referans haricinde(class), struct değişkenleride atayabiliyoruz.


Swift compiler bu işlemi nasıl yapıyor?

  • Any türüne bir yapı veya enum atandığı zaman “boxing conversion” yoluyla HEAP alanında bir kopyasını çıkarır.
  • Any referansı artık HEAP alanında ki o kopya değeri gösterir.

Senaryo: Parametresi Any türünden olan bir fonksiyon yazalım. Bu fonksiyonu herhangi türden bir değer ile çağıralım.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
var ival: Int = 10
var dval: Double = 9.9
var sval: String = "Kerem"

struct Lispic {
    // ...
}

class LearnSwift {
    // ...
}

func anyType(herhangiBirTip x: Any) {
    print(type(of: x))
}

anyType(herhangiBirTip: ival)   // Int
anyType(herhangiBirTip: dval)   // Double
anyType(herhangiBirTip: sval)   // String
anyType(herhangiBirTip: Lispic())   // Lispic
anyType(herhangiBirTip: LearnSwift())   // LearnSwift

Sonuç: Fonksiyonlar Any türünden olabilir ve herhangi türden bir değişken ile çağırabiliriz.


Any türünden bir değişkenin kullanımı

  • Tür dönüşümü yapmadan kullanmamalıyız.
  • Tür dönüşümünü as!(Forced downcasting) yada as?(Conditional downcasting) operatorlerini kullanarak yapabiliriz.

Senaryo: Bir User sınıfı tanımlayalım. Sınıf isim, yaş ve favori programlama dili özelliklerinden oluşsun. Sınıfın display diye bir fonksiyonu olsun. Biz Any türden bir değişkene oluşturduğumuz sınıfı atıp, display fonksiyonuna erişmeye çalışalım.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class User {
    var name: String
    var age: Int
    var favProgLang: String
    
    init(name: String, age: Int, favProgLang: String){
        self.name = name
        self.age = age
        self.favProgLang = favProgLang
    }
    
    func display(){
        print("Isim -> \(self.name)\nYas -> \(self.age)\nFavori Programlama Dili -> \(self.favProgLang)")
    }
}


var anyType: Any
anyType = User(name: "Kerem", age: 28, favProgLang: "Lisp")
anyType.display() // Value of type 'Any' has no member 'display'

var anyType: Any
anyType = User(name: "Kerem", age: 28, favProgLang: "Lisp")
var res = anyType as! User
res.display()

Sonuç: Hatadan anladığımız gibi Any türünden bir değişkeni direk olarak işleme sokamıyoruz. Tür dönüşümü yapmamız gerekiyor.


Any bir referans mıdır? Compiler burada nasıl davranır?

  • Any bir referanstır.
  • Swift dilinde bir referans HEAP alanında ki bir nesneyi gösterir.
  • Referans ise stack alanında yer alır. Yani bir yapıyı any türünden bir refaransa atadığımızda boxing conversion gerçekleşir. Yukarıda da dediğimiz gibi swift compiler HEAP’te yapı değişkeninin bir kopyasını oluşturur.
  • Any referansı Stack alanındadır ve HEAP alanında ki nesneyi gösterir hale gelir.

Bu söylediğimizi ispatlayan ufak bir kod yazalım.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func printAddress<T>(of pointer: UnsafePointer<T>) {
    print(pointer)
}

var ival: Int = 3
printAddress(of: &ival) // 0x00000001003edec0
var anyType: Any

anyType = ival
printAddress(of: &anyType) // 0x00000001003edec8
// anyType ival nesnesini gostermiyor, ival nesnesinin kopyasini gosteriyor
// Hemen ival nesnesinde degisiklik yapiyorum
ival = 999

// Eger anyType ival nesnesini gosteriyorsa degeri 999 olmasi gerekiyor
print(anyType as! Int) // 3 degerini gosteriyor

Sonuç: Bellek adreslerinde de anlaşıldığı gibi aynı nesneyi değil, kopyasını göstermektedir.


Any türden diziler tanımlayabilir miyiz?

  • Any türünden diziler tanımlayabiliriz.
  • Dizileri dolaşırken dikkat etmemiz gereken nokta is operatörü ile dizi elemanlarının türlerini belirlememiz gerekiyor.
  • İstenilirse Any? türünden de referanslar tanımlayabiliriz. Bu sayede referanslara nil atayabiliriz.

Any ile AnyObject arasında ki fark nedir?

Any &amp; AnyObject
Any & AnyObject

Yukarıda ki grafikte herşey çok açık duruyor. AnyObject türüne referans türlerine ilişkin nesneler atayabiliyoruz. Any türüne ise her türden nesne atayabiliyoruz.


Kaynaklar:

How to share structs using boxing?

Type casting in swift : difference between is, as, as?, as!

Swift Type Casting – as, is, Any, AnyObject

Mastering Swift: Tips About Array and Dictionary Literals

What Is Any in Swift

Any Object