I am trying to give the user the option to edit hue and brightness using a couple sliders that use CIFilters to affect the image in a UIImageView.
I have it working pretty well, but with one issue: if the sliders are used too much, memory warnings start popping up, and then, if the user keeps on trying after that, the app quits, but there isn't a crash log (??? super confused by this).
I'm still pretty new to programming, and especially new to memory management, but hopefully someone here can help.
Here's my code:
In my .h file:
@property (weak, nonatomic) IBOutlet UISlider *hueSlider;
@property (weak, nonatomic) IBOutlet UISlider *brightnessSlider;
@property (weak, nonatomic) CIImage *outputImage;
@property (strong, nonatomic) CIFilter *hueFilter;
@property (strong, nonatomic) CIFilter *brightnessFilter;
@property (strong, nonatomic) CIContext *ccontext;
@property (strong, nonatomic) CIImage *inputImage;
- (IBAction)hueValueChangedAction:(id)sender;
- (IBAction)brightnessValueChangedAction:(id)sender;
And I also have an instance BOOL defined called "imageFilterUpdating"
Here are the relevant parts of the .m:
- (IBAction)hueValueChangedAction:(id)sender {
if (!imageFilterUpdating) {
[self performSelectorInBackground:@selector(sliderValueChanged) withObject:nil];
}
}
- (IBAction)brightnessValueChangedAction:(id)sender {
if (!imageFilterUpdating) {
[self performSelectorInBackground:@selector(sliderValueChanged) withObject:nil];
}
}
- (void)sliderValueChanged {
imageFilterUpdating = YES;
if (inputImage == nil)
inputImage = [[CIImage alloc] initWithImage:self.imageWithoutOverlayView.image];
if (brightnessFilter == nil) {
brightnessFilter = [CIFilter filterWithName:@"CIExposureAdjust"];
[brightnessFilter setDefaults];
[brightnessFilter setValue: inputImage forKey: @"inputImage"];
}
[brightnessFilter setValue: [NSNumber numberWithFloat:self.brightnessSlider.value] forKey: @"inputEV"];
[self setOutputImage:[brightnessFilter valueForKey: @"outputImage"]];
if (ccontext == nil)
ccontext = [CIContext contextWithOptions:nil];
CIImage *editedInputImage = [[CIImage alloc] initWithCGImage:[ccontext
createCGImage:outputImage
fromRect:outputImage.extent]];
if (hueFilter == nil) {
hueFilter = [CIFilter filterWithName:@"CIHueAdjust"];
[hueFilter setDefaults];
}
[hueFilter setValue: editedInputImage forKey: @"inputImage"];
[hueFilter setValue: [NSNumber numberWithFloat:self.hueSlider.value] forKey: @"inputAngle"];
[self setOutputImage:[hueFilter valueForKey: @"outputImage"]];
UIImage *endImage = [[UIImage alloc] initWithCGImage:[ccontext
createCGImage:outputImage
fromRect:outputImage.extent]];
self.imageWithoutOverlayView.image = endImage;
[self setOutputImage:nil];
imageFilterUpdating = NO;
}
- (void)didReceiveMemoryWarning {
self.brightnessSlider.enabled = NO;
self.hueSlider.enabled = NO;
[super didReceiveMemoryWarning];
while (imageFilterUpdating); // this is because if you set the other parts nil in the middle of updating the image filter, the app crashes
[self setHueFilter:nil];
[self setBrightnessFilter:nil];
[self setCcontext:nil];
[self setOutputImage:nil];
self.brightnessSlider.enabled = YES;
self.hueSlider.enabled = YES;
}
Can anyone help me clear out memory more efficiently so the user can use the sliders infinitely? This code all works fine otherwise.
I've tried to alloc/init things where ever I can, as I've read in a lot of places that ARC deals with that a whole lot better than using things like [UIImage imageNamed:@""], and it seems to have helped some, but the app still crashes after a lot of editing. I've also started reusing instance variables, rather than create new variables every time the method is run, which also seems to help, but it's not 100%.
The UISliders are set to be continuous, and that's something I'd prefer not to change.
Thanks
Every time you run [ccontext createCGImage:outputImage fromRect:outputImage.extent]
you're creating a CGImageRef
that isn't released automatically and isn't managed by ARC. Pull that out into its own variable and release it with CGImageRelease()
after you create editedInputImage
.
CGImageRef tempImage = [ccontext createCGImage:outputImage fromRect:outputImage.extent];
CIImage *editedInputImage = [[CIImage alloc] initWithCGImage:tempImage];
CGImageRelease(tempImage);