GPUImage and GPUImageView: application terminated due to a memory error

I use GPUImage and many instances of GPUImageView. The goal is to display the original image, overlay a few fragments of the filtered images on top, and finally revitalize the cut filters slowly over the original image. Imagine an image in which some sepia stripes roll across to show a normal image and a sepia image in cross sections.

I wrapped this functionality in a subclass of UIView, as shown below:

import Foundation import QuartzCore class FilteredImageMaskView : UIView { init(frame: CGRect, image: UIImage){ super.init(frame: frame); let imageViewFrame = CGRectMake(frame.origin.x, 0.0, frame.size.width, frame.size.height); let origImage = GPUImagePicture(image: image); origImage.forceProcessingAtSizeRespectingAspectRatio(imageViewFrame.size); // Display the original image without a filter let imageView = GPUImageView(frame: imageViewFrame); origImage.addTarget(imageView); origImage.processImageWithCompletionHandler(){ origImage.removeAllTargets(); var contentMode = UIViewContentMode.ScaleAspectFit; imageView.contentMode = contentMode; // Width of the unfiltered region let regularWidth: CGFloat = 30.0; // Width of filtered region let filterWidth: CGFloat = 30.0; // How much we are moving each bar let totalXMovement = (regularWidth + filterWidth) * 2; // The start X position var currentXForFilter: CGFloat = -totalXMovement; // The filter being applied to an image let filter = GPUImageSepiaFilter(); filter.intensity = 0.5; // Add the filter to the originalImage origImage.addTarget(filter); let filteredViewCollection = FilteredViewCollection(filteredViews: [GPUImageView]()); // Iterate over the X positions until the whole image is covered while(currentXForFilter < imageView.frame.width + totalXMovement){ let frame = CGRectMake(currentXForFilter, imageViewFrame.origin.y, imageViewFrame.width, imageViewFrame.height); var filteredView = GPUImageView(frame: frame); filteredView.clipsToBounds = true; filteredView.layer.contentsGravity = kCAGravityTopLeft; // This is the slice of the overall image that we are going to display as filtered filteredView.layer.contentsRect = CGRectMake(currentXForFilter / imageViewFrame.width, 0.0, filterWidth / imageViewFrame.width, 1.0); filteredView.fillMode = kGPUImageFillModePreserveAspectRatio; filter.addTarget(filteredView); // Add the filteredView to the super view self.addSubview(filteredView); // Add the filteredView to the collection so we can animate it later filteredViewCollection.filteredViews.append(filteredView); // Increment the X position currentXForFilter += regularWidth + filterWidth; } origImage.processImageWithCompletionHandler(){ filter.removeAllTargets(); // Move to the UI thread ThreadUtility.runOnMainThread(){ // Add the unfiltered image self.addSubview(imageView); // And move it behind the filtered slices self.sendSubviewToBack(imageView); // Animate the slices slowly across the image UIView.animateWithDuration(20.0, delay: 0.0, options: UIViewAnimationOptions.Repeat, animations: { [weak filteredViewCollection] in if let strongfilteredViewCollection = filteredViewCollection { if(strongfilteredViewCollection.filteredViews != nil){ for(var i = 0; i < strongfilteredViewCollection.filteredViews.count; i++){ strongfilteredViewCollection.filteredViews[i].frame.origin.x += totalXMovement; strongfilteredViewCollection.filteredViews[i].layer.contentsRect.origin.x += (totalXMovement / imageView.frame.width); } } } }, completion: nil); } } } } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder); } } class FilteredViewCollection { var filteredViews: [GPUImageView]! = [GPUImageView](); init(filteredViews: [GPUImageView]!){ self.filteredViews = filteredViews; } } 

The FilteredImageMaskView instance is added programmatically to the viewController. When this viewController is rejected, it is assumed that the resources will be disposed of - I tried to avoid save cycles. When I observe the memory consumption in the debugger on a real device, the memory turns off accordingly when the viewController is turned off. However, if I reload this viewController to look at the image and then release it and then reload it again, I will eventually encounter "Application terminated due to a memory error"

If I wait a while after the viewController is rejected, the memory errors seem less frequent, which leads me to believe that the memory is still freed after the viewController is rejected ...? But I also saw an error after several times not so quickly opening and closing the viewController.

I have to use GPUImage and / or GPUImageView inefficiently and I am looking for guidance.

Thanks!

EDIT: See below for the implementation of a view controller.

 import UIKit class ViewImageViewController: UIViewController, FetchImageDelegate { var imageManager = ImageManager(); @IBOutlet var mainView: UIView! override func viewDidLoad() { super.viewDidLoad() imageManager.fetchImageAsync(delegate: self); } // This callback is dispatched on the UI thread func imageFetchCompleted(imageData: [UInt8]) { let imageView = FilteredImageMaskView(frame: self.mainView.frame, image: UIImage(data: imageData)); mainView.addSubview(imageView); var timer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(10.0), target: self, selector: Selector("displayReminder"), userInfo: nil, repeats: false); } func displayReminder(){ // Show an alert or message here } } class ImageManager { func fetchImageAsync(delegate: FetchImageDelegate) { // This dispatches a high priority background thread ThreadUtility.runOnHighPriorityBackgroundThread() { [weak delegate] in // Get the image (This part could take a while in the real implementation) var imageData = [UInt8](); // Move to the UI thread ThreadUtility.runOnMainThread({ if let strongDelegate = delegate { strongDelegate.imageFetchCompleted(imageData); } }); } } } 

Now when I look at this truncated version, pass self to ImageManager to create a save loop, although I reference it weak ly to the background thread? Can I pass this as a weak link to the right of the ViewImageViewController ? It is possible that the ViewImageViewController rejected before the fetchImageAsync method fetchImageAsync and a callback is called.

EDIT: I think I found the problem. If you look at the ViewImageViewController in the callback, I create NSTimer and pass self. My suspicion is that a save loop is created if the viewController is rejected before the timer runs. This explains why, if I wait a few extra seconds, I don't get a memory error - because the timer is triggering and the viewController is correctly allocated. Here is the fix (I think).

 // This is on the ViewImageViewController var timer: NSTimer!; // Then instead of creating a new variable, assign the timer to the class variable self.timer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(10.0), target: self, selector: Selector("displayReminder"), userInfo: nil, repeats: false); // And finally, on dismiss of the viewcontroller (viewWillDisappear or back button click event, or both) func cancelTimer() { if(self.timer != nil){ self.timer.invalidate(); self.timer = nil; } } 
+6
source share
2 answers

I think I found the problem. If you look at the ViewImageViewController in the callback, I create NSTimer and pass self. My suspicion is that a save loop is created if the viewController is rejected before the timer runs. This explains why, if I wait a few extra seconds, I don't get a memory error - because the timer is triggering and the viewController is correctly allocated. Here is the fix (I think).

 // This is on the ViewImageViewController var timer: NSTimer!; // Then instead of creating a new variable, assign the timer to the class variable self.timer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(10.0), target: self, selector: Selector("displayReminder"), userInfo: nil, repeats: false); // And finally, on dismiss of the viewcontroller (viewWillDisappear or back button click event, or both) func cancelTimer() { if(self.timer != nil){ self.timer.invalidate(); self.timer = nil; } } 
+2
source

FilteredImageMaskView is strictly referenced in the processImageWithCompletionHandler block, which is likely to form a save loop. Try to use a weak self, not in a block

0
source

Source: https://habr.com/ru/post/986340/


All Articles