美文网首页
关于CGImageRelease的crash

关于CGImageRelease的crash

作者: huaweiluo | 来源:发表于2018-12-12 22:06 被阅读0次

    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

    相关文章

      网友评论

          本文标题:关于CGImageRelease的crash

          本文链接:https://www.haomeiwen.com/subject/eqhmhqtx.html