Instruments Time profiler 调优APP 之图片解码
- 以前闲时用instruments的Time profiler调试过APP,发现用tableView: cellForRowAtIndexPath: 中cell的图片设置耗时较多,之前改了一下,如下
dispatch_async(dispatch_get_global_queue(0, 0), ^{ UIImage *cellImage = [UIImage imageNamed: self.imageNamesArray[indexPath.row] ]; dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image = cellImage; }); });
发现没什么效果,就没去改了(这就是不求甚解,😂),直到最近被人问到图片的显示流程,去网上搜索相关资料,才发现用imageNamed的这种方式创建的图片,并不会立即解码,而是发生在CALayerPrepareCommit后 提交到GPU之前,才会去解码,那样总是会在主线程解码的,参考图片网址,参考YYKit大神写的文章
后来在GitHub上面搜到了一种解决方案,就是在后台线程先把图片绘制到 CGBitmapContext 中,然后从 Bitmap中直接创建图片,UIImage+ImmediateDecode.m中主要代码如下
+ (instancetype)imageWithName:(NSString*)name{ NSString *imgPath = [[NSBundle mainBundle] pathForResource:name ofType:nil]; if ( !imgPath ) { imgPath = [[NSBundle mainBundle] pathForResource:name ofType:@"png"]; } if (!imgPath) { imgPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@@2x" ,name] ofType:@"png"]; } if (!imgPath) { imgPath = [[NSBundle mainBundle] pathForResource:name ofType:@"jpg"]; } return [UIImage initImmediateLoadWithContentsOfFile: imgPath]; } + (UIImage*) initImmediateLoadWithContentsOfFile:(NSString*)path { UIImage *image = [[UIImage alloc] initWithContentsOfFile:path]; CGImageRef imageRef = [image CGImage]; CGRect rect = CGRectMake(0.f, 0.f, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); CGContextRef bitmapContext = CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little ); //kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little are the bit flags required so that the main thread doesn't have any conversions to do. CGContextDrawImage(bitmapContext, rect, imageRef); CGImageRef decompressedImageRef = CGBitmapContextCreateImage(bitmapContext); UIImage* decompressedImage = [[UIImage alloc] initWithCGImage: decompressedImageRef scale:2.0 orientation:UIImageOrientationUp]; CGImageRelease(decompressedImageRef); CGContextRelease(bitmapContext); return decompressedImage; }
tableView: cellForRowAtIndexPath: 中的代码改为如下
dispatch_async(dispatch_get_global_queue(0, 0), ^{ UIImage *tempImage = [UIImage imageWithName:self.imageNamesArray[indexPath.row]]; dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image = tempImage; [cell setNeedsLayout]; [cell layoutIfNeeded]; }); });
- Tips:
- imageNamed:的方式对图片名称要求比较松,可以自动识别2x、3x的图片,以及png不带后缀都可以;但是以[[NSBundle mainBundle] pathForResource:@"myCamera" ofType:@"png”] 如果图片是myCamera@2x的话,则获取的路径为空
- Instruments 底部的call tree 设置一般勾选第2、3、4项;call tree constraints 可以设置最小的耗时操作如16ms
- Instruments 鼠标框选时间轴中的某部分可以只看选中区域的耗时操作;Option + Click 智能展开或折叠call tree