2 votes

Téléchargement des images en série dans une file d'attente très lente

Exigence - J'ai un besoin dans lequel je reçois un dictionnaire JSON à partir duquel je récupère un tableau d'images et de texte de contenu. Ensuite, je dois afficher toutes les images avec le contenu correspondant dans une vue de collection.

Mise à jour - Avant tout, j'ai besoin de calculer la taille de la cellule en fonction de la taille de l'image mise à l'échelle à une largeur constante pour laquelle je pense que (peut être pas correct) j'ai besoin que toutes les images soient téléchargées complètement puis recharger la vue de la collection.

Problème - Mais le problème est que lorsque je télécharge les images en arrière-plan et que je les remplis dans des tableaux séparés, les images ne peuvent pas être ajoutées dans le même ordre qu'elles étaient dans le dictionnaire JSON puisque je les télécharge dans une file d'attente simultanée.

Ma solution - J'ai donc pensé à les télécharger en mettant tout dans une file d'attente en série, ce qui a rendu ma récupération de données très lente. Quelle peut être une alternative efficace pour cela ?

Code -

let serialQueue = dispatch_queue_create("my serial queue", nil)

            dispatch_async(serialQueue, {

                print("This is first Method")

                for var i=0;i<self.resultArr.count;i++//resultArr is my array of data's in the jsonDic
             {

                    sleep(2)

                    print(self.resultArr[i].valueForKey("profile_pic")! as! String)
                    if self.resultArr[i].valueForKey("profile_pic")! as! String != "Null" && self.resultArr[i].valueForKey("profile_pic")! as! String != "null" && self.resultArr[i].valueForKey("profile_pic")! as! String != "NULL" && self.resultArr[i].valueForKey("profile_pic")! as! String != ""
                    {
                        let imageUrl = UrlClass.imageUrlWithoutExtension + String(self.resultArr[i].valueForKey("profile_pic")!)
                        print(imageUrl)
                        let url = NSURL(string: imageUrl)
                        let imageData = NSData(contentsOfURL: url!)

                        self.contentlabelArr.insertObject(String(self.resultArr[i].valueForKey("content")!), atIndex: i)

                        if imageData != nil && imageData?.length > 0
                        {
                            print("this is \(i) image")
                            print(UIImage(data: imageData!))

                            self.imageArr.insertObject(UIImage(data: imageData!)!, atIndex: i)
                        }
                        else
                        {
                            print("\(i) image has nill")
                            self.imageArr.insertObject(UIImage(named: "logo.png")!, atIndex: i)
                        }

                    }
                    else
                    {
                        print("\(i) image has nill")
                        self.contentlabelArr.insertObject(String(self.resultArr[i].valueForKey("content")!), atIndex: i)
                        self.imageArr.insertObject(UIImage(named: "logo.png")!, atIndex: i)
                    }

                    print("\(i) times 5 is \(i * 5)")

                    if self.imageArr.count==self.resultArr.count
                    {
                         print(self.resultArr.count)
                         print(self.imageArr.count)
                        dispatch_async(dispatch_get_main_queue(),
                            {
                                print(self.resultArr.count)
                                print(self.imageArr.count)
                                print(self.imageArr)
                                print(self.contentlabelArr)
                                self.collectionView?.reloadData()
                        })
                    }

3voto

Oleg Danu Points 1490

Une manière plus efficace serait de créer un objet de modèle de données qui représenterait votre lien d'image et l'UIImage optionnel. Quelque chose comme ceci :

class NetworkImage {
    let imageURL: String!
    let image: UIImage?
}

Maintenant, lorsque vous recevez votre JSON avec le tableau de liens d'images, vous pouvez créer votre tableau de modèle de données, qui respectera l'ordre :

let dataModel: [NetworkImage]

Ainsi, lorsque vous récupérez vos images de manière asynchrone, vous pouvez mettre à jour votre dataModel avec votre image, de sorte qu'aucun ordre ne sera affecté. L'idée peut évoluer en fonction de vos besoins. Vous ne devriez jamais utiliser les opérations de synchronisation pour ce type de tâches.

1voto

Fonix Points 2289

Vous pouvez certainement conserver l'ordre si vous utilisez une file d'attente concurrente. Je pense que votre code, tel qu'il est, n'utilise pas du tout la file d'attente correctement (et pourquoi y a-t-il un problème d'ordre dans la file d'attente). sleep(2) ?) votre file d'attente concurrente devrait se trouver à l'intérieur de la boucle for afin de pouvoir déclencher les différents blocs en même temps, et ils utiliseront l'index correct de la boucle for qui leur a été assignée pour placer l'image résultante dans l'emplacement correct du tableau.

let sema = dispatch_semaphore_create(2); //depending how many downloads you want to go at once
for i in 0..<self.resultArr.count {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {

        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

        //download images here, order of execution will not be guaranteed, but after they are finished, they will always put the images in the array at 'i' so it doesnt matter

        dispatch_semaphore_signal(sema);
    })
}

1voto

CouchDeveloper Points 7377

Vous pouvez jouer avec cet exemple de solution, en utilisant des groupes d'envoi :

//: Playground - noun: a place where people can play

import UIKit
import Dispatch
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

class Record {
    init(text: String, imageURL: String) {
        self.text = text
        self.imageURL = imageURL
        self.image = nil
    }
    var text: String
    var imageURL: String
    var image: String?
}

extension Record: CustomStringConvertible {
    var description: String {
        return "text: \(text), imageURL: \(imageURL), image: \(image)"
    }
}

// Fetch text and image url, but no image.
func fetchRecords(completion: ([Record]?, ErrorType?) -> ()) {
    let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
    dispatch_after(delayInNanoSeconds, dispatch_get_global_queue(0, 0)) {
        let result: [Record] = [
            Record(text: "Aaa", imageURL: "path/image1"),
            Record(text: "Bbb", imageURL: "path/image2"),
            Record(text: "Ccc", imageURL: "path/image3")
        ]
        completion(result, nil)
    }
}

// fetch an image
func fetchImage(url: String, completion: (String?, ErrorType?) -> () ) {
    let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
    dispatch_after(delayInNanoSeconds, dispatch_get_global_queue(0, 0)) {
        let image = url
        completion(image, nil)
    }
}

// Put everything together: 
// 1) Fetch an array of records, omitting the image
// 2) When this is finished, in parallel, for each record 
//    fetch each image.
// 3) When all is finished, call the completion handler containing
//    the records including the images
func fetchRecordsWithImages(completion: ([Record]?, ErrorType?) -> () ) {
    fetchRecords { (result, error) in
        if let records = result {
            let grp = dispatch_group_create()
            records.forEach { record in
                dispatch_group_enter(grp)
                fetchImage(record.imageURL) { (image, error) in
                    if let image = image {
                        record.image = image
                    }
                    dispatch_group_leave(grp)
                }
            }
            dispatch_group_notify(grp, dispatch_get_global_queue(0, 0)) {
                completion(records, nil)
            }
        }
    }
}

fetchRecordsWithImages() { (records, error) in
    if let records = records {
        print("Records: \(records)")
    }
}

Console :

Records: [text: Aaa, imageURL: path/image1, image: Optional("path/image1"), text: Bbb, imageURL: path/image2, image: Optional("path/image2"), text: Ccc, imageURL: path/image3, image: Optional("path/image3")]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X