ios - 准确计时将图像从文件加载到 UIImageView 的问题

标签 ios swift uiimageview

我正在尝试测量将大照片 (JPEG) 从文件加载到 iOS 8.0 上的 UIImageView 所花费的时间。

我当前的代码:

import UIKit
class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!
    @IBAction func loadImage(sender: UIButton) {
        if let imageFile = NSBundle.mainBundle().pathForResource("large_photo", ofType: "jpg") {
            // start our timer
            let tick = Tick()
            // loads a very large image file into imageView
            // the test photo used is a 4608 × 3456 pixel JPEG
            // using contentsOfFile: to prevent caching while testing timer
            imageView.image = UIImage(contentsOfFile: imageFile)
            // stop our timer and print execution time
            tick.tock()
        }
    }
}

class Tick {
    let tickTime : NSDate
    init () {
        tickTime = NSDate()
    }
    func tock () {
        let tockTime = NSDate()
        let executionTime = tockTime.timeIntervalSinceDate(tickTime)
        println("[execution time]: \(executionTime)")
    }
}

当我在我的测试设备(第 5 代 iPod touch)上加载一个非常大的图像(4608 x 3456 JPEG)时,我可以看到执行时间约为 2-3 秒并阻塞了主线程。这是可以观察到的,因为 UIButton 在这段时间内保持突出显示状态,并且没有其他 UI 元素允许交互。

因此,我希望我的计时函数报告大约 2-3 秒的时间。但是,它报告毫秒时间 - 例如:

[execution time]: 0.0116159915924072

tick.tock() 在加载图像之前 将消息打印到控制台。这让我感到困惑,因为主线程似乎被阻塞直到图像加载完毕。

这让我提出以下问题:

  • 如果图片在后台异步加载,那么 为什么用户交互/主线程被阻塞?
  • 如果图像正在主线程上加载,为什么 tick.tock() 函数打印到控制台 before 图像是 显示?

最佳答案

您在这里测量的内容分为两部分:

从磁盘加载图像:

UIImage(contentsOfFile: imageFile)

并将图像从 JPEG 解压缩为要显示的位图:

imageView.image = ....

第一部分实际涉及从磁盘(磁盘 I/O)检索压缩的 JPEG 数据并创建一个 UIImage 对象。 UIImage 对象持有对压缩 数据的引用,直到需要显示它为止。只有在它准备好呈现到屏幕上时,它才会将图像解压缩为位图以显示(在主线程上)。

我的猜测是你的计时器只捕捉磁盘加载部分,解压缩发生在下一个运行循环中。解压缩这么大的图像可能需要一段时间,可能是大部分时间。

如果您想明确测量解压缩需要多长时间,您需要手动完成,方法是将图像绘制到屏幕外的上下文中,如下所示:

let tick = Tick()

// Load the image from disk
let image = UIImage(contentsOfFile: imageFile)

// Decompress the image into a bitmap
var newImage:UIImage;
UIGraphicsBeginImageContextWithOptions(image.size, true, 0);
image.drawInRect(CGRect(x:0,y:0,width:image.size.width, height:image.size.height))
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

tick.tock()

在这里,我们正在复制将 image 分配给 imageView.image 时发生的解压缩

在处理这种大小的图像时,保持 UI 响应的一个简便技巧是将整个过程踢到后台线程。这很有效,因为一旦您手动解压缩了图像,UIKit 就会检测到这一点并且不会重复该过程。

// Switch to background thread
dispatch_async(dispatch_get_global_queue(Int(DISPATCH_QUEUE_PRIORITY_DEFAULT.value), 0)) {

    // Load the image from disk
    let image = UIImage(contentsOfFile: imageFile)

    // Ref to the decompressed image
    var newImage:UIImage;

    // Decompress the image into a bitmap
    UIGraphicsBeginImageContextWithOptions(image.size, true, 0);
    image.drawInRect(CGRect(x:0,y:0,width:image.size.width, height:image.size.height))
    newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // Switch back to main thread
    dispatch_async(dispatch_get_main_queue()) {

        // Display the decompressed image
        imageView.image = newImage
    }
}

免责声明:此处的代码尚未在 Xcode 中进行全面测试,但如果您决定使用它,它是 99% 正确的。

关于ios - 准确计时将图像从文件加载到 UIImageView 的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30194395/

相关文章:

ios - UIImageView分配后未更改

ios - 在保留 alpha 的同时从阴影 View 创建 UIImage?

ios - 在 Swift 中作为整数的索引

ios - 没有得到 "MAP Route"

json - Swift 4 使用随 secret 钥解码嵌套的 JSON

ios - Swift:应用程序因 EXC_BAD_INSTRUCTION 错误而崩溃?

iphone - SDWebimage 占位符中的事件指示器

ios - 解除意外行为

iphone - 发布 App 后 iAd 未在设备上显示

ios - 在新库 ViewController 上方添加 UIButton