EXC_BAD_ACCESS on a simple array of UIImageViews - iphone

What is wrong with this code?
in the interface:
NSArray *myImages;
#property (nonatomic, retain) NSArray *myImages;
implementation:
NSArray *array = [NSArray arrayWithObjects:
[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image1.png"]],
[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image2.png"]],
[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image3.png"]],
nil];
self.myImages = array;
[array release];
If I log myImages right after initializing it, it correctly logs the array of UIImageViews. However, later in the app, when I try to access self.myImages from a different method, I get EXC_BAD_ACCESS. It is getting retained in the interface. What is the problem?

Do not release array. Using arrayWithObjects:, it will return an autoreleased object. In a sense, you are releasing it twice. An alternative is:
[[NSArray alloc]initWithObjects:...]
Then you can release array.
See Apple's memory management article:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html%23//apple_ref/doc/uid/20000043-BEHDEDDB

arrayWithObjects is a convenience method and returns an autoreleased object, so remove the
[array release];
Plus you leak memory by doing this :
[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image1.png"]]
Because this time the imageView isn't released.

arrayWithObjects returns an autoreleased object, you're over releasing it. See here http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/MemoryMgmt/Articles/mmRules.html%23//apple_ref/doc/uid/20000994-BAJHFBGH

Related

Store NSMutableArray in NSArray?

I have a property an NSArray property called toolbarButtons that in one instance (when the UIViewController is modal) it needs to be an NSMutableArray, and when the UIViewController is nonmodal, it needs to simply be an NSArray.
I have initialized the property like this:
#property (nonatomic, copy) NSArray *toolbarButtons;
I am initializing toolbarButtons as follows:
- (void) setToolbarButtons
{
UIBarButtonItem *exitButton = [[UIBarButtonItem alloc] initWithTitle:#"Exit" style:UIBarButtonItemStyleBordered target:self action:#selector(exitButtonPushed)];
toolbarButtonsTemp = (NSMutableArray *)[[NSMutableArray alloc] init];
[(NSMutableArray*)toolbarButtonsTemp addObject:exitButton];
self.toolbarButtons = toolbarButtonsTemp;
[exitButton release];
}
Then later on I override the above method in a subclass like this:
- (void) setToolbarButtons
{
[super setToolbarButtons];
UIBarButtonItem *leftFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *toolButton = [[UIBarButtonItem alloc] initWithTitle:#"Tools" style:UIBarButtonItemStyleBordered target:self action:#selector(toolButtonPushed)];
[(NSMutableArray*)self.toolbarButtons addObject:leftFlexibleSpace];
[(NSMutableArray*)self.toolbarButtons addObject:toolButton];
[toolButton release];
[leftFlexibleSpace release];
}
and I'm getting the following error:
[__NSArrayI addObject:]: unrecognized selector sent to instance
I know there are a ton of work arounds to this, but from a design perspective I'm just wondering if what I'm doing is possible and how to correct my mistake.
#property ( nonatomic, copy ) NSArray* array
means:
- (void) setArray: (NSArray*) array
{
_array = [array copy];
}
[array copy] always returns an non mutable NSArray, even if array is a NSMutableArray.
#property ( nonatomic, copy ) NSMutableArray* array
is handled a bit differently:
- (void) setArray: (NSMutableArray*) array
{
_array = [array mutableCopy];
}
In that case, you can do
self.array = (NSMutableArray*) [NSArray new];
and array will be assigned with a NSMutableArray, as mutableCopy always returns an NSMutableArray.
The latter is of course strongly discouraged. It works for now. No guarantee it will work forever.
Your other choice, besides an NSMutableArray, is to use a strong property and to handle the copies yourself.
you cannot do that!
when you:
[(NSMutableArray*)self.toolbarButtons addObject:leftFlexibleSpace];
[(NSMutableArray*)self.toolbarButtons addObject:toolButton];
you are just casting the property, it means you are promising in runtime that that property is a NSMutableArray... but you are lying!!!
i suggest you do the contrary: just declare it as a NSMutableArray (it's a subclass of NSArray, so you can use it as a NSArray when you want it),and use the NSMutableArray methods just when you need them
Casting won't work in this case. You need to change your property to an NSMutableArray:
#property (nonatomic, copy) NSMutableArray *toolbarButtons;
You should just use an NSMutableArray in your property, but you could always do something like this too...
NSMutableArray *mutArray = [[NSMutableArray alloc] init];
[mutaArray addObject:leftFlexibleSpace];
[mutaArray addObject:toolButton];
self.toolbarButtons = [NSArray arrayWithArray:mutArray];

Store UIImageViews in NSMutableDictionary

I have simple question. This is my header file :
#import <UIKit/UIKit.h>
#interface FirstFaceController : UIViewController
#property (nonatomic,retain) NSMutableDictionary *face1Layers;
#end
This .m, here i init my Dictionary and put where UIImageView :
#import "FirstFaceController.h"
#implementation FirstFaceController
#synthesize face1Layers;
-(void) dealloc {
[face1Layers release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.face1Layers = [NSMutableDictionary dictionary];
[self.face1Layers setObject:
[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"pic.png"]]
forKey:#"pic"];
[self.view addSubview:[self.face1Layers objectForKey:#"pic"]];
if ( [[face1Layers objectForKey:#"pic"] superview] == nil ) {
//....
}
}
Then i call [[face1Layers objectForKey:#"pic"] superview] i have "EXC_BAD_ACCESS".
Why?
Try to do this:
NSMutableDictionary* tempDict = [[NSMutableDictionary alloc] init];
self.face1Layers = tempDict;
UIImageView* picView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"pic.png"]];
[self.face1Layers setObject:picView forKey:#"pic"];
[picView release];
[tempDict release];
Do not create and insert yours NSMutableDictionary and UIImageView throught a single line of code because you have leaks.
In the first case, if you do the following you have a retain count of two. face1Layers has a retain policy.
self.face1Layers = [[NSMutableDictionary alloc] init];
You can avoid this splitting the code as I explained before or send an autorelease message to the initialized object.
In the second case, when you add an object in NSDictionary or NSArray (and theirs subclasses), these classes retain added objects.
Hope it helps.
Well, I think there are a couple of things wrong here:
You never allocate the dictionary as in NSMutableDictionary alloc init
The UIImageView is allocated but never released. I would allocate before setting it as object, and then add it, and then release it

NSMutableArray memory Management when allocated directly

I know that when we add an object obj into a NSMutableArray, it takes the co_ownership of that object. So we can release the object obj just after adding that into the NSMutableArray. And when we release the NSMutableArray, it also calls the release of all the objects in it. So there is no memory leak and every thing is fine.
My doubt is if do like
NSMutableArray myArray = [[NMutableArray alloc] init];
[myArray addObject:[MyClass alloc] init]];
[myArray release];
Will this Cause any memory leak in our program?
Yes indeed, it will cause a memory leak.
The offensing line is (I have splitted it for clarity):
[myArray addObject:
[[MyClass alloc] init] // <- An instance is allocated with ownership
]; // <- The array retains the instance
At the end, the retain/release ownership are not balanced and causes the leak.
One solution is to make the allocation before the addition:
MyClass *obj = [MyClass alloc] init];
[[myArray addObject:obj];
[obj release];
Put autorelease for your MyClass. That should fix any memory leaks
NSMutableArray myArray = [[NMutableArray alloc] init];
[myArray addObject:[[[MyClass alloc] init] autorelease];
[myArray release];

do I need to allocate and init variables with retain properties?(iphone)

#interface Approval : NSObject
{
NSMutableArray *approvalValues;
}
#property (nonatomic,retain) NSMutableArray *approvalValues;
If i do this, do I still need to call `approvalValues = [[NSMutableArray alloc] init] in the init method? I was under the impression that I had to but it is causing a leak. In the dealloc method I am releasing approvalValues
You need to alloc and init approvalValues. The problem seems to be related to the fact that you are over-retaining your object.
Your code probably looks like this:
self.approvalValues = [[NSMutableArray alloc] init];
alloc will return an object with a retainCount of 1, and when using the retain setter it will get bumped to 2. In order to solve it, you might want to autorelease the object before assigning it, making a code that looks like this:
self.approvalValues = [[[NSMutableArray alloc] init] autorelease];
This will end up with an instance variable with a retainCount of only 1, so when you dealloc the object it won't leak.
Yes you still need to alloc/init, however you only release in dealloc method.
In the init method you will often access the ivar directly and initialize it like this:
approvalValues = [[NSMutableArray alloc] init];
In the dealloc you will need a matchin release like this:
[approvalValues release];
It is often recommended to access the ivars directly in the init and dealloc method to avoid any side effects caused by setters/getters.
Throughout your class you will want to use the KVC setters/getters or dot notation to set objects like this
// Dot notation
NSMutableArray *tmpApprovalValues = [[NSMutableArray alloc] init];
self.approvalValues = tmpApprovalValues;
[tmpApprovalValues release]; tmpApprovalValues = nil;
// Call to setters/getters
NSMutableArray *tmpApprovalValues = [[NSMutableArray alloc] init];
[self setApprovalValues:tmpApprovalValues];
[tmpApprovalValues release]; tmpApprovalValues = nil;
Corrected terminology thanks to #Yuji

NSString leaking even with release at the right place (I guess)?

When I analyze the following code with Instruments, it reports a leak on variable imageName:
//loadImagesFromPotatoesIndexesArray
-(void) loadImagesFromPotatoesIndexesArray{
//Load Textures from Disk
textures = [[NSMutableArray alloc] init];
//NSArray *masks = [[NSArray alloc] initWithArray:mainDelegate.masksArray];
for (int i = 0;i<[potatoesIndexesArray count];i++){
int imageNumber = [[potatoesIndexesArray objectAtIndex:i]intValue];
NSString *imageName = [[NSString alloc] initWithFormat:#"texture%d",imageNumber];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
NSArray *pics = [[NSArray alloc] initWithObjects:
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
imageName,
nil];
[textures addObject:pics];
[image release];
[imageName release];
[pics release];
}
}
[potatoesIndexesArray count] = 16, so I've got 16 times that NSCFString leaking ... But to me the code is respecting memory management ... obviously not!!!
What did I do wrong?
You never release the 'textures' array. It's still holding everything.
How often is loadImagesFromPotatoesIndexesArray called in your code? If is called more than once, all of the values in the original array will be leaked, since you don't properly release textures before replacing it with a new array.
If it is being called more than once, this should do the trick:
// load textures from disk
[textures removeAllObjects];
//NSArray *masks = [[NSArray ...
for (int i=0; ...
If think that when you add imageName in your pics array it retain it ;-)
(I think it answer to your question)
But, why are you doing a alloc here ?
why not doing something like
[ NSString stringWithFormat:#"" ] ?
Good Luck !
This is a complicated issue. You alloc the imageName, so the retainCount is 1, then you add it into an array, the retain count is 2, when you release the imageName, the retain Count is 1 again. Then if you also release the pics array, everything will be fine. But your pics array is added into textures, then the pics is released, so your pics retainCount is still 1. And your imageName is leaked. But, if you release the textures array, everything will be fine
NSString *imageName = [[NSString alloc] initWithFormat:#"texture%d",imageNumber];
NSArray *pics = [[NSArray alloc] initWithObjects:
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
imageName,
nil];
[imageName release];