2015-08-03 16 views
8

Parse'den bir PFFile nesnem var ve bu PFFile'ın UIImage temsilini alan ve döndüren bir işlev oluşturmaya çalışıyorum. gibi bir şey:iOS - Swift - Eşzamansız olarak alınan değer döndüren işlev

func imageFromFile(file: PFFile) -> UIImage? { 
    var image: UIImage? 

    file.getDataInBackgroundWithBlock() { (data: NSData?, error: NSError?) -> Void in 
     if error != nil { 
      image = UIImage(data: data!) 
     } 
    } 

    return image 
} 

Ancak sorun burada açıktır. GetDataInBackroundWithBlock işlevi eşzamansız olduğu için her seferinde sıfırlayacağım. Görüntü değişkeni geri gelmeden önce UIImage alınana kadar beklemek için herhangi bir yolu var mı? Eşzamanlı getData() kullanmanın bu durumda gitmenin etkili bir yolu olup olmadığını bilmiyorum.

+1

Beklemek mümkündür ('NSCondition kullanılabilir ') ancak çağrı kodunuzun ana iş parçacığı üzerinde olması kötü bir fikirdir. Genel strateji, tamamlama işleyicisindeki görüntü ile bir şeyler yapmak olabilir. –

+0

Eğer beklerseniz, asenkronize olmaz.) - – EmilioPelaez

+0

Yinelenen [tamamlama işleyicisinden dönüş değeri Swift] arasında (http://stackoverflow.com/questions/31608302/return-value-from-completion-handler-swift/31608684#31608684) –

cevap

11

Evet, bunu yapmak mümkündür. Buna closure veya daha yaygın olarak callback denir. Bir callback esasen başka bir işlevde bir argüman olarak kullanabileceğiniz bir işlevdir. Tartışmanın sözdizimi

functionName: (arg0, arg1, arg2, ...) -> ReturnType 

ReturnType genellikle Void olmasıdır. Senin durumunda, bunu kolaylaştırmak için, içinde bir geri arama ile bir işlevi çağırmak sözdizimi (girintili olduğu

function(arg0, arg1, arg2, ...){(callbackArguments) -> CallbackReturnType in 
    //code 
} 

Ve birkaç geri aramaları ile bir işlevi çağırmak sözdizimi

result: (image: UIImage?) -> Void 

kullanabilirsiniz fonksiyon fonksiyonunu (işlev döndükten sonra çağrılır kaçarsa))

function(
    arg0, 
    arg1, 
    arg2, 
    {(cb1Args) -> CB1Return in /*code*/}, 
    {(cb2Args) -> CB2Return in /*code*/}, 
    {(cb3Args) -> CB3Return in /*code*/} 
) 

okumak, sen argüman türü önünde @escaping eklemelisiniz

İşlev döndürdükten sonra ve sonuç olarak UIImage? içeren tek bir geri çağırma kullanmak isteyeceksiniz.

Yani, kod bu

func imageFromFile(file: PFFile, result: @escaping (image: UIImage?) -> Void){ 
    var image: UIImage? 

    file.getDataInBackgroundWithBlock() { (data: NSData?, error: NSError?) -> Void in 
     //this should be 'error == nil' instead of 'error != nil'. We want 
     //to make sure that there is no error (error == nil) before creating 
     //the image 
     if error == nil { 
      image = UIImage(data: data!) 
      result(image: image) 
     } 
     else{ 
      //callback nil so the app does not pause infinitely if 
      //the error != nil 
      result(image: nil) 
     } 
    } 
} 

gibi bir şey olabilir Ve onu aramak için, sadece istediğiniz ne

imageFromFile(myPFFile){(image: UIImage?) -> Void in 
    //use the image that was just retrieved 
} 
+0

bir kapaktır bilmek Bir iplik engelleyici çizgisi boyunca daha fazla bir şey düşünüyordum ama onlar hakkında fazla bir şey bilmiyorum, bu yüzden bu durumun ilgili olup olmadığını bilmiyorum. Sadece yapmak istediğim tüm beri şeyler biraz dürülmüş görünüyor burada bir kapatma kullanarak ileride kullanılmak üzere görüntüyü döndürmek ama bunu yapmak için bir yoldur sanırım. You @itstrueimryan –

+0

* * ileride kullanılmak üzere bir sözlükte görüntü verdiler, ama bu muhtemelen bunu yapmanın en iyi yolu olacağını hissediyorum. Hiç değiştirmek istedim nasıl ya bir görseli kullanırken, bir kapak yardımıyla o kadar daha az kod – Jojodmo

+3

@itstrueimryan değiştirmek zorunda yapmak istiyorsanız - Evet, bu tamamlama kapatma desen en iyisidir. Teknik olarak ağ isteği (örneğin, vb semaforları, koşullar kullanılarak) bitene kadar, sen parçacığı engelleyen _could_ ama (sen niye onun kötü bir fikir, bize bildirin nedenlerle bir listesini gerekirse) o korkunç şablon. Diğer kalıpları arıyorsanız, bağımlılıklara (biraz daha fazla kod, ancak çok karmaşık senaryolarda kullanışlıdır) sahip asenkron "NSOperation" alt sınıflarını, temsilci protokol kalıplarını (daha hantal, IMHO) veya gelecek/vaatleri (standart dışı) içerir Eşzamansız kod yazmak için üçüncü taraf modeli. – Rob

3

söz/gelecekteki tasarım deseni yapar tam olarak ne olduğunu kullanabilirsiniz. Swift'de birçok uygulama var. Örnek olarak mükemmel BrightFutures kütüphanesini kullanacağım. İşte (https://github.com/Thomvis/BrightFutures)

kodu:

func imageFromFile(file: PFFile) -> Future<UIImage> { 
    let promise = Promise<UIImage>() 

    file.getDataInBackgroundWithBlock() { (data: NSData?, error: NSError?) -> Void in 
     if error != nil { 
      image = UIImage(data: data!) 

      // As soon as the method completes this will be called 
      // and triggers the future.onSuccess in the caller. 
      promise.success(image) 

     } else { 

      // This would trigger future.onFailure in the caller 
      promise.failure(error) 
     } 
    } 

    return promise.future 
} 

Açıklama: temelde "gelecekte" bir sonucu olacağı bir "sözünü" yaratıyor ne. Ve async yöntemi tamamlanmadan önce bu gelecek vaadini hemen geri veriyorsunuz.

bu yöntemin arayanın bunu böyle idare edecek:

func doSomethingWithTheImage() { 
    let future = imageFromFile(file: pffile) 

    future.onSuccess { image in 
     // do something with UIImage: image 
    } 

    future.onFailure { error in 
     // handle NSError: error 
    } 
} 

başarıyla indirilen görüntü ile tüm işler yapıyorlar onSuccess işleyicisi. Bir hata varsa, onFailure işleyicisinde bunu ele alırsınız.

Bu, "nil" döndürme sorununu çözer ve ayrıca eşzamansız süreçleri işlemek için en iyi uygulamalardan biridir.

1

Bu

ki sen soruyorsun olurdu senkron ve asenkron yöntemleri arasında dönüştürebilirsiniz Swift 2.0 için küçük bir kütüphane yazdım önermiyoruz. here bulunabilir. Böyle kullanmak mümkün olurdu

bunu kullanmanın uygun olduğu hangi senaryolar içinde açıklar sorumluluk reddi (sizin durumda en olası değil) mutlaka okuyun:

func imageFromFile(file: PFFile) throws -> UIImage? { 
    let syncGet = toSync(file.getDataInBackgroundWithBlock) 
    let data = try syncGet() 
    return data.map(UIImage.init) 
}