I'm trying to optimize some image filtering by using a singleton object that contains the image filter for me to use anywhere. I am running into an issue where I believe I am calling the filter several times at once (several cells are displayed, after downloading image, before display, I am processing the image with a filter, which may occur pretty much at the same time).
I run into this issue:
NSAssert(framebufferReferenceCount > 0, #"Tried to overrelease a framebuffer, did you forget to call -useNextFrameForImageCapture before using -imageFromCurrentFramebuffer?");
https://github.com/BradLarson/GPUImage/blob/master/framework/Source/GPUImageFramebuffer.m#L269
Here's my code:
Interface:
#import <GPUImage/GPUImage.h>
#interface GPUImageFilterManager : NSObject
+ (GPUImageFilterManager*)sharedInstance;
#property (nonatomic, strong) GPUImageiOSBlurFilter *blurFilter;
#property (nonatomic, strong) GPUImageLuminanceThresholdFilter *luminanceFilter;
#end
Implementation:
#import "GPUImageFilterManager.h"
#implementation GPUImageFilterManager
+ (GPUImageFilterManager*)sharedInstance {
static GPUImageFilterManager *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [GPUImageFilterManager new];
});
return _sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
self.luminanceFilter = [[GPUImageLuminanceThresholdFilter alloc] init];
self.luminanceFilter.threshold = 0.5;
self.blurFilter = [[GPUImageiOSBlurFilter alloc] init];
[self.blurFilter setBlurRadiusInPixels:BLUR_RADIUS];
[self.blurFilter setSaturation:SATURATION];
[self.blurFilter setDownsampling:DOWNSAMPLING];
[self.blurFilter setRangeReductionFactor:RANGEREDUCTION];
}
return self;
}
-(GPUImageiOSBlurFilter*)blurFilter {
[_blurFilter useNextFrameForImageCapture];
return _blurFilter;
}
-(GPUImageLuminanceThresholdFilter*)luminanceFilter {
[_luminanceFilter useNextFrameForImageCapture];
return _luminanceFilter;
}
And in my code I call:
[[[GPUImageFilterManager sharedInstance] blurFilter] imageByFilteringImage:image];
Previously, I had a GPUImageLuminanceThresholdFilter property in each cell and I wanted to optimize this to just use a single instance of it, but it now seems I won't be able to process multiple images at once. Any ideas or advice here?
My guess about what's going wrong here is that -imageByFilteringImage: isn't really an atomic operation, and if you're triggering it on multiple threads at the same time, you're going to get bizarre behavior. Take a look at what happens inside that method (via the -newCGImageByFilteringCGImage: method):
- (CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter;
{
GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithCGImage:imageToFilter];
[self useNextFrameForImageCapture];
[stillImageSource addTarget:(id<GPUImageInput>)self];
[stillImageSource processImage];
CGImageRef processedImage = [self newCGImageFromCurrentlyProcessedOutput];
[stillImageSource removeTarget:(id<GPUImageInput>)self];
return processedImage;
}
A GPUImagePicture is created, attached to the filter, processed, and the output extracted. If you're using the same filter across multiple threads, and you call this method, odds are that you're going to interrupt this at various points and create bizarre filtering pathways. Thus the error above (as well as other potential crashes and image corruption).
If you want to use this filter in a singleton, my recommendation would be to set up a serial dispatch queue and wrap accessed to that filter in dispatches to that queue. In particular, I'd wrap synchronous dispatches to this queue around your -imageByFilteringImage: above to guarantee that this always fully executes and returns an image before being triggered again by a different image and thread.
The GPUImage OpenGL ES context can only perform a single rendering operation at a time, so you don't lose much by limiting access to the filter to only one thread at a time. It's also pretty simple to implement, and I use this pattern of limiting access to shared resources via GCD queues throughout the framework.
Related
I am needing to use #autoreleasepool because I Am getting some high memory usage. The issue is, the objects that are taking up memory are created in one method on a background thread, then sent to a second method where they are used to update the UI.
- (void)createObjects {
TestObject = *test = [[TestObject alloc] init];
[self updateUIWithTestObject:test];
}
- (void)updateUIWithTestObject:(TestObject *)testObject {
// Update UI
self.textLabel.text = testObject.text;
}
Where would I be able to place the #autoreleasepool so that I can release the objects that I have created?
I know about using dispatch_barrier_async to lock a given resource, but in my case it isn't a good candidate because I am not modifying a shared data structure, rather a resource on disk and don't want to block the whole queue, rather just a given key as the action could take a long time. I'm not certain how the file system works pertaining to accessing the same file (by name) from several threads simultaneously and couldn't find a clear answer in the documentation, just best practices. I think I would like to lock by "file name" - and am missing a method "tryLock(key)"
Something like:
-(void)readFileAtPath:(NSString *)path completion:(void(^)(NSData *fileData))completion
{
dispatch_async(self.concurrentQueue,^{
// acquire the lock for a given key and block until can acquire
trylock(path);
NSData *fileData = [self dataAtPath:path];
unlock(path);
completion(fileData);
});
}
-(void)writeData:(NSData *)data toPath:(NSString *)path completion:(void(^)())completion
{
dispatch_async(self.concurrentQueue,^{
// if someone is reading the data at 'path' then this should wait - otherwise should write
trylock(path);
[data writeToFile:path atomically:YES];
unlock(path);
completion();
});
}
EDIT:
Does #synchronized do this? Is this a proper use case?
If you want to create "scoped queues", just do it. Create a serial queue for each file, and have them target your concurrent queue. It might look like this:
#interface Foo : NSObject
#property (readonly) dispatch_queue_t concurrentQueue;
#end
#implementation Foo
{
NSMutableDictionary* _fileQueues;
dispatch_queue_t _dictGuard;
}
#synthesize concurrentQueue = _concurrentQueue;
- (instancetype)init
{
if (self = [super init])
{
_concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
_dictGuard = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
_fileQueues = [[NSMutableDictionary alloc] init];
}
return self;
}
- (dispatch_queue_t)queueForFile: (NSString*)path
{
__block dispatch_queue_t retVal = NULL;
dispatch_sync(_dictGuard, ^{
retVal = _fileQueues[path];
if (!retVal)
{
retVal = dispatch_queue_create(path.UTF8String, DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(retVal, self.concurrentQueue);
_fileQueues[path] = retVal;
}
});
return retVal;
}
- (void)doStuff: (id)stuff withFile: (NSString*)path
{
dispatch_queue_t fileQueue = [self queueForFile: path];
dispatch_async(fileQueue, ^{
DoStuff(stuff, path);
});
}
#end
That said, this queue-per-file thing has a little bit of a "code smell" to it, especially if it's intended to improve I/O performance. Just off the top of my head, for max performance, it feels like it would be better to have a queue per physical device than a queue per file. It's not generally the case that you as the developer know better than the OS/system frameworks how to coordinate file system access, so you will definitely want to measure before vs. after to make sure that this approach is actually improving your performance. Sure, there will be times when you know something that the OS doesn't know, but you might want to look for a way to give the OS that information rather than re-invent the wheel. In terms of performance of reads and writes, if you were to use dispatch_io channels to read and write the files, you would be giving GCD the information it needed to best coordinate your file access.
It also occurs to me that you also might be trying to 'protect the application from itself.' Like, if you were using the disk as a cache, where multiple tasks could be accessing the file at the same time, you might need to protect a reader from another writer. If this is the case, you might want to look for some existing framework that might address the need better than rolling your own. Also, in this use case, you might want to consider managing your scope in-application, and just mmaping one large file, but the cost/benefit of this approach would depend on the granule size of your files.
It would be hard to say more without more context about the application.
To your follow-on question: #synchronized could be used to achieve this, but not without much the same mechanics required as posted above for the GCD way. The reason for this is that #synchronized(foo) synchronizes on foo by identity (pointer equality) and not value equality (i.e. -isEqual:), so NSString and NSURL (the two most obvious objects used to refer to files) having value semantics, makes them poor candidates. An implementation using #synchronized might look like this:
#interface Bar : NSObject
#property (readonly) dispatch_queue_t concurrentQueue;
#end
#implementation Bar
{
NSMutableDictionary* _lockObjects;
dispatch_queue_t _dictGuard;
}
- (instancetype)init
{
if (self = [super init])
{
_concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
_dictGuard = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
_lockObjects = [[NSMutableDictionary alloc] init];
}
return self;
}
#synthesize concurrentQueue = _concurrentQueue;
- (id)lockForFile: (NSString*)path
{
__block id retVal = NULL;
dispatch_sync(_dictGuard, ^{
retVal = _lockObjects[path];
if (!retVal)
{
retVal = [[NSObject alloc] init];
_lockObjects[path] = retVal;
}
});
return retVal;
}
- (void)syncDoStuff: (id)stuff withFile: (NSString*)path
{
id fileLock = [self lockForFile: path];
#synchronized(fileLock)
{
DoStuff(stuff, path);
}
}
- (void)asyncDoStuff: (id)stuff withFile: (NSString*)path
{
id fileLock = [self lockForFile: path];
dispatch_async(self.concurrentQueue, ^{
#synchronized(fileLock)
{
DoStuff(stuff, path);
}
});
}
#end
You'll see that I made two methods to do stuff, one synchronous and the other asynchronous. #synchronized provides a mutual exclusion mechanism, but is not an asynchronous dispatch mechanism, so if you want parallelism, you still have to get that from GCD (or something else.) The long and short of it is that while you can use #synchronized to do this, it's not a good option these days. It's measurably slower than equivalent GCD mechanisms. About the only time #synchronized is useful these days is as a syntactic shortcut to achieve recursive locking. That said, many smart folks believe that recursive locking is an anti-pattern. (For more details on why, check out this link.) The long and short of it is that #synchronized is not the best way to solve this problem.
I've wrote a class which gets an image from the camera. Its header is as follows:
typedef void(^ImageTakenCallback)(UIImage *image);
#interface ImageGetter : NSObject <UIImagePickerControllerDelegate, UIPopoverControllerDelegate>
{
UIImagePickerController *picker;
ImageTakenCallback completionBlock
}
-(void) requestImageInView:(UIView*)view withCompletionBlock:(void(^)(UIImage*))completion;
#end
As you can see, I'm trying to make something like that in client code:
[[[ImageGetter alloc] init] requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
}];
Here is how I've implemented ImageGetter:
-(void) requestImageInView:(UIView*)view withCompletionBlock:(ImageTakenCallback)completion
{
completionBlock = [completion copy];
picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.delegate = self;
[view addSubview:picker.view];
}
- (void)imagePickerController:(UIImagePickerController *)picker_
didFinishPickingImage:(UIImage *)image
editingInfo:(NSDictionary *)editingInfo
{
[picker.view removeFromSuperview];
picker = nil;
completionBlock(image);
}
The problem is since I'm using ARC, the instance of ImageGetter is deallocated instantly after call for -requestImage..., so the weak delegate of picker becomes nil.
Which are common ways to resolve such a issue?
I can see some ways, however, none of them seems to be quite right:
retain ImageGetter from client code, for example, assign it to a strong property. The problems here are: I wont be able to release it by setting this property to nil right after I get image, because this will mean setting retain count of object to 0 while executing the method of this object. Also, I don't want unnecessary properties (well, it is not a big problem, but nevertheless).
disable ARC for ImageGetter and manually retain at start itself and release after sending image to callback.
make static manager ImageGetterManager, which will have method requestImage..., it will create ImageGetter instances, retain them, redirect the requestImage... call, get callbacks from them and release. That seems the most consistent way, but is not it a bit complex for such a little code?
So how can I build such a class?
You can handle that within the ImageGetter class by creating and releasing a "self-reference".
In a class extension in the implementation file, declare a property
#interface ImageGetter ()
#property (strong, nonatomic) id selfRef;
#end
In requestImageInView:, set self.selfRef = self to prevent deallocation.
In the completion method, set self.selfRef = nil.
Remark: Actually you can manage the retain count even with ARC:
CFRetain((__bridge CFTypeRef)(self)); // Increases the retain count to prevent deallocation.
CFRelease((__bridge CFTypeRef)(self)); // Decreases the retain count.
But I am not sure if this is considered "good programming" with ARC or not.
Any feedback is welcome!
If this issue is introduced when switching to ARC, I should just go for option 1, and define it as a strong property.
However the behaviour is a bit different than you described for option 1: Setting the property to nil, does NOT mean the object is instantly released, it will just cause a decrement of the retaincount. ARC will handle that fine, the object will be released as soon as all referenced objects have 'released' it.
You can use the following strategy:
ImageGetter* imgGetter = [[ImageGetter alloc] init];
[imgGetter requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
[imgGetter releaseCompletionBlock]; // With this line, the completion block will retain automatically imgGetter, which will be released after the release of the completionBlock.
}];
Inside your ImageGetter implementation class, create a method that you can call inside the block like this.
-(void) releaseCompletionBlock
{
completionBlock = nil;
}
I've seen several different approaches to memory management in iOS as regards releasing properties. After some debate with colleagues, the pros and cons have become muddled in my head.
I'm hoping to get a summary of pros and cons that will allow myself and others to easily choose a default approach while still understanding when to make exceptions. Here are the 3 variations I've seen:
Assume #property (nonatomic, retain) MyObject *foo;
// Release-only. Seems to be the favored approach in Apple's sample code.
- (void)dealloc {
[foo release];
[super dealloc];
}
// Property accessor set to nil.
- (void)dealloc {
self.foo = nil;
[super dealloc];
}
// Release, then nil.
- (void)dealloc {
[foo release];
foo = nil;
[super dealloc];
}
If you have a different variation to add, comment here and I'll edit the op.
Versions (1): is the best. Each of the others have attributes that may be harmful.
Version (2): It is generally advised not to use accessors in dealloc (or init). The reasoning behind this is that the object is in the process of being torn-down (or created) and is in an inconsistent state. This is especially true if you are writing a library where someone else may later override an accessor unaware that it may be called when the object is in an inconsistent state. (Of course even Apple sometimes breaks this rule -[UIView initWithFrame:] calls -[UIView setFrame:] if the argument is not CGRectZero which can make for fun debugging.
Version (3); Setting the ivar to nil serves no useful purpose, indeed it may mask an error and make debugging more difficult. To see why this is true, consider the following piece of code, assume myObject has a version (3) dealloc.
FastMovingTrain* train = [[FastMoving alloc] init];
MyObject* myObject = [[MyObject alloc] init];
myObject.foo = train;
[train release];
// my myObject.foo is the only thing retaining train
...
....
[myObject release];
// Because of version (3) dealloc if myObject
// points to the dealloced memory this line
// will silently fail...
[myObject.foo applyBrakes];
Interestingly enough this code provides an opportunity to demonstrate when setting a variable to nil after a release does make sense. The code can be made more resilient by modifying it as follows.
FastMovingTrain* train = [[FastMoving alloc] init];
MyObject* myObject = [[MyObject alloc] init];
myObject.foo = train;
[train release];
// my myObject.foo is the only thing retaining train
...
....
[myObject release];
myObject = nil;
// This assertion will fail.
NSAssert(myObject, #"myObject must not be nil");
[myObject.foo applyBrakes];
Just my $0.02.
I am new to iPhone programming and Objective-C.
I am building a View-based application.
The problem is that none of the UIViewController's dealloc functions are ever called.
I have decided to unload all my retained objects programmaticaly, right before presenting the next UIViewController class.
I have resolved all the leaks detected by Xcode, tested the application with Leaks and Allocations Tools and everything seams OK, but the memory used builds up to around 180 MB and the app crashes.
The self.retainCount before presenting the next UIViewController is 1, the objects owned are released and the pointers are nil, but still the memory builds up.
Can you give me a clue? If needed I will post some code samples. Or can you send me some references to something like Objective-C memory management 101?
This is my interface:
#interface GameScreen : UIViewController
{
IBOutlet UILabel *timeLabel;
IBOutlet UIView *gameView;
IBOutlet UIImageView *gameViewFrame;
IBOutlet UIButton *showOutlineButton;
UIImage *puzzleImage;
UIView *optionsView;
UIView *blockView;
NSTimer *timer;
UIImageView *viewOriginalPicture;
SHKActivityIndicator *activityIndicator;
BOOL originalPictureShown;
BOOL outLineShown;
}
#property (nonatomic, retain) IBOutlet UILabel *timeLabel;
#property (nonatomic, retain) IBOutlet UIView *gameView;
#property (nonatomic, retain) IBOutlet UIImageView *gameViewFrame;
#property (nonatomic, retain) IBOutlet UIButton *showOutlineButton;
#property (nonatomic, retain) UIImage *puzzleImage;
And here is the implementation:
- (id) initWithPuzzleImage: (UIImage *) img
{
if((self = [super init]))
{
puzzleImage = [[UIImage alloc] init];
puzzleImage = img;
}
return self;
}
This is the function called when the user taps the exit button:
- (void) onExit
{
[timer invalidate];
CurrentMinuts = 0;
CurrentSeconds = 0;
//remove piece configurations
[pieceConfigMatrix removeAllObjects];
[pieceFramesMatrix removeAllObjects];
PuzzleViewController *modalView = [[PuzzleViewController alloc] init];
[self unloadObjects];
[self presentModalViewController:modalView animated:YES];
[modalView release];
}
And the unloadObjects function:
- (void) unloadObjects
{
[self resignFirstResponder];
[viewOriginalPicture release];
viewOriginalPicture = nil;
[timeLabel release];
timeLabel = nil;
[gameView release];
gameView = nil;
[originalImage release];
originalImage = nil;
[gameViewFrame release];
gameViewFrame = nil;
[timer release];
[showOutlineButton release];
showOutlineButton = nil;
}
I have a lead of what I do wrong, but I am not sure. Let me explain. I am adding the puzzle pieces to the 'gameView' property. For this, I have a 'SplitImage' object. The following function is called in - (void) viewDidLoad:
- (void) generatePuzzleWithImage:(UIImage *) image
{
SplitImage *splitSystem = [[SplitImage alloc] initWithImage:image andPuzzleSize:gPuzzleSize];
[splitSystem splitImageAndAddToView:self.gameView];
[splitSystem release];
}
Next, initialization function for the SplitImage class and the splitImageAndAddToView function:
- (id) initWithImage: (UIImage *) image andPuzzleSize: (int) pSize
{
if((self = [super init]))
{
UIImage *aux = [[[UIImage alloc] init] autorelease];
pieceCenterSize = [SplitImage puzzlePieceSizeForNumberOfPieces:pSize];
UIImage *outSideBallSample = [UIImage imageNamed:#"sampleHorizontal.jpg"]; //convexity size for puzzle size
outSideBallSample = [outSideBallSample resizedImageWithContentMode:UIViewContentModeScaleAspectFit bounds:CGSizeMake(pieceCenterSize, pieceCenterSize) interpolationQuality:kCGInterpolationHigh];
outSideBallSize = roundf(outSideBallSample.size.height);
puzzleSize = pieceCenterSize * pSize;
pieceNumber = pSize;
if(image.size.height < puzzleSize || image.size.height > puzzleSize || image.size.width < puzzleSize || image.size.width > puzzleSize)
{
aux = [SplitImage resizeImageForPuzzle:image withSize:puzzleSize];
aux = [SplitImage cropImageForPuzzle:aux withSize:puzzleSize];
}
aux = [aux imageWithAlpha];
originalImage = [[UIImage imageWithCGImage:aux.CGImage] retain];
mainImage = aux.CGImage;
imageSize = CGSizeMake(aux.size.width, aux.size.height);
NSLog(#"%#", NSStringFromCGSize(imageSize));
splitImageSize = CGSizeMake(pieceCenterSize + 2*outSideBallSize, pieceCenterSize+2*outSideBallSize);
}
return self;
}
- (void) splitImageAndAddToView: (UIView *) view
{
for (int i = 0; i < pieceNumber; i++)
for(int j = 0; j < pieceNumber; j++)
//some code
UIImage *mask;
mask = [self randomlyRetriveMaskWithPrefix:1 forPieceAtI:i andJ:j];
CGImageRef split = CGImageCreateWithImageInRect(mainImage, cuttingRect);
PuzzlePiece *splitView = [[PuzzlePiece alloc] initWithImage:[UIImage imageWithCGImage:split] andMask:mask centerSize:pieceCenterSize objectMatrixPosition:i*pieceNumber+j outSideBallSize:outSideBallSize pieceType:pieceType pieceFrame:cuttingRect];
[pieceFramesMatrix addObject:[NSValue valueWithCGRect:cuttingRect]];
[splitView setTag:(i+1)*100+j];
[view addSubview:splitView];
CGImageRelease(split);
[splitView release];
}
Thank you,
Andrei
Objective-C memory management 101 is here:
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
Ignore the stuff on garbage collection, it isn't available for iOS.
It's not normal to release objects before presenting the next UIViewController.
Assuming you have a NIB, its contents will be loaded the first time that you (or the system) accesses the view member. Your view controller will get a call to loadView, then subsequently to viewDidLoad. If you have any views you want to add programmatically, you can do that in loadView or viewDidLoad, if you want to interrogate objects loaded from the NIB then you can do that in viewDidLoad.
Once loaded, the view will remain in memory unless and until a low memory warning occurs. At that point it'll be released. You'll get viewDidUnload. You should release anything view related that you've still got an owning reference to in there, and set to nil any weak references you may have.
Any attempt to access the view property subsequently will cause the NIB to be reloaded, etc.
So, when presenting a new UIViewController, just present it. If you create objects that are used only for display of that controller then do so on viewDidLoad, and release them on viewDidUnload.
That all being said, the Leaks tool in Instruments should be able to tell you which types of object are leaking and where you first allocated them, so it makes finding leaks really quite easy. The only thing to watch out for is that if one object handles its properties/members entirely correctly but is itself leaked then anything it creates will generally also leak. So when something leaks, check the thing that created it isn't also leaking before tearing your hair out over why you can't find a problem.
First, retainCount is useless.
Activity Monitor is pretty close to just as useless. As well, the behavior in the simulator can be quite different on the memory use front. Better to focus debugging of a problem like this on the device.
Next, this:
The problem is that none of the
UIViewController's dealloc functions
are ever called. I have decided to
unload all my retained objects
programmaticaly, right before
presenting the next UIViewController
class.
Any time you find yourself programmatically working around incorrect behavior, you are just creating more bugs.
Step back from your custom hack and figure out why the dealloc isn't being called. Something somewhere is over-retaining the object. The allocations instrument with retain tracking turned on will show you exactly where all retains and releases are sent to the errant objects.
Leaks likely won't show anything if whatever is retaining the objects is still reachable from a global or the stack (i.e. leaks are objects that can never be used by your program again -- but there are many more ways to explode memory use without it being truly a leak).
You should also "Build and Analyze", then fix any problems it identifies.
Next, if you are seeing memory accretion of repeated operations on the user's part, then Heapshot analysis is extremely effective at figure out exactly what has gone wrong.
Some specific comments:
puzzleImage = [[UIImage alloc] init];
puzzleImage = img;
2 bugs; you are leaking a UIImage and not retaining img. That your app doesn't crash in light of the above code indicates that there is likely an over-retain elsewhere.
retainCount is not a reliable debugging tool.
You should never pay attention to or rely on retainCount. Just because you released it does not mean that some part of the program does not still have a reference to it. retainCount has no value.
Generally as a rule of thumb. If you use 'alloc' you must 'release' at some point.
Unless ofcourse you have put it into an autorelease pool.
Leaks should be able to point you to the objects that are leaking, using that, narrow down to where those objects are added, stored etc.
Post examples of your code on how you instantiate objects and where you release them, you maybe doing something wrong early on.
edit: apologies, i put 'init' not 'alloc' previously, thank you dreamlax, early morning mistake.