iphone - Memory warning results in crash when creating layers with images -


i'm creating app takes photo each second , saves image disk. when enough photos has been taken, app creates video of images using avfoundation framework. done creating layer each image, add animation hides layer after time combine these layers single layer held avvideocompositioncoreanimationtool. after creating avvideocomposition holds animation tool, end using avassetexportsession export avmutablecomposition.

however, while creating layers app give 2 memeory warnings , crash if have 50-60 photos or more. use below code create layers. means 60 images, below loop run 60 times. once each image. imagelayer created contents of photo, animation applied layer , layer added sublayer layer.

calayer *layer = [calayer layer]; layer.frame = cgrectmake(0.0f, 0.0f, rendersize.width, rendersize.height); layer.bounds = cgrectmake(0.0f, 0.0f, rendersize.width, rendersize.height);  (nsstring *imagepath in imagepaths) {     cmtime endtime = cmtimeadd(cursortime, cmtimemake(1.0f, (cgfloat)fps));      uiimage *theimage = [[uiimage alloc] initwithcontentsoffile:imagepath];     cgfloat scalefactor = min(rendersize.width / theimage.size.width, rendersize.height / theimage.size.height);     cgsize newsize = cgsizemake(theimage.size.width * scalefactor, theimage.size.height * scalefactor);      uiimage *resizedimage = [theimage resizedimagetosize:newsize];     cgimageref image = resizedimage.cgimage;     cgsize imagesize = resizedimage.size;      calayer *imagelayer = [calayer layer];     imagelayer.frame = cgrectmake((rendersize.width - imagesize.width) * 0.50f, (rendersize.height - imagesize.height) * 0.50f, imagesize.width, imagesize.height);     imagelayer.bounds = cgrectmake((rendersize.width - imagesize.width) * 0.50f, (rendersize.height - imagesize.height) * 0.50f, imagesize.width, imagesize.height);     imagelayer.contents = (__bridge id)image;     imagelayer.begintime = (cgfloat)cursortime.value / (cgfloat)cursortime.timescale;      cabasicanimation *hideanimation = [cabasicanimation animationwithkeypath:@"hidden"];     hideanimation.fromvalue = @(no);     hideanimation.tovalue = @(yes);     hideanimation.additive = no;     hideanimation.removedoncompletion = no;     hideanimation.begintime = (cgfloat)endtime.value / (cgfloat)endtime.timescale;     hideanimation.fillmode = kcafillmodeboth;     [imagelayer addanimation:hideanimation forkey:nil];     [layer addsublayer:imagelayer];      cursortime = endtime; } 

it seems code use memory. can tell me how can optimize code or in other way avoid memory warnings , avoid crash?

update per daij-djans answer.

the autoreleasepool did not change thing. tried writing custom layer , draw image myself. allow me render more images still ends giving memory warnings , crashing app. turns out -drawincontext: not called unless -setneedsdisplay called on layer. class looks following:

photolayer.h

#import <quartzcore/quartzcore.h>  @interface photolayer : calayer  @property (nonatomic, copy) nsstring *path;  @end 

photolayer.m

#import "photolayer.h" #import <imageio/imageio.h>  @implementation photolayer  - (void)drawincontext:(cgcontextref)context {     [super drawincontext:context];      // retrieve image file path     cgdataproviderref dataprovider = cgdataprovidercreatewithcfdata((__bridge cfdataref)[nsdata datawithcontentsoffile:self.path]);     cgimageref image = cgimagecreatewithjpegdataprovider(dataprovider, null, true, kcgrenderingintentdefault);     cgsize imagesize = cgsizemake(cgimagegetwidth(image), cgimagegetheight(image));      // create transformation orientation     cgimagesourceref source = cgimagesourcecreatewithdataprovider(dataprovider, null);     cfdictionaryref dictionaryref = cgimagesourcecopypropertiesatindex(source, 0, null);     cgaffinetransform transform;     nsinteger orientation = [(nsnumber *)cfdictionarygetvalue(dictionaryref, kcgimagepropertyorientation) integervalue];     switch (orientation) {         case 1:             // flip vertically             transform = cgaffinetransformmake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, self.bounds.size.height);             break;         case 3:             // flip horizontally             transform = cgaffinetransformmake(-1.0f, 0.0f, 0.0f, 1.0f, self.bounds.size.width, 0.0f);             break;         case 6:             // rotate 90 degrees , flip vertically             transform = cgaffinetransformmake(0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f);             break;         case 8:             // rotate -90 degrees , flip vertically             transform = cgaffinetransformmake(0.0f, -1.0f, -1.0f, 0.0f, self.bounds.size.height, self.bounds.size.width);             break;         default:             // unknown orientation, consider landscape right             transform = cgaffinetransformmake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, self.bounds.size.height);             nslog(@"unknown orientation when drawing image in layer.");             break;     }      // calculate amount scale image     cgfloat scalefactor = min(self.bounds.size.width / imagesize.width, self.bounds.size.height / imagesize.height);      // calculate new size , positiondepending on orientation     cgsize newsize = cgsizezero;     cgpoint pos = cgpointzero;     if (orientation == 1 || orientation == 3)     {         newsize = cgsizemake(imagesize.width * scalefactor, imagesize.height * scalefactor);         pos = cgpointmake((self.bounds.size.width - newsize.width) * 0.50f, (self.bounds.size.height - newsize.height) * 0.50f);     }     else     {         imagesize = cgsizemake(imagesize.height, imagesize.width);         newsize = cgsizemake(imagesize.height * scalefactor, imagesize.width * scalefactor);         pos = cgpointmake((self.bounds.size.height - newsize.width) * 0.50f, (self.bounds.size.width - newsize.height) * 0.50f);     }      // apply transformation     cgcontextconcatctm(context, transform);      // draw image     cgcontextdrawimage(context, cgrectmake(pos.x, pos.y, newsize.width, newsize.height), image);      // clean     cgdataproviderrelease(dataprovider);     cgimagerelease(image);     cfrelease(source);     cfrelease(dictionaryref); }  - (void)dealloc {     _path = nil; }  @end 

i use layer below. instead of setting contents of layer, set path image , draw it. draw it, need call -setneedsdisplay.

calayer *layer = [calayer layer]; layer.frame = cgrectmake(0.0f, 0.0f, rendersize.width, rendersize.height); layer.bounds = cgrectmake(0.0f, 0.0f, rendersize.width, rendersize.height);  (nsstring *imagepath in imagepaths) {     @autoreleasepool {         cmtime endtime = cmtimeadd(cursortime, cmtimemake(1.0f, (cgfloat)fps));          photolayer *photolayer = [[photolayer alloc] init];         photolayer.path = imagepath;         photolayer.frame = cgrectmake(0.0f, 0.0f, rendersize.width, rendersize.height);         photolayer.bounds = cgrectmake(0.0f, 0.0f, rendersize.width, rendersize.height);         photolayer.begintime = (cgfloat)cursortime.value / (cgfloat)cursortime.timescale;          cabasicanimation *hideanimation = [cabasicanimation animationwithkeypath:@"hidden"];         hideanimation.fromvalue = @(no);         hideanimation.tovalue = @(yes);         hideanimation.additive = no;         hideanimation.removedoncompletion = no;         hideanimation.begintime = (cgfloat)endtime.value / (cgfloat)endtime.timescale;         hideanimation.fillmode = kcafillmodeboth;         [photolayer addanimation:hideanimation forkey:nil];         [layer addsublayer:photolayer];          [photolayer setneedsdisplay];          cursortime = endtime;     } }  calayer *parentlayer = [calayer layer]; calayer *videolayer = [calayer layer]; parentlayer.frame = cgrectmake(0.0f, 0.0f, rendersize.width, rendersize.height); videolayer.frame = cgrectmake(0.0f, 0.0f, rendersize.width, rendersize.height); [parentlayer addsublayer:videolayer]; [parentlayer addsublayer:layer]; 

when number of imagepaths large, each imagepath load original, make new resized image, layer , animation.

thats lot of memory accumulate on time.

  1. you should try , wrap content of loop inside @autoreleasepool {...}

if not enough,

i'd lazily load images. make subclass of calayer, pass url , let load when displayed! (implement in drawincontext: in subclaass

something like

 @myurllayer : calayer  @property(copy) nsstring *path;  @end   @implementation myurllayer  - (void)drawincontext:(cgcontextref)ctx {     //load image , draw it!     //...  }  @end 

btw if not using arc leaking originalimage , thats prob


Comments

Popular posts from this blog

Ansible - ERROR! the field 'hosts' is required but was not set -

customize file_field button ruby on rails -

SoapUI on windows 10 - high DPI/4K scaling issue -