Store NSMutableArray in NSArray? - iphone

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];

Related

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

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

Objective-C initializing an NSArray vs NSMutableArray

Why does this work:
self.array = newArray;
But this doesn't:
[[self mutableArray] addObject:object];
Meaning, why do I need to init the NSMutableArray for it to work when I don't have to init the NSArray?
EDIT: Hops this is clearer guys.
Interface:
#interface Foo : UIViewController {
}
#property (nonatomic, retain) NSArray *array;
#property (nonatomic, retain) NSMutableArray *mutableArray;
#end
Implementation:
#implementation Foo
#synthesize array;
#synthesize mutableArray;
- (void)viewDidLoad {
[super viewDidLoad];
self.array = [Class returningAnArray];
[[self mutableArray] addObject:objectIHaveOmittedTheCode];
}
#end
self.array = newArray;
In this line, you are assigning already created object to self.array. So, you've no need to create it.
[[self mutableArray] addObject:object];
But in this line you are trying to add an object to a array which is not created actually. If you don't create the array, it will be nil, and sending message to nil won't take any effect.
In Objective-C NSArray objects are immutable.
self.array = newArray;
This line is a property assignment. The self.array property returns a reference pointing to a location in memory that contains an NSArray object. By assigning the property to different objects you're not really modifying the object themselves.
If you wish to modify an existing NSArray object, you'll have to create a NSMutableArray object containing the same elements:
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:self.array];
[mutableArray addObject:object];
Note that NSMutableArray inherits from NSArray, so you can safely assign the object referenced by mutableArray to any variable of type NSArray.
[Class returningArray] did the allocation for you. Every object needs to be allocated (and should be initialized) before it can used. The solution is.
- (void)viewDidLoad {
[super viewDidLoad];
self.array = [Class returningAnArray];
self.mutableArray = [NSMutableArray array];
//Now you can use mutable array
[[self mutableArray] addObject:objectIHaveOmittedTheCode];
}
Now you have created your array and your mutable array with out explicitly calling alloc on either because those classes have done it for you.

EXC_BAD_ACCESS on a simple array of UIImageViews

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

iPhone: Memory Leak in Custom Class and NSMutableDictionary

I've spent a couple of days trying to find out what's going on. I have read loads of Memory Management documentation and I am sick to death of hearing "for every alloc you need a release" - I know that and I still can't figure out why my code is producing memory leaks.
I am writing a simple custom class with an NSMutableDictionary as one of its properties. Basically it mimics an XMLELement. I cannot for the life of me figure out why the allocation of a dictionary is causing a memory leak. The leak occurs on the device as well as the simulator - 5 leaks on the device, and 20 on the simulator.
The leak occurs when I declare and allocate the variable *tmp.
There is also a leak when I set the attribute details (name and value).
This is driving me nuts. Please help!
Part of the code:
#interface IMXMLElement : NSObject {
NSString *strElementName;
NSString *strElementValue;
NSMutableDictionary *dictAttributes;
}
#property (nonatomic, retain) NSString *strElementName;
#property (nonatomic, retain) NSString *strElementValue;
#property (nonatomic, retain) NSMutableDictionary *dictAttributes;
#end
#implementation IMXMLElement
#synthesize strElementName;
#synthesize strElementValue;
#synthesize dictAttributes;
-(id)initWithName:(NSString *)pstrName
{
self = [super init];
if (self != nil)
{
self.strElementName = pstrName;
**LEAK NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
self.dictAttributes = tmp;
[tmp release];
}
return self;
}
-(void)setAttributeWithName:(NSString *)pstrAttributeName
andValue:(NSString *)pstrAttributeValue
{
**LEAK [self.dictAttributes setObject:pstrAttributeValue forKey:pstrAttributeName];
}
-(void)dealloc
{
[strElementName release];
[strElementValue release];
[dictAttributes release];
[super dealloc];
}
The access this class using the following code:
NSString *strValue = [[NSString alloc] initWithFormat:#"Test Value"];
IMXMLElement *xmlElement = [[IMXMLElement alloc] initWithName:#"Test_Element"];
[xmlElement setAttributeWithName:#"id" andValue:strValue];
When you have strings as properties, declare them as copy, not retain.
NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
self.dictAttributes = tmp;
[tmp release];
the above is unnecessary, instead do:
(retain count will automatically be incremented for this autorelease object)
self.dictAttributes = [NSMutableDictionary dictionaryWithCapacity:0];
in dealloc do:
(retain count will automatically be decremented)
self.dictAttributes = nil;
normally for properties you just set them to nil instead of explicitly releasing them
since the get/setter handles that for you.
Try [dictAttributes removeAllObjects] before releasing dictAttributes.
Edit:
Also, you will positive allocation because you are allocating memory for "tmp". The memory will be retained because you now have a reference from dictAttributes.
You then have more positive allocation when you add elements to the dictionary, which also need to be allocated and are kept in memory by the dictionary's internal references
Typical syntax is NSMutableDictionary *tmp = [[[NSMutableDictionary alloc] init] autorelease];