Swift @autoclosure Attribute

Swift AutoClosure

Bir fonksiyonun şayet “() -> T” türünde bir parametresi varsa, @autoclosure attribute (özniteliği) ile küme parantezi kullanılmadan çağırılabilir. Örneğin:

// Explicit Closure

func Test(parametre p: () -> Int)

{

print(p())

}

Test(parametre: {3 + 5}) // 8

Yerine aşağıdaki gibi kullanılabilir:

func Test(parametre p: @autoclosure () -> Int)

{

print(p())

}

Test(parametre: 3 + 5) // 8

Bu kod parçasında argüman otomatik olarak bir closure’a dönüşmektedir.

Reklamlar

Swift4 type() fonksiyonu ile dinamik türün öğrenilmesi

Yakında çıkacak olan #Swift kitabımdan küçük alıntılara devam:

Swift’te bir nesnenin dinamik tür ismini öğrenmek için geçmiş versiyonlarda dynamicType kullanılmaktaydı ancak yeni versiyonda bu işlem için artık type() isimli fonksiyon kullanılmaktadır.

Bu fonksiyonun parametre etiket isminin “of” olması okuyucunun aklını karıştırıp, C türevi dillerdeki typeof() operatörününün Swift’te olduğu izlenimini uyandırmamalıdır. “obj” bir nesne örneği olmak üzere:

print(obj.dynamicType) // yerine
print(type(of: obj)) // kullanılmalıdır

Şüphesiz aynı işlem gerektiğinde self için de yapılabilir.

Swift Protocol Composition

Birden fazla protokol ile birleştirilmiş referanslar bildirilebilir. Bu durumda o referansa ancak o bildirimde belirtilen protokollerin hepsini destekleyen bir sınıf, yapı ya da enum değişkeni atanabilir.

Bildirimde protokol birleştirme işlemi protokol isimlerinin arasına “&” sembolü konularak yapılır. Örneğin:

protocol Protokol1

{

func Foo()

}

protocol Protokol2

{

func Bar()

}

class CSample1 : Protokol1

{

func Foo()

{

print(“foo”)

}

}

class CSample2 : Protokol1, Protokol2

{

func Foo()

{

print(“foo”)

}

func Bar()

{

print(“bar”)

}

}

/////////////TEST//////////////

func Test(prm: Protokol1 & Protokol2)

{

prm.Foo()

prm.Bar()

}

// Test(prm: CSample1())

// Argument type CSample1 doesnt conform to expected type ‘…’

Test(prm: CSample2()) // OK

Şüphesiz protokol listesinde sırasının bir önemi yoktur. Örneğin:

let p1: Protokol1 & Protokol2 = CSample2()

let p2: Protokol2 & Protokol1 = p1       // geçerli

Swift 3 Associated Type Generic Protocol typealias associatedtype

Dikkat: Aşağıdaki yazı yakında yazımı tamamlanacak olan Swift kitabımdan sınırlı bir alıntıdır izinsiz kopyalanıp kullanılamaz.

Swift’te fonksiyonların, class’ların, structure’ların ve enum’ların generic olabileceği daha önce de belirtilmişti. Ancak protokoller bu durumun bir istisnasıdır yani bir protokol generic olamaz. Bu nedenle aşağıdaki işlem “Protocols don’t allow generic parameters; use associated types instead” hatası ile sonuçlanır.

protocol PSample<T>

{

}

Hata mesajından da anlaşılabileceği gibi bu durumda uygulanması gereken çözüm; associated type kullanımıdır. Aşağıdaki örneği inceleyiniz:

protocol Repository

{

// Associated type tanımlamak

typealias Entity

// associatedtype Entity // Xcode 7.3+

func Save(e:Entity)

func Delete(e:Entity)

func GetRecord() -> Entity

func GetTable() -> [Entity]

}

Repository isimli protokol, temel veritabanı işlemlerini entity türünden bağımsız olarak soyutlamak için yazılmıştır. Burada typealias anahtar sözcüğü ile tanımlanan Entity aslında bir tür generic parametredir. Bu protokolü implemente eden özgün repository sınıfları hangi entity için yazılmışlarsa Entity yerine o sınıfı kullanabilir. Aşağıdaki örnekte Customer isimli sınıf Entity yerine geçecek olan sınıftır.

class Customer {

}

class CustomerRepository : Repository

{

func Save(e:Customer)

{

}

func Delete(e:Customer)

{

}

func GetRecord() -> Customer

{

return Customer()

}

 

func GetTable() -> [Customer]

{

return []

}

}

İpucu: typealias anahtar sözcüğünün iki farklı anlamda (türlere takma isim atamak ve associated type tanımlamak için) kullanılıyor olması bir tür karışıklığa neden olmaktaydı. Bu karışıklığı gidermek için Swift 3 versiyonunda associated type tanımlama amacıyla ile associatedtype isimli yeni bir anahtar sözcük gündeme getirildi. typealias anahtar sözcüğünün bu amaçla kullanımı Xcode 7.3 güncellemesi sonrasında bu gerekçeyle bir uyarıya neden olmaktadır.

Swift Generic & Contraint Kullanımı

Dikkat: Aşağıdaki yazı yakında çıkacak olan Swift kitabımdan bir alıntıdır izinsiz kopyalanıp kullanılamaz.

Swift’te generic bir fonksiyon ya da sınıfa herhangi bir türde açılım yapılabilir. Ancak geliştirici şayet isterse açılım yapılabilecek türlerde kısıtlamaya gidebilir. Kısıtlamalar, açılım yapılabilecek türlerin belirli bir taban sınıftan türetilmesi ya da belirli bir protokolü implemente etmesi şartına dayandırılabilir. Örneğin aşağıdaki Foo() isimli fonksiyonun ilk parametresine açılım yapacak olan türün CKisitlama isimli sınıftan türetilmesi, ikinci parametresi ise PKisitlama isimli protokolden implemente edilmesi gereklidir.

class CKisitlama {

}

class Sinif1 {

}

class Sinif2 : CKisitlama {

}

protocol PKisitlama {

}

class CSinif3 {

}

class CSinif4 : PKisitlama {

}

func Foo<A: CKisitlama, Z: PKisitlama>(p1:A, p2:Z)

{

}

////// TEST ///////

Foo(Sinif1(), p2: CSinif3()) // Geçersiz!!!

Foo(Sinif2(), p2: CSinif4())

Sinif1 ve Sinif3 türündeki nesneler gerekli şartları sağlamadıkları için A ve Z yer tutucuları için açılım yapamazlar.

where anahtar sözcüğü

Swift’te kısıtlamalar sadece generic fonksiyonlar için değil aynı zamanda generic türler için de belirtilebilir. Kısıtlamayı aşma koşulu ise yine belirli bir sınıftan türetme ya da belirli bir protokolü implemente etme şeklinde kurgulanır. Generic türün bildiriminde <> içerisinde yazılan yer tutucu(lar) için where anahtar sözcüğünden sonra bir veya daha fazla sayıda kısıtlama belirtilebilir. Örneğin aşağıdaki Controller isimli sınıfta T yerine PRepository ve PTestable protokollerini implemente eden bir tür kullanılabilir.

protocol PRepository

{

}

 

protocol PTestable

{

}

 

class CRepository1

{

}

 

class CRepository2 : PRepository

{

}

 

class CRepository3 : PRepository, PTestable

{

}

 

class Controller<T where T: PRepository, T: PTestable>

{

}

var cnt = Controller<CRepository3>()

Bu örnekte CRepository1 ve CRepository2 gerekli şartları sağlamadığı için açılım yapma amacıyla kullanılamayacak sınıflardır.

C-style for statement is deprecated and will be removed in a future version of Swift

İpucu : Konvansiyonel for döngüsü Swift’in 2+ versiyonlarında Xcode 7.3 güncellemesi sonrasında C-style for statement is deprecated and will be removed in a future version of Swift” açıklamalı bir uyarıya neden olmaktadır. Dahası konvansiyonel for döngüsü kullanımının gelecekteki Swift versiyonlarında tümüyle sona erdirileceği yönünde yaygın bir beklenti vardır. Bu nedenle for-in döngüsü daha çok önem arz etmektedir. Sadece dizi veya nesne koleksiyonlarının dolaşılması amacıyla değil konvansiyonel for döngüsü yerine de kullanılabilir. Örneğin:

for var i = 0; i <= 9; i += 1
{
 print("\(i)") // 0 – 9
}

yerine:
for var i in 0 ..< 10
{
    print("\(i)") // 0 – 9 
} 

Swift 2.0 guard keyword

Bu yazı yakında yayınlanacak olan Swift ile iOS Programlama kitabımdan bir alıntıdır izinsiz kullanılıp kopyalanamaz.

Bu yazıda WWDC15’te duyurulan Swift 2.0 versiyonunda gelen yeniliklerden birisi olan guard anahtar sözcüğü anlatılmaktadır.

Pratikte aldığı parametre argümanlarını çeşitli sınamalardan geçirip, bu argümanların tümü geçerli olduğunda işlem yapan fonksiyonlara sıkça ihtiyaç duyulur. Örneğin Tedarikci sınıfına ait Kaydet() fonksiyonu String türündeki tüm parametreleri boş değilse işlem yapmaktadır.

// Arrow Anti-Pattern

enum Hatalar : ErrorType

{

case Isim

case Soyisim

case Firma

}

class Tedarikci

{

class func Kaydet(isim:String, soyisim:String,

firma:String) throws -> Bool

{

if (isim != “”)

{

if (soyisim != “”)

{

if (firma != “”)

{

print(“Kaydedildi …”)

return true

}

else

{

throw Hatalar.Firma

}

}

else

{

throw Hatalar.Soyisim

}

}

else

{

throw Hatalar.Isim

}

}

}

///// TEST ///////

do

{

try Tedarikci.Kaydet(“Steve”,

soyisim: “”, firma: “Apple”)

}

catch Hatalar.Isim

{

print(“İsim hatalı”)

}

catch Hatalar.Soyisim

{

print(“Soyisim hatalı”)

}

catch Hatalar.Firma

{

print(“Firma hatalı”)

}

Burada “if piramidi” ya da “arrow” isimleriyle bilinen bir tür anti-patern söz konusudur. Çok karmaşık görünen ve hataya da çok açık olan bu kötü tekniğin yerine programcılar bazen “early return” denilen başka bir kötü çözüm daha uygulamaktadır. Bu çözümün ifade gücü zayıftır üstelik Swift özelinde optional değerler söz konusu olduğunda sorunu daha da derinleştirebilir.

// Early Return Anti-Pattern

func Kaydet(isim:String, soyisim:String, firma:String)

{

if (isim == “”)

{

return

}

if (soyisim == “”)

{

return

}

if (firma == “”)

{

return

}

print(“Kaydedildi”)

}

Swift 2.0 ile dile eklenmiş olan guard anahtar sözcüğü ise, bu tür durumlarda ve yukarıda ortaya konulan sorunlara neden olmayan bir çözüm sunar. Kullanım şekli aşağıdaki gibidir:

guard koşul else

{

// hata durumunda yapılacaklar

}

Özetlemek gerekirse:

  1. İfade gücü yüksektir yani bu anahtar sözcüğün kullanıldığını gören farklı bir programcı yapmak istediğiniz şeyi daha net anlar.
  2. Daha kısa ve temiz kod yazmanızı sağlar. Böylece hata olasılıkları da azalır.
  3. Özellikle optional değerler söz konusu ise guard ile açılan (unwraping işlemi) ve sınanan optional değerler bu işlem sonrasında doğrudan kullanılabilirler.

Örnek: Aşağıdaki uygulamada Ornek() fonksiyonu iki parametre almaktadır. İlk parametrenin geçerlilik koşulu; nil olmamaktır. Burada guard ile sınanan, dil isimli optional parametrenin değeri açılmakta (unwrapping) ve nil ise hata verilip, çıkılmaktadır. ver isimli ikinci parametrenin ise pozitif olması beklenmekte değilse hata üretilmektedir. Şayet her iki parametrede de bir hata yok ise ekrana “OK …” yazdırılmaktadır.

enum Hata : ErrorType

{

case Negatif

}

func Ornek(dil:String?, ver:Int) throws

{

guard var _ = dil else

{

print(“Dil ismi belirtmediniz”)

return

}

guard ver > 0 else

{

throw Hata.Negatif

}

print(“OK …”)

}

////// Test ////////

try Ornek(nil, ver: 3)

try Ornek(“Swift”, ver: -5)

try Ornek(“Swift”, ver: 2)