在串行队列中连续下载图像非常慢

时间:2016-04-28 07:34:35

标签: ios json swift grand-central-dispatch

要求 - 我要求我收到一个JSON字典,我从中检索图像和内容文本数组。然后我必须在集合视图中显示具有相应内容的所有图像。

更新 - 最重要的是,我需要根据图像尺寸来计算单元格大小,该图像尺寸缩放到我所下降的恒定宽度(可能不正确)I需要完全下载所有图像然后重新加载集合视图

问题 - 但问题是当我在后台线程中下载图像并填充在单独的数组中时。因为我不能按照它们在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 个答案:

答案 0 :(得分:3)

更有效的方法是创建一个数据模型对象,它将代表您的图像链接和可选的UIImage。像这样:

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

现在,当您收到带有图像链接数组的JSON时,您可以创建数据模型数组,这将遵守顺序:

let dataModel: [NetworkImage]

因此,当您异步检索图像时,可以使用图像更新dataModel,因此不会影响订单。 这个想法可以根据您的需求进行改进。 你永远不应该对这类工作使用同步操作。

答案 1 :(得分:1)

如果你使用并发队列,你肯定可以保留订单。我认为你的代码几乎没有正确使用队列(为什么有public delegate bool PreRemoveTab(int indx); public class TabControlEx : TabControl { public TabControlEx() : base() { PreRemoveTabPage = null; this.DrawMode = TabDrawMode.OwnerDrawFixed; } public PreRemoveTab PreRemoveTabPage; protected const int size = 5; protected int moveRight = 0; protected int MoveRight { get { return moveRight; } set { moveRight = value; } } protected override void OnDrawItem(DrawItemEventArgs e) { Brush b = new SolidBrush(Color.Salmon); Brush b1 = new SolidBrush(Color.Black); Font f = this.Font; Font f1 = new Font("Arial", 9,FontStyle.Bold); if (e.Index != 0) { Rectangle r = e.Bounds; r = GetTabRect(e.Index); r.Offset(2, 2); r.Width = size; r.Height = size; Pen p = new Pen(b,2); string title = this.TabPages[e.Index].Text; string boldLetter = title.Substring(0, 1); title = title.Remove(0, 1); MoveRight = ((Int32)e.Graphics.MeasureString(title, f, 200).Width) + 1; // -1 e.Graphics.DrawLine(p, r.X +10 + MoveRight - 2, r.Y, r.X +10 + MoveRight + r.Width, r.Y + r.Height+2); e.Graphics.DrawLine(p, r.X +10 + MoveRight + r.Width, r.Y, r.X + 10 + MoveRight-2, r.Y + r.Height+2); e.Graphics.DrawString(boldLetter, f1, b1, new PointF(r.X, r.Y)); e.Graphics.DrawString(title, f, b1, new PointF(r.X+8, r.Y+1)); } else { Rectangle r = GetTabRect(e.Index); e.Graphics.DrawString(this.TabPages[e.Index].Text, f, b1, new PointF(r.X + 5, r.Y)); } } protected override void OnMouseClick(MouseEventArgs e) { Point p = e.Location; for (int i = 0; i < TabCount; i++) { Rectangle r = GetTabRect(i); r.Offset(2, 2); r.Width = size+2; r.Height = size+2; r.X = r.X + MoveRight + 8; if (r.Contains(p)) { if (i != 0) { CloseTab(i); } } } } private void CloseTab(int i) { if (PreRemoveTabPage != null) { bool closeIt = PreRemoveTabPage(i); if (!closeIt) return; } TabPages.Remove(TabPages[i]); } } ?)你的并发队列应该在forloop内部,这样它可以同时触发不同的块,并且他们将使用分配给它们的for循环的正确索引将结果图像放在正确的数组位置

sleep(2)

答案 2 :(得分:1)

您可以使用调度组来使用此示例解决方案:

//: 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")]