首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将图像从文件加载到UIImageView的精确定时问题

将图像从文件加载到UIImageView的精确定时问题
EN

Stack Overflow用户
提问于 2015-05-12 14:49:46
回答 2查看 640关注 0票数 1

我试图测量将一张大照片(JPEG)从文件加载到UIImageView上的iOS 8.0所需的时间。

我现在的代码是:

代码语言:javascript
复制
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触摸)上加载一个非常大的图像(4608x3456JPEG)时,我可以看到执行时间是2-3秒,并阻塞主线程。这可以通过这样一个事实观察到:在这段时间内,UIButton仍然处于突出显示的状态,并且其他UI元素不允许交互。

因此,我希望我的计时函数报告的时间为2-3秒.然而,它报告的时间是毫秒--例如:

代码语言:javascript
复制
[execution time]: 0.0116159915924072

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

这使我提出以下问题:

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

回答 2

Stack Overflow用户

发布于 2015-05-13 21:11:31

你在这里测量的东西有两部分:

从磁盘加载映像:

UIImage(contentsOfFile: imageFile)

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

imageView.image = ....

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

我的猜测是,您的计时器只捕获磁盘加载部分,解压缩在下一个运行循环中进行。那么大的图像的解压可能需要一段时间,可能是狮子共享的时间。

如果要显式地度量解压缩所需的时间,则需要手动完成,方法是将图像绘制到屏幕外的上下文中,如下所示:

代码语言:javascript
复制
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就会检测到这一点,并且不会重复这个过程。

代码语言:javascript
复制
// 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%正确的。

票数 1
EN

Stack Overflow用户

发布于 2015-05-12 16:22:03

我会尝试使用单元测试来计时,因为XCTest框架提供了一些很好的性能度量工具。我认为这种方法可以解决懒惰的装载问题.虽然我不是百分之百的。

代码语言:javascript
复制
func testImagePerformance() {
    let date = NSDate()

    measureBlock() {
        if let imageFile = NSBundle.mainBundle().pathForResource("large_photo", ofType: "jpg") {
            imageView.image = UIImage(contentsOfFile: imageFile)
        }
    }
}

(顺便提一下,你提到的加载阻塞了主应用程序线程.你应该用NSOperationQueue来确保这不会发生.你可能已经知道了:http://nshipster.com/nsoperation/)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30194395

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档