crash描述
最近看到一段代码,使用到了CGImageCreateWithImageInRect方法对图片进行裁剪.
当这段代码第一次执行时,可能没有任何问题,但是第二次执行或者多次执行以后,发生了crash,而且很难定位.
这种情形往往是内存问题.
代码分析
UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.width, view.frame.size.height), opaque, [UIScreen mainScreen].scale);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRefimageRef = viewImage.CGImage;
CGRectturnRect = rect;
CGImageRef imageRefRect =CGImageCreateWithImageInRect(imageRef, turnRect);
UIImage*snapshotImage = [[UIImagealloc]initWithCGImage:imageRefRect];
CGImageRelease(imageRef);/// 不需要释放,否则会造成过度释放.
CGImageRelease(imageRefRect);
看了以上代码发现2个问题:
<1>对象imageRef,实际上是viewImage的属性CGImage,而这个属性由viewImage管理,并不需要在当前方法释放;
<2>imageRefRect是在这个方法里create的,是需要Release的;
<3>C类对象的内存管理,不是由ARC管理的,所以需要考虑手动管理内存;
问题比较容易发现,但是CGImageCreateWithImageInRect的声明里有这样一段注释:
The resulting image retains a reference to the original image, so you may
release the original image after calling this function.
这句英文还是有一定迷惑性的,我理解是这样的:这个方法返回传入image的引用,所以在调用这个方法之后,你可以释放这个image对象.英文单词用的may,意思应该是"可以"但不是必须.也就是我已经持有了,你可以释放,对我不影响.(但是我觉得他还有一句话没说,这个引用也是需要release的)(欢迎拍砖,说实话我感觉这句话挺奇怪的,如果有更合理的解释,请不吝赐教)
回到上述问题,imageRef对于方法CGImageCreateWithImageInRect就是那个original image是但是,imageRef是viewImage的属性,什么时候释放或者如何释放由viewImage管理,不需要当前方法操作.
但是imageRefRect是需要当前方法在不使用的时候release掉的.
因此这段代码里同时调用了:
CGImageRelease(imageRef);/// 不需要释放,否则会造成过度释放.
CGImageRelease(imageRefRect);那么就过度释放了.
但是并不是一定马上crash,因为imageRef的管理者,可能会延期调用release,在调用之前是不会crash的,但是当这个调用时期到来的时候就crash掉了.这就是为什么这个crash是具有不确定性的.
那么解决办法就是CGImageRelease调用一次就可以了.
另外一个问题
有人认为,imageRef和imageRefRect是一回事,
CGImageRelease 掉其中任何一个就可以,都没问题.我认为不是,分析如下:
由apple api:CGImageCreateWithImageInRect的声明可以看出,imageRef和imageRefRect的确都是对同一个对象的引用,也就是说他们指向的是同一个对象,但是意义不同.
正如上文所说,imageRef的管理者是viewImage, 引用imageRefRect的管理者才是当前方法,所以在当前方法中应该CGImageRelease掉imageRefRect.这样比较安全.
我进行了2个方面的测试验证:
<1>很简单:注释掉CGImageCreateWithImageInRect那么CGImageRelease(imageRef)会crash.
<2>如果CGImageCreateWithImageInRect传入的参数不正确,返回了0x00,CGImageRelease(imageRef)也会crash.(函数执行失败,实际上没有真正引用imageRef)
显然,imageRef和imageRefRect并不是等价的.
Demo
相关工程里的代码肯定是不方便公开的,不过我在github上传了我的Demo代码.大家可以参考.crashDemo
网友评论