我正在尝试更新进度条,因为我的图片已加载。我在这里阅读了几个答案,并尝试以不同的方式格式化我的代码。我正在尝试阅读图像并更新进度条。然后,将加载所有图像调用代码来处理它们。我得到的最好结果是一些大部分时间都能运行的代码。但是,如果我正在处理大量图像的情况,我会遇到奇怪的错误。我认为它会继续并在所有图像完全加载之前运行继续代码。当我删除dispatch_async时,代码工作正常,但进度条不会更新。
func imageLocXML(il:String) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
let url:NSURL? = NSURL(string: il)
let data:NSData? = NSData(contentsOfURL: url!)
let image = UIImage(data: data!)
pieceImages.append(image!)
self.numImagesLoaded += 1
self.updateProgressBar()
if self.numImagesLoaded == self.numImagesToLoad {
self.continueLoad()
}
}
}
答案 0 :(得分:4)
有很多问题:
此代码不是线程安全的,因为您在numImagesLoaded
上有竞争条件。从理论上讲,这可能导致continueLoad
不止一次被调用。您可以通过将{(1}}同步到这个(和其他模型对象)的更新回到主队列来实现线程安全。
与DashAndRest一样,您必须将UI更新分派给主队列。
当您使此异步时,您在发起大量请求时引入了网络超时风险。您可以通过重构代码来使用操作队列而不是调度队列来解决此问题,并指定numImagesLoaded
。
图像正在添加到数组中:
由于这些任务是异步运行的,因此无法保证按任何特定顺序完成,因此阵列不会按顺序排列。您应该将图像保存在字典中,在这种情况下,订单不再重要。
就像maxConcurrentOperationCount
一样,numImagesLoaded
不是线程安全的。
您正在使用大量强制解包,因此如果任何请求失败,则会崩溃。
但要解决这个问题,我们必须退一步看看调用此方法的例程。让我们想象你有类似的东西:
pieceImages
我建议您用以下内容替换它:
var pieceImages = [UIImage()]
func loadAllImages() {
for imageUrl in imageURLs {
imageLocXML(imageUrl)
}
}
func imageLocXML(il:String) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
let url:NSURL? = NSURL(string: il)
let data:NSData? = NSData(contentsOfURL: url!)
let image = UIImage(data: data!)
self.pieceImages.append(image!)
self.numImagesLoaded += 1
self.updateProgressBar()
if self.numImagesLoaded == self.numImagesToLoad {
self.continueLoad()
}
}
}
话虽如此,我认为这里存在更深层次的问题:
您是否应该像这样提前加载图像?我们通常建议延迟加载图像,只在需要时加载它们。
如果您要将图像加载到这样的结构中,您应该优雅地处理内存压力,在低内存警告时清除它。然后,您需要优雅地处理在检索图像时要执行的操作,并且由于内存压力而将其清除(导致您回到即时延迟加载模式)。
< / LI>我们通常建议不要使用同步网络请求(var pieceImages = [String: UIImage]()
func loadAllImages() {
let queue = NSOperationQueue()
queue.maxConcurrentOperationCount = 4
let completionOperation = NSBlockOperation {
self.continueLoad()
}
for imageURL in imageURLs {
let operation = NSBlockOperation() {
if let url = NSURL(string: imageURL), let data = NSData(contentsOfURL: url), let image = UIImage(data: data) {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.numImagesLoaded += 1
self.pieceImages[imageURL] = image
self.updateProgressBar()
}
}
}
queue.addOperation(operation)
completionOperation.addDependency(operation)
}
NSOperationQueue.mainQueue().addOperation(completionOperation)
}
)。我们通常使用NSData(contentsOfURL:_)
这是可取消的,提供更丰富的错误处理等等。不可否认,这进一步使上述代码复杂化(可能导致我走上异步NSURLSession
子类的道路),但你应该至少了解NSOperation
的限制。
答案 1 :(得分:0)
尝试DISPATCH_QUEUE_PRIORITY_DEFAULT
将此作为后台队列:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let url:NSURL? = NSURL(string: il)
let data:NSData? = NSData(contentsOfURL: url!)
let image = UIImage(data: data!)
self.pieceImages.append(image!)
self.numImagesLoaded += 1
dispatch_async(dispatch_get_main_queue(), {
//UI must be updated on main thread queue
self.updateProgressBar()
})
if self.numImagesLoaded == self.numImagesToLoad {
self.continueLoad()
}
}
必须在MAIN线程队列上更新UI!
您还必须使用self
来访问pieceImages
。