Загрузка изображений последовательно в последовательную очередь очень медленная
Требование - у меня есть требование, по которому я получаю словарь JSON, из которого я получаю массив изображений и текст контента. Затем я должен отобразить все изображения с соответствующим содержанием в виде коллекции.
Обновление - Прежде всего мне нужно рассчитать размер ячейки на основе размера изображения, масштабированного до постоянной ширины, для которой я упал, что (возможно, это не правильно) Мне нужно полностью загрузить все изображения, а затем перезагрузить представление коллекции
Проблема - Но проблема в том, что когда я загружаю изображения в фоновом потоке и заполняю их в отдельные массивы. Затем изображение не может быть добавлено в том же порядке, в котором они были в Словаре JSON, так как я загружаю их в параллельную очередь.
Мое решение - поэтому я подумал о загрузке их, поместив все в последовательную очередь, что сделало мой поиск очень медленным. Что может быть эффективной альтернативой для этого?
Код -
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()
})
}
3 ответа
Вы определенно можете сохранить порядок, если используете параллельную очередь. я думаю, что ваш код в его нынешнем виде не использует очередь вообще (и почему sleep(2)
?) ваша параллельная очередь должна быть внутри forloop, чтобы она могла одновременно запускать различные блоки, и они будут использовать правильный индекс цикла for, который был назначен им, чтобы поместить получающееся изображение в правильное расположение массива
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);
})
}
Более эффективным способом было бы создать объект модели данных, который будет представлять вам ссылку на изображение и необязательный UIImage. Что-то вроде этого:
class NetworkImage {
let imageURL: String!
let image: UIImage?
}
Теперь, когда вы получите ваш JSON с массивом ссылок на изображения, вы можете создать свой массив модели данных, который будет соответствовать порядку:
let dataModel: [NetworkImage]
Поэтому, когда вы будете извлекать ваши изображения асинхронно, вы можете обновить вашу dataModel своим изображением, чтобы не повлиять на порядок. Идея может быть разработана в соответствии с вашими потребностями. Вы никогда не должны использовать операции синхронизации для такого рода заданий.
Вы можете поиграть с этим примером решения, используя группы рассылки:
//: 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)")
}
}
Приставка:
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")]