2016-04-02 20 views
1

Tembel veya hesaplanmış bir özellikten bir uyumsuzluk işlevini çağırmanın bir yolu var mı?Tembel var özellikte async işlevi çağrısı

struct Item { 
    lazy var name: String = { 
    API.requestThing({ (string: String) in // Xcode didn't like this 
     return string // this would not work here 
    }) 
    }() 
} 

class API { 
    class func requestThing(completion: String -> Void) { 
    completion("string") 
    } 
} 
+2

Xcode ne dedi? – Tim

+0

Belirli bir hata vermez, ancak bunu da derleyemez. –

+0

API'yi sadece bir kez aramak ve sonucu önbelleğe almak istiyor musunuz? Aksi halde API'yi her seferinde çağırıyorsunuz. –

cevap

1

API.requestThing Your tamamlama işleyicisi bir String döndürür, henüz hiçbir dönüş değeri beklenir:

(completion: String -> Void) 

Ben bu işe aldı:

struct Item { 
    lazy var name: String = { 
     API.requestThing({ (string: String) in 
      return string 
     }) 
    }() 
} 

class API { 
    class func requestThing(completion: String -> String) -> String { 
     return completion("string") 
    } 
} 
+0

"requestThing" işlevi _synchronous_ ve istenildiği gibi - senkronize değil. – CouchDeveloper

0

Birincisi, requestThing, ()'u (yani geçersiz) ve String'u döndürmez. Yani şu ifadenin türü de () değil String geçerli:

API.requestThing { string in 
    return string 
} 

İkincisi, requestThing çağrısı, var vücut işlevine çağrı hala tembel var olarak name tanımlanan bu yüzden bile, asenkron olduğunu synchronous ve hemen geri dönecektir. Eğer alınan değeri önbelleğe almak istiyorum

func name(completion: String ->()) { 
    API.requestThing { string in 
     completion(string) 
    } 
} 

// Later you call it in this way 
myItem.name { name in 
    // use the value of name 
} 

ek olarak eğer bir class için Item değiştirmek ve aşağıdaki kodu

class Item { 
    private var nameValue: String? 
    func name(completion: String ->()) { 
     if let value = nameValue { 
      // return cached value 
      return completion(value) 
     } else { 
      // request value 
      API.requestThing { string in 
       // cache retrieved value 
       self.nameValue = string 
       // return retrieved value 
       completion(string) 
      } 
     } 
    } 
} 
kullanabilirsiniz:

Yani böyle bir fonksiyonu haline name dönüştürebilir eğer

+0

Çözümünüzde 'name' artık bir özellik değil. Ve önbelleğe alma yaklaşımınız bir veri yarışından muzdariptir. Potansiyel olarak 'çağıracağı Yani, çağırıyor' name' birden çok kez API.requestThing' ve 'erişen nameValue' nedeniyle veri yarışı doğası gereği güvensiz olduğunu. – CouchDeveloper

0

Bu durumda "tembel" kullanımı için iyi bir sebep yoktur. tembel başlatma için. Sadece normal bir func oluşturun ve bir tamamlama işleyicisi geçin.

+0

Aslında kullanım durumlarını hayal edebiliyorum. Örneğin, bir 'profileImage' özelliği ile bir 'Kullanıcı' sınıfına sahip olmak. Bir kullanıcı getirilirken API, profil resmine _URL_ sahip bir JSON döndürür. "Kullanıcı" sınıfı dahili olarak URL'yi tutar ve yalnızca görüntülendiğinde resmi getirmeye başlar. Bu _safely_ _future_ ile uygulanabilir. 'ProfileImage 'özelliği,' Future 'türünde bir _lazy_ özelliği haline gelir. – CouchDeveloper

+0

Tamam, ama burada normal bir işlevin bariz avantajı nedir? 'Fonk getProfileImage() -> Gelecek ' – Darko

0

Orada bunu yapmak için hiçbir zorlayıcı nedeni muhtemelen, ama şu yaklaşım makul görünmektedir:

yerine tip String bir değişken olan - bazen o şeyin bir "Geleceği" gerektirir, örneğin Future<String>. Gelecek, senkronize olmayan bir işlemin sonucu olan nolu sonucunu temsil eder; bu, sorunuzda tam olarak verilenin aynısıdır.

Geleceğin kendisi bir "normal" değişkendir ve aynı zamanda tembel olarak da değerlendirilebilir. Henüz nihai değerine sahip değil. Bunun anlamı, altta yatan görev yalnızca açık olduğunda başlatılacaktır (ör. tembel). Tasarım veya mimari açıdan bakıldığında bu mantıklı olabilir. aşağıdaki gibi

func fetchString() -> Future<String> { ... } 

lazy var name: Future<String> = fetchString() 

Daha sonra kodunuzda, sen değişkeni edinin:

item.name.map { string in 
    print(string) 
} 

bu tembel özelliğine ilk erişim ise, dize hesaplar altında yatan asenkron çalışmaya başlayacaktır. Daha sonra, değişken mevcut olduğunda, map işlevinde sağlanan eşleme işlevi, değişken ile bir değişken olarak çağrılır - muhtemelen bir süre sonra da.

Aksi takdirde (bu ilk erişim değilse), yalnızca mümkün olduğunda hemen parametrede bulunan dizgiyi sağlayacaktır.işlemler başarısız olabileceğinden

, ayrıca araç sağlayan bir "Gelecek" Bu işlemek için: Ayrıca

item.name.map { string in 
    print(string) 
}.onFailure { error in 
    print("Error: \(error)") 
} 

bakınız: https://en.wikipedia.org/wiki/Futures_and_promises

de sık sık adı verilen Swift ve Objective-C Futures için uygulamalar vardır "Söz vermek".

+0

Aslında, çünkü oldukça tehlikeli olabilir burada Gelecek kalıbı kullanarak: Eğer Future depolamak if (bazı nedenlerden dolayı başka türlü neden 'lazy'?) Aynı Promise/Gelecek üzerinde döndürülür Bir sonraki "tembel" çağrısı. Ancak tasarım başına bir Vaat/Gelecek olarak sadece _exactly_ _once_ dosyasını çözebildiğiniz için artık hiçbir şey yapamazsınız. İkinci bir kez çözmek için çalışıyorum, çoğu Future/Promise uygulamalarında gerçekten uygulamayı kilitleyecektir. – Darko

+0

Aslında, bir gelecek sadece bir kez çözülebilir (yani tamamlanabilir). Tasarım gereği, bir gelecek sadece _after_ tamamlanmış olan bir _constant_'ı temsil eder. Geleceği elde eden bir müşteri, değerini bir kereden fazla elde edebilir. Bunun gerekli olup olmadığını geleceği _created_ ve sınıf, bu istediği gibi, gibi birçok yeni gelecekleri oluşturabilir, Gelecek tip özelliği _exposes_ veya bu mantıklı olmadığı. Yine de hiç tehlikeli olmamalı. – CouchDeveloper