在串行队列中串行下载图像非常慢
Downloading images serially in a serial queue very slow
要求 - 我有一个要求,我收到一个 JSON 字典,我从中检索一个数组图像和内容文本。然后我必须在集合视图中显示所有具有相应内容的图像。
Update - 最重要的是,我需要根据图像大小计算单元格大小,该图像大小缩放到我掉落的恒定宽度那(可能不正确)我需要完全下载所有图像然后重新加载集合视图
问题 - 但问题是当我在后台线程中下载图像并单独填充时 arrays.Then 图像无法按照与它们相同的顺序添加在 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()
})
}
使用并发队列肯定能保持顺序。我认为你的代码几乎根本没有正确使用队列(为什么有 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);
})
}
一种更有效的方法是创建一个数据模型对象,它将代表您的图像 link 和可选的 UIImage。像这样:
class NetworkImage {
let imageURL: String!
let image: UIImage?
}
现在,当您收到带有图像 link 数组的 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")]
要求 - 我有一个要求,我收到一个 JSON 字典,我从中检索一个数组图像和内容文本。然后我必须在集合视图中显示所有具有相应内容的图像。
Update - 最重要的是,我需要根据图像大小计算单元格大小,该图像大小缩放到我掉落的恒定宽度那(可能不正确)我需要完全下载所有图像然后重新加载集合视图
问题 - 但问题是当我在后台线程中下载图像并单独填充时 arrays.Then 图像无法按照与它们相同的顺序添加在 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()
})
}
使用并发队列肯定能保持顺序。我认为你的代码几乎根本没有正确使用队列(为什么有 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);
})
}
一种更有效的方法是创建一个数据模型对象,它将代表您的图像 link 和可选的 UIImage。像这样:
class NetworkImage {
let imageURL: String!
let image: UIImage?
}
现在,当您收到带有图像 link 数组的 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")]