NSImage memory leak - objective-c

First of all: I already searched on google and SO for solutions - none worked.
I've got an application which loads the artwork of the current iTunes track and displays it; this is stored in a NSImage instance, among some other variables, in a class:
#interface infoBundle : NSObject
#property (strong) NSImage *track_artwork;
#property (weak) NSString *track_title;
#property (weak) NSString *track_album;
#property (weak) NSString *track_artist;
#end
Then, an instance of this class is created:
-(infoBundle*)returnInfoBundle {
infoBundle* tmpBundle = [[infoBundle alloc]init];
tmpBundle.track_artwork = [[NSImage alloc]initWithData:[(iTunesArtwork *)[[[iTunes currentTrack] artworks] objectAtIndex:0] rawData]];
[...]
return tmpBundle;
}
And later used:
-(void)iTunesDidChange {
infoBundle* tmpBundle = [self returnInfoBundle];
[...]
[imageView setImage:tmpBundle.track_artwork];
}
That's eating up ~2MB (Cover size, I'd guess) per call of iTunesDidChange.
I already tried:
[tmpBundle autorelease];
[tmpBundle release];
[tmpBundle dealloc];
tmpBundle = nil;
and, after that didn't help:
- Enabling ARC.
=> Why is this eating up memory, although the object (tmpbundle) should get removed?
=> How may I achieve leak-less NSImage usage?
Thanks for any tips/suggestions/solutions :)

Issue
You will have a memory leak if you create your object on your method and not release it inside that method or you have to reference it when you pass it as a parameter by reference : Passing arguments by value or by reference in objective C
Your problem is that you are creating an instance of infoBundle two times, and when you are initializing another instance of it, you are leaving the first one without reference, so it remains in memory, and without connection to remove it (memory leak).
Solution
To make your things easier you should create an instance of your object
#implementation
{
infoBundle* tmpBundle;
}
Use it where ever you need it
-(infoBundle*)returnInfoBundle
{
tmpBundle = [[infoBundle alloc]init];
tmpBundle.track_artwork = [[NSImage alloc]initWithData:[(iTunesArtwork *)[[[iTunes currentTrack] artworks] objectAtIndex:0] rawData]];
[...]
return tmpBundle;
}
-(void)iTunesDidChange
{
tmpBundle = [self returnInfoBundle];
[...]
[imageView setImage:tmpBundle.track_artwork];
}
And when you are finished with that object dealloc will automatically release it if you add it to dealloc method:
- (void) dealloc
{
[tmpBundle release];
tmpBundle = nil;
}
Hope it helps! :)

Just modifiy this line :-
infoBundle* tmpBundle = [[[infoBundle alloc]init]autorelease];

I can’t tell from your code what you are doing in [imageView setImage:tmpbundle.track_artwork]; but you may be having the same problem I had.
I was using
self.imageToDisplay = [UIImage imageNamed:pictFileName];
and kept getting leaks. I switched to
self.imageToDisplay = [UIImage imageWithContentsOfFile:pictFile];
and they went away.
According to the documentation for imageNamed,
This method looks in the system caches for an image object with the
specified name and returns that object if it exists… If you have an
image file that will only be displayed once and wish to ensure that it
does not get added to the system’s cache, you should instead create
your image using imageWithContentsOfFile:. This will keep your
single-use image out of the system image cache, potentially improving
the memory use characteristics of your app.
It sounds like you have either the same or a similar issue.

Related

Why does my NSArray get deallocated?

I'm trying to understand Automatic Reference Counting, as I come from a high-level programming language (Python) and I'm working on a project which use this feature of Objective-C. I often get problems with ARC deallocating objects which I need later, but now I got a concrete example for which I hope I'll get an explanation.
- (void) animateGun:(UIImageView *)gun withFilmStrip:(UIImage *)filmstrip{
NSMutableArray *frames = [[NSMutableArray alloc] init];
NSInteger framesno = filmstrip.size.width / gun_width;
for (int x=0; x<framesno; x++){
CGImageRef cFrame = CGImageCreateWithImageInRect(filmstrip.CGImage, CGRectMake(x * gun_width, 0, gun_width, gun_height));
[frames addObject:[UIImage imageWithCGImage:cFrame]];
CGImageRelease(cFrame);
}
gun.image = [frames objectAtIndex:0];
gun.animationImages = frames;
gun.animationDuration = .8;
gun.animationRepeatCount = 1;
[gun startAnimating];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(arc4random() % 300)/100 * NSEC_PER_SEC), dispatch_get_current_queue(),^{
[self animateGun:leftGun withFilmStrip:[self getFilmStripForAction:gunShoot andTeam:nil withWeapon:nil]];
});
}
The idea behind this snippet of code is simple: I have a (UIImageView*)gun which I animate with the images stored in (NSMutableArray *)frames, at random times. (UIImage *)filmstrip is just an image which contains all the frames which will be used on animation. The first iteration of animation works, but the problems appears on the second iteration, where I get -[UIImage _isResizable]: message sent to deallocated instance ... or -[UIImage _contentStretchInPixels]: message sent to deallocated instance ... or -[NSArrayI release]: message sent to deallocated instance .... This happens at
gun.animationImages = frames;
but I don't understand why. I'm not requesting a fix for my issue, but just to help me understand what's happening here. Thanks.
ARC is a mechanism that removes the need to manually retain/release objects. Here's a nice site that explains how this works: http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained/
Try changing "leftGun" for "gun". I think that's probably the one that gets deallocated at some point, if you're using it through an ivar. Otherwise, leftGun simply isn't in the scope.
Here's what it should look like:
In your .h file:
#property (nonatomic, strong) IBOutlet UIImageView *leftGun;
In your .m file:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(arc4random() % 300)/100 * NSEC_PER_SEC), dispatch_get_current_queue(),^{
[self animateGun:gun withFilmStrip:[self getFilmStripForAction:gunShoot andTeam:nil withWeapon:nil]];
});
Also, not quite sure where "gunShoot" is coming from. Is that supposed to be an enum?
EDIT
Added an example of how the leftGun property should be defined. The reason behind using a property over an ivar is for memory management purposes. If you want to release or destroy an object that is a property, simply set it to nil and the property will take care of releasing the object if it has to.
You may prevent the deallocation of the frames array if you mark it as __block.
__block NSMutableArray *frames = [NSMutableArray array];
see “The __block Storage Type.”

Object alloc init irregularities

I'm seeing some disturbing irregularities concerning object allocation and initialization in an app I'm trying to write.
I have a 'root' Modelcontroller object, which in turn contains references to subcontrollers. The root controller is called modelController, and in it's init method it allocates and inits the subcontrollers like so:
- (id)init
{
NSLog(#"%#", #"ModelController begin init");
self = [super init];
if (self) {
LibraryController * tempLibrary = [[LibraryController alloc] init];
self.library = tempLibrary;
StoresController * tempStores = [[StoresController alloc] init];
self.stores = tempStores;
CLLocationManager * tempLocationManager = [[CLLocationManager alloc] init];
self.locationManager = tempLocationManager;
}
NSLog(#"%#", #"ModelController complete init");
return self;
}
Pretty standard. The subcontrollers' init code also contain an NSLog messages at the beginning and the end, for me to be able to see that all is well.
The properties are defined as
#property (strong) LibraryController * library;
#property (strong) StoresController * stores;
#property (strong) CLLocationManager * locationManager;
And I am using ARC.
What puzzles me is that sometimes I see the NSLogs from one of the subcontrollers, but not from the root controller. Sometimes I see the 'begin init' log message from the root controller, but not the 'complete init'. Sometimes I see no init log messages. The application launches anyway in any of these cases.
This happens seemingly at random, in one out of five launches or in one out of twenty launches. When it happens, the app acts very strange (but not every time, mind you), beachballing for no apparent reason and exhibiting general wonkiness.
As a side note, at one time I put a breakpoint in the init method of the StoreController class, which when pausing executing spit out a chunk of random data in the debugging console:
$m2303,3503,3603,3703,3803,3903#00$m2303,3503,3603,3a03#00$88ee410901000000981e420901000000001e42090100000060ee410901000000b062f668ff7f000070044391ff7f0000f00e0800000000000300000068200100dc62f668ff7f0000d862f668ff7f00000000000000000000717ddd8aff7f00000000000068200100801e420901000000000000000600000706000007000000007063f668ff7f000003280000000000007863f668ff7f000001ee410901000000f062f668ff7f00006c5bd391ff7f000000000000ff7f0000ab064391ff7f000000000000ffffffff032800000000000040
...and so on
Where should I begin to look to troubleshoot this?
The modelController is alloc init'd from the MyDocument equivalent class, and is modeled as a singleton.
The singleton implementation looks like this:
static ModelController *sharedModelController = nil;
+ (ModelController*)sharedManager
{
if (sharedModelController == nil) {
sharedModelController = [self new];
}
return sharedModelController;
}
Final note: I have tried removing the locationManager stuff and disabling/enabling the 'Restore state' preference in the scheme, but to no avail.
Sounds like you're doing some UI stuff not on the main thread.
This generally leads to weird behavior.
Make sure you call everything UI related on the main thread
Best guess: the ModelController object is being released. Perhaps the Singleton is faulty.

UIImage setter NSInvalidArgumentException

have a problem with object setters in a class.
I have the class GEOImage, where things like description, title etc. will be saved according
to an image.
#interface GEOImage : UIImage
{
NSString *title;
NSString *imgDescription;
NSString *latitude;
NSString *longitude;
NSDictionary *editInfo;
}
#property (nonatomic, copy) NSString *title, *imgDescription, *latitude, *longitude;
#property (nonatomic, copy) NSDictionary *editInfo;
#end
Now i try to store a description out of another class:
self.chosenImage.imgDescription = #"description";
where chosenImage is of type GEOImage.
But i get the error:
-[UIImage setTitle:]: unrecognized selector sent to instance 0x939d220
2011-12-05 10:59:40.621 GeoPG[511:17c03] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIImage setTitle:]: unrecognized selector sent to instance 0x939d220'
If I'm looking in the debugger, the chosenImage is not NULL, and its been displayed correct in an image view.
Greets
s4lfish
We can safely infer that chosenImage is not nil; if it were nil, sending it a message would simply do nothing, not crash.
(Also, I'm assuming that you meant title rather than imgDescription in your usage sample, or that you implemented setImgDescription: to set the title in turn.)
There are two possibilities:
Dead object
You created the image you stored to chosenImage as a GEOImage, but then it died while you were holding on to it. Subsequently, a UIImage (as identified in the exception message) was created at the same address, so the pointer you still hold now points to a UIImage. You sent a message to it that only works on GEOImages, but it's only a UIImage, so it doesn't respond to the message, which is the exception.
The cause of an object dying while you're holding it is that either you didn't retain it somewhere where you should have, or you released it somewhere where you shouldn't have. Or possibly both.
Run your app under Instruments with the Zombies template. It will raise a flag when you hit this crash, and you can then investigate by clicking the button in that flag. Look at all of the Release and Autorelease events, starting from the end, to find the one that shouldn't be there; then, if the release itself is unwarranted, take it out, or if it should be balanced by a previous retain, put one of those in.
One possible cause of the crash is that you declared the chosenImage property as assign, but you should have declared it as retain/strong. If this is the problem, your Instruments findings will support it.
Long-term, you should convert to ARC, which eliminates 90% of the cases where this problem could happen.
You never created a GEOImage in the first place
Just because you declared that chosenImage will hold a pointer to a GEOImage doesn't mean it does. You can assign any object pointer there, and in many cases, the compiler doesn't know if it isn't actually a GEOImage.
(They introduced a feature called “related result types” in a future version of Clang that should make this much less likely.)
At a guess, I'd say you're doing something like this:
self.chosenImage = [GEOImage imageNamed:#"blah blah blah"];
or this:
self.chosenImage = [imagePicker takePicture];
There is no reason to expect takePicture to return a GEOImage (how should it know that's what you want?), and it's likely that +[UIImage imageNamed:] (assuming you simply inherit it) won't, either. Unless you create a GEOImage instance yourself, using alloc and an initializer, you cannot assume that any UIImage you get will be a GEOImage.
The solution is to make it easy to create a GEOImage from a UIImage (which will involve wrapping this method), and then do that.
Once you have a live (not dead) GEOImage (not UIImage) in your chosenImage property, it will work.
In fact, it was like Peter said: I never created an GEOImage. I now I create the GEOImage like this:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = [info valueForKey:#"UIImagePickerControllerOriginalImage"];
NSURL *imgURL = [info valueForKey:#"UIImagePickerControllerMediaURL"];
CGImageRef imageRef = [image CGImage];
[self createGEOImage:imageRef info:info imageURL:imgURL];
}
And the method called with the imageRef:
- (void)createGEOImage:(CGImageRef*)imageRef info:(NSDictionary*)info imageURL:(NSURL*)imgURL{
GEOImage *geoImage = [[GEOImage alloc]initWithCGImage:imageRef];
geoImage = info;
geoImage = imgURL;
}
Thanks for your help!

NSMutableDictionary Singleton issue

I am coding Objective-C using the Cocos2D framework, and I have a singleton used for multiple purposes. One new purposes is to get and set character's "states" which are strings. I've recently made an NSDictionary for this purpose, but I have issues with the program freezing up when a method inside the singleton is called.
Here's the singleton code. I'm just leaving in the character state stuff:
.h
#interface ExGlobal : NSObject {
NSArray *charStates_keys;
NSArray *charStates_objects;
NSMutableDictionary *charStates;
}
#property(nonatomic, retain) NSMutableDictionary *charStates;
+(ExGlobal*)sharedSingleton;
- (NSString *)charState:(NSString *)charName;
- (void)set_charState:(NSString *)value forCharName:(NSString *)charName;
#end
.m
#import "ExGlobal.h"
#implementation ExGlobal
#synthesize charStates;
static ExGlobal* _sharedSingleton = nil;
+(ExGlobal*)sharedSingleton {
#synchronized([ExGlobal class]) {
if (!_sharedSingleton) {
[[self alloc] init];
}
return _sharedSingleton;
}
return nil;
}
+(id)alloc {
#synchronized([ExGlobal class]) {
NSAssert(_sharedSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedSingleton = [super alloc];
return _sharedSingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
exitName = #"ruinsSkyMid";
sceneChangeKind = #"reborn";
charStates = [[NSMutableDictionary alloc] init];
charStates_keys = [NSArray arrayWithObjects:#"Feathers", #"Hummus", nil];
charStates_objects = [NSArray arrayWithObjects:#"at wall", #"with Feathers", nil];
charStates = [NSMutableDictionary dictionaryWithObjects:charStates_objects forKeys:charStates_keys];
}
return self;
}
- (NSString *)charState:(NSString *)charName{
NSString *value = [charStates objectForKey:charName];
return value;
}
- (void)set_charState:(NSString *)charState forCharName:(NSString *)charName{
[charStates setObject:charState forKey:charName];
}
- (void)dealloc {
//I know it doesn't get called, but just in case
[charStates release];
[super dealloc];
}
#end
It's unclear to me what exactly the issue is when it freezes. When this happens, all I get in the console is:
Program received signal: “EXC_BAD_ACCESS”.
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.5 (8L1)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib (file not found).
Previous frame inner to this frame (gdb could not unwind past this frame)
Previous frame inner to this frame (gdb could not unwind past this frame)
Which I'm sure doesn't help finding the issue. I found if I redefine charStates_keys, charStates_objects and charStates inside both the charState and set_charState methods, it seems to work without freezing, except set_charState does not change the state.
It isn't freezing, it is crashing. Hence the EXC_BAD_ACCESS. It looks like your Xcode installation is borked, too, as the two messages following should not happen.
Note that methods should not have _s in the name; not a cause of the problem, but a comment on following convention.
You aren't retaining charStates and that is likely the cause of the crash.
Not an answer as such but I didn't have enough space in the comments field above to post this, but it might be useful.
As bbum already said, your lack of retaining charStates is likely the problem.
If you are confused about when to retain and not retain objects there's a really good book called "Learn Objective-C on the Mac" and I know it's a Mac book but most of it applies to iPhone too. On page 171 of chapter 9 (Memory Management) it talks about the "Memory Management Rules" and how if you are confused about when to retain or not then you don't understand the simple rules of Objective C memory management.
Essentially if you create an object using new, alloc or copy, then the retain count is automatically set to 1 so the object is retained and does not require you to retain it and will require a subsequent release to deallocate.
If you create the object any other way then the object will be an autoreleased object.
Obviously these rules only apply within the standard iOS libraries and can't necessarily be applied to third party libraries.
I recommend anyone who doesn't fully understand memory management in Objective C read this book. I found highly enlightening even for my iPhone work.
Hope that helps/.

Array of pointers causes leaks

-(void)setUserFilters{
//init the user filters array
userFilters = [[NSMutableArray alloc] init];
SearchCriteria *tmpSc= [[SearchCriteria alloc] init];
for(int i=0;i<[searchFilters count];i++)
{
tmpSc=[self.searchFilters objectAtIndex:i];
if(tmpSc.enabled==TRUE)
[userFilters addObject:tmpSc];
}
}
searchFilters is a list of filters that can be setted to true or false and I use userFilters to populate a table view with the filters that are only setted to TRUE
But the line SearchCriteria *tmpSc= [[SearchCriteria alloc] init]; causes leaks, and I don't know how to solve because if I release at the end of the function I loose my pointers and it crashes
Any ideas?
twolfe18 has made the code >much slower if searchFilters can be large. -objectAtIndex: is not a fast operation on large arrays, so you shouldn't do it more than you have to. (While true that FE is faster than objectAtIndex:, this overstated the issue and so I've striken it; see my other comments on the advantages of Fast Enumeration.)
There are a number of problems in your code:
Never create a method that begins "set" but is not an accessor. This can lead to very surprising bugs because of how Objective-C provides Key-Value Compliance. Names matter. A property named userFilters should have a getter called -userFilters and a setter called -setUserFilters:. The setter should take the same type that the getter returns. So this method is better called -updateUserFilters to avoid this issue (and to more correctly indicate what it does).
Always use accessors. They will save you all kinds of memory management problems. Your current code will leak the entire array if -setUserFilters is called twice.
Both comments are correct that you don't need to allocate a temporary here. In fact, your best solution is to use Fast Enumeration, which is both very fast and very memory efficient (and the easiest to code).
Pulling it all together, here's what you want to be doing (at least one way to do it, there are many other good solutions, but this one is very simple to understand):
#interface MyObject ()
#property (nonatomic, readwrite, retain) NSMutableArray *userFilters;
#property (nonatomic, readwrite, retain) NSMutableArray *searchFilters;
#end
#implementation MyObject
#synthesize userFilters;
#synthesize searchFilters;
- (void)dealloc
{
[searchFilters release];
serachFilters = nil;
[userFilters release];
userFilters = nil;
[super dealloc];
}
- (void)updateUserFilters
{
//init the user filters array
// The accessor will retain for us and will release the old value if
// we're called a second time
self.userFilters = [NSMutableArray array];
// This is Fast Enumeration
for (SearchCriteria *sc in self.searchFilters)
{
if(sc.enabled)
{
[self.userFilters addObject:sc];
}
}
}
It seems that your initially creating a SearchCriteria object and before you use it or release it your reassigning the variable to another object from self.searchFilters. So you don't need to create the initial object and why it's leaking and not being released.
Try:
SearchCriteria *tmpSc = nil;
Hope that helps.
first of all, the worst n00b code you can write involves if(condition==true) do_something(), just write if(condition) do_something().
second, there is no reason to have tempSc at all (never mind alloc memory for it), you can just do the following:
-(void)setUserFilters{
//init the user filters array
userFilters = [[NSMutableArray alloc] init];
for(int i=0;i<[searchFilters count];i++)
{
if([self.searchFilters objectAtIndex:i].enabled)
[userFilters addObject:[self.searchFilters objectAtIndex:i]];
}
}