Analyzer claiming an object was released when it wasn't - objective-c

I am getting a static analysis error in this code which doesn't make any sense to me. The error is:
Reference-counted object is used after it is released
This is glue code to allow for PNG loading in a game originally written in C++.
int pngLoad(const char *filename, pngInfo *info, int format, GLuint *textureName)
{
char fullPath[Engine::Settings::MaxPath];
strcpy(fullPath, filename);
appendAppBundlePath(fullPath);
NSString *path = [NSString stringWithCString:fullPath encoding:NSUTF8StringEncoding];
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:data];
[data release];
Texture2D *loadedTex = [Texture2D alloc];
// ##### Analyzer claims the object is released here: #####
[loadedTex initWithImage:image format:format];
int didLoad;
// ##### Error is here: #####
if (loadedTex.contentSize.width == 0 || loadedTex.contentSize.height == 0)
{
didLoad = 0;
}
else
{
didLoad = 1;
*textureName = loadedTex.name;
// return texture info
info->ScaleFactor = loadedTex.scaleFactor;
info->Width = (float)image.size.width / (float)info->ScaleFactor;
info->Height = (float)image.size.height / (float)info->ScaleFactor;
info->Alpha = 1;
info->PaddedWidth = loadedTex.pixelsWide;
info->PaddedHeight = loadedTex.pixelsHigh;
}
[loadedTex release];
[image release];
return didLoad;
}
If I use Texture2D *loadedTex = [[Texture2D alloc] retain]; this warning is removed, but then an warning that I've leaked an object comes up, so something is seriously weird here.
initWithImage:format: used to contain a [self release] which shouldn't have been there, which I removed when I found this warning. However, even after a full clean and rebuild, I still get the warning. Am I doing something else wrong? Is something not getting properly cleaned up by the Clean command in Xcode?

The analyzer may be right, at least in a general way.
Texture2D *loadedTex = [Texture2D alloc];
[loadedTex initWithImage:image format:format];
In general, "init" might actually discard the object passed in and return a different one. Whether or not this is the case for "Texture2D" is something I don't know, but if the analyzer is going for the general case then it is right.
You should be able to work around that by using
Texture2D *loadedTex = [Texture2D alloc];
loadedTex=[loadedTex initWithImage:image format:format];
Or by simply combining the two calls, as it is done in most Objective-C examples.

You should always combine the alloc and initXXX call when creating objects, in this case
Texture2D *loadedTex = [[Texture2D alloc] initWithImage:image format:format];
An init method need not return the same object it was called with, it is allowed to return a different object.
In this case your result from loadedTex = [Texture2D alloc] would be released and initWithImage would return a different object (which you discard).

Related

Is a value inside an NSDictionary released when removed?

I have something along the line of this :
#implementation ImageLoader
NSMutableDictionary *_tasks;
- (void) loadImageWithURL:(NSURL *)url callback:(SMLoaderCallback)callback {
NSMutableArray *taskList = [_tasks objectForKey:urlString];
if (taskList == nil) {
taskList = [[NSMutableArray alloc] initWithCapacity:5];
[taskList addObject:callback];
[_tasks setObject:taskList forKey:urlString];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [[UIImage alloc] initWithData:data];
for (SMLoaderCallback cb in taskList) {
cb(image, nil);
}
[_tasks removeObjectForKey:url.absoluteString];
});
});
} else {
[taskList addObject:callback];
}
}
#end
In here, I'm trying to queue up image downloads to gain performance (as an exercise only). So I'm keeping a NSDictionary that maps an URL with an array of callbacks to be called whenever the data is downloaded.
Once the image is downloaded, I no longer need this array, so I remove it from the dictionary.
I would like to know if, in this code (with ARC enabled), my array along with the callbacks are correctly released when I call [_tasks removeObjectForKey:url.absoluteString].
If your project uses ARC and that dictionary is the only thing that is referencing to those values, yes. it will be remove permanently.
ARC keeps track of number of objects that is pointing to some other object and it will remove it as soon as the count reaches 0.
so adding to dictionary -> reference count += 1
removing from dictionary -> reference count -= 1
Somewhe

GPUImage memory usage

I noticed that the more I use GPUImage, the more memory my app takes over time (using Instruments to monitor memory use).
As an example, I use each filter in a different methods:
(UIImage*)ToonFilter:(UIImage*)theImage
{
GPUImageSmoothToonFilter *smoothToonFilter = [[GPUImageSmoothToonFilter alloc] init];
[smoothToonFilter setTexelHeight:0.0025];
[smoothToonFilter setTexelWidth:0.0025];
return [smoothToonFilter imageByFilteringImage:theImage];
}
(UIImage*)SketchFilter:(UIImage*)theImage
{
GPUImageSketchFilter *sketchFilter = [[GPUImageSketchFilter alloc] init];
[sketchFilter setTexelHeight:0.003];
[sketchFilter setTexelWidth:0.003];
return [sketchFilter imageByFilteringImage:theImage];
}
(UIImage*)PixellateFilter:(UIImage*)theImage
{
GPUImagePixellateFilter *pixellateFilter = [[GPUImagePixellateFilter alloc] init];
[pixellateFilter setFractionalWidthOfAPixel:0.01;
return [pixellateFilter imageByFilteringImage:theImage];
}
And this is how I use these filters (testImage is a UIImage):
testImage = [self SketchFilter:testImage];
testImage = [self PixellateFilter:testImage];
If I just cycle through these filters over and over, without doing anything else, the app takes more and more memory.
What am I doing wrong? How can I release the memory once I don't need it anymore?
You keep allocating new filters every time you call these functions. Create them once in your view controller etc and send them to your functions like the sample code below (as well as a GPUImagePicture). Also don't forget to call [removeAllTargets] on your filters, sometimes they do not get removed from memory when they have targets
(UIImage*)PixellateFilter:(UIImage*)theImage withPixellateFilter:(GPUImagePixellateFilter *) pixellateFilter andStaticPicture:(GPUImagePicture *)staticPicture
and also use [filter prepareForImageCapture] before getting UIImage from your filters
For example you can build your function like
(UIImage*)PixellateFilter:(UIImage*)theImage withPixellateFilter:(GPUImagePixellateFilter *) pixellateFilter andStaticPicture:(GPUImagePicture *)staticPicture{
[staticPicture removeAllTargets];
UIImage __block *imageToReturn;
[staticPicture addTarget:pixellateFilter];
[staticPicture processImageWithCompletionHandler:^{
[pixellateFilter prepareForImageCapture];
imageToReturn = [pixellateFilter imageFromCurrentlyProcessedOutput];
[pixellateFilter removeAllTargets];
pixellateFilter = nil;
}];
return imageToReturn;
}
I use GPUImageView for my application which allows you to use GPUImageFilters and GPUImagePictures directly without getting UIImage, you should definitely consider it

Leaderboard Requests, Nested Blocks, and Retain Cycles

I have developed a leaderboard display class for my iPhone game. The class has the following instance method.
-(void)displayScoresWithRequest:(CBLeaderboard*)request completionHandler:(void(^)())completionHandler
{
if (request_ != nil)
return;
request_ = [[CBLeaderboard alloc] init];
[request_ setCategory:[request category]];
[request_ setPlayerScope:[request playerScope]];
[request_ setTimeScope:[request timeScope]];
[request_ setRange:[request range]];
__block CBLeaderboardDisplay* blockSelf = self;
[request_ loadScoresWithCompletionHandler:^(NSArray* scores, NSError* error)
{
blockSelf->request_ = nil;
NSUInteger scoresCount = [scores count];
if (scoresCount == 0 && error != nil)
return;
NSMutableArray* playerIDs = [NSMutableArray array];
for (GKScore* score in scores)
[playerIDs addObject:[score playerID]];
[GKPlayer loadPlayersForIdentifiers:playerIDs withCompletionHandler:^(NSArray* players, NSError* error)
{
if (scoresCount > [players count] && error != nil)
return;
[blockSelf displayScores:scores players:players];
completionHandler();
}];
}];
[request_ release];
}
As you can see, the method copies a leaderboard request, executes it, and calls the supplied completion handler. A layer in my game calls this method as follows.
-(void)refreshDisplay
{
CBLeaderboard* request = [[CBLeaderboard alloc] init];
[request setCategory:[[sharedGameCenterManager_ classicLeaderboard] category]];
[request setPlayerScope:GKLeaderboardPlayerScopeFriendsOnly];
[request setTimeScope:GKLeaderboardTimeScopeAllTime];
static NSRange kRequestRange = NSMakeRange(1, 3);
[request setRange:kRequestRange];
__block GJGameOver* blockSelf = self;
[display_ displayScoresWithRequest:request completionHandler:^
{
CGSize displayContentSize = [blockSelf->display_ contentSize];
displayContentSize.width = width(blockSelf) - 2.0 * kGJLabelPadding;
[blockSelf->display_ setContentSize:displayContentSize];
CGFloat displayHeight =
bottomEdge(blockSelf->multiplierLabel_) - topEdge(blockSelf->menu_) - 2.0 * kGJLabelPadding;
CGFloat displayScoreDisplaysCount = [blockSelf->display_ scoreDisplaysCount];
CGFloat displayLabelPadding =
(displayHeight - [blockSelf->display_ minContentSize].height) / displayScoreDisplaysCount;
[blockSelf->display_ setLabelPadding:MIN(floor(displayLabelPadding), kGJLabelPadding)];
static CGFloat kFadeInDuration = 2.0;
if ([blockSelf->display_ opacity] == 0)
[blockSelf->display_ runAction:[CCFadeIn actionWithDuration:kFadeInDuration]];
}];
[request release];
}
My game crashes when both the layer and hence display are deallocated and the request has not completed. When the request completes, it attempts to send a message to a deallocated instance and the crash ensues. Is it possible to cancel a leaderboard request? If not, is there any way I can avoid the crash without causing a memory leak?
In both of your blocks, you use __block to allow the block to reference self without retaining it. This is the problem, because you are doing an asynchronous operation, and if the block is executed after self has been deallocated, it is using a dangling pointer. The whole point of blocks retaining objects they capture is to keep them alive so the block can use it.
Not retaining self when making blocks is commonly done to avoid retain cycles. However, I don't see any retain cycles here:
The request_ in displayScoresWithRequest probably retains the block in displayScoresWithRequest
The block in displayScoresWithRequest retains self, the CBLeaderboardDisplay object
The block in displayScoresWithRequest retains the block from refreshDisplay
The block in refreshDisplay retains self, the GJGameOver object
The GJGameOver object retains display_, the CBLeaderboardDisplay object
However, the CBLeaderboardDisplay object does not retain its instance variable request_. (This code is extremely poorly written, as request_ is released at the end of the method but not set to nil. It should probably be made a local variable or something. And a boolean flag should be used if you want to check whether the code has run once or not.)

glCreateProgram causes segfault?

I'm trying to write a Wavefront OBJ file viewer in Objective-C, that is capable of loading meshes/materials/shaders from files. I've created classes for shaders and shader programs, and I'm trying to create an OpenGL shader program object as part of my shader program class's init method:
- (id)initWithVertexShader:(NSString *)vshader FragmentShader:(NSString *)fshader {
self = [super init];
if (self) {
SRShader* shaders[2] = {
[[SRShader alloc] initWithFilename:vshader Type:GL_VERTEX_SHADER Source:nil],
[[SRShader alloc] initWithFilename:fshader Type:GL_FRAGMENT_SHADER Source:nil]
};
program = glCreateProgram();
for (int i = 0; i < 2; i++) {
SRShader* s = shaders[i];
NSError* e = nil;
s.source = [NSString stringWithContentsOfFile:s.filename encoding:NSUTF8StringEncoding error:&e];
if (!e) {
NSLog(#"Failed to read shader file: %#\n", s.filename);
exit(-1);
}
GLuint shader = [s compile];
... and so on.
However, calling glCreateProgram results in EXC_BAD_ACCESS, as does the call to [SRShader compile], which in turn calls glCreateShader. Does anyone know of any issues with these function calls in Objective-C? Maybe something to do with ARC or calling them in an initialization function?
If you're using an NSOpenGLView, you will need to add the following lines of code before you ever call glCreateProgram(), so probably in -(id)initWithCoder if you load the view from Interface Builder:
NSOpenGLContext* context = [self openGLContext];
[context makeCurrentContext];
Very basic, and I know this thread is dated, but maybe it will save someone a minute or two when they Google and find this thread (as I did just now).
As Trevor Answered in another post you should initialize the glew.
glewInit();
source of the answer:
https://gamedev.stackexchange.com/a/22788

Array to saved photos always returned as empty

I am trying to create an array of all images from the saved photo album that match a certain criteria. Here is a simplified code for it. I add the photos to myImages array and confirmed via the "Added Image" log that the right images get logged. However the array returned by the function is always empty. Fairly new to Objective-C so any suggestions would be helpful.
NSMutableArray * myImages = [NSMutableArray array];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate just photos.
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
[group enumerateAssetsUsingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullResolutionImage]];
NSLog(#"Added Image");
[myImages addObject:latestPhoto];
}
}];
}
failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than this.
NSLog(#"No groups");
}];
return myImages;
What is imagesTakenOnDate? Is that supposed to be myImages? If so, you cannot return it in this manner as the block code will execute after the method returns. The method is asynchronous. Rather than "return" you have 2 options to be able to access the modified array outside the function:
option 1: make your method take a completion block as a parameter, and then call the completion block inside the enumerateGroupsWithTypes block, and pass the completion block the array. For example:
typedef void (^CompletionBlock)(id, NSError*);
-(void)myMethodWithCompletionBlock:(CompletionBlock)completionBlock;
then when you're done with success call:
completionBlock(myImages, nil);
and in the failureBlock call:
completionBlock(nil, error);
option 2: make the array an ivar that is retained on your parent object, rather than a local variable, and then declare it as a __block variable so it can be modified within the block.
First thing. Do you really return imagesTakenOnDate? can`t see any reference to this ivar in your code. I would say that your put some breakpoints in your code. In the gdb debugger console you can type:
po myImages
than the debugger will print out the content of your array. Hope that helps