I'm pretty new to objective-c and try to create a small app for the iphone.
I'm nearly done beside this little error here. Actually, I've searched hours with google to find a proper solution but unfortunately I'm not able to find a solution which works.
I'm using this tutorial here to build up an UITableView: UITableView Tutorial
The full error message looks like this:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '* -[NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
This is the Data Controller Header:
MyLinksDataController.h
#interface MyLinksDataController : NSObject {
NSMutableArray *tableList; //<---important part
}
- (unsigned)countOfList;
- (id)objectInListAtIndex:(unsigned)theIndex;
- (void)addData:(NSString *)data; //<---important part
- (void)removeDataAtIndex:(unsigned)theIndex;
#property (nonatomic, copy, readwrite) NSMutableArray *tableList; //<---important part
.....
And the Data Controller Method:
MyLinksDataController.m
#import "MyLinksDataController.h"
#implementation MyLinksDataController
#synthesize tableList;
- (id)init {
if (self = [super init]) {
NSLog(#"Initilizing DataController");
//Instantiate list
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.tableList = [localList copy];
[localList release];
//Add initial Data
[self addData:#"AAAAAAAAAAAAAA"];
[self addData:#"BBBBBBBBBBBBBB"];
}
return self;
}
-------------------------------later on in the source code---------------------------------
- (void)addData:(NSString*)data; {
[tableList addObject:data]; //<---- here the app crashes
}
I would pretty much appreciate any help.
Thank you for your support in advance.
Daniel
Sending the copy message to an NSMutableArray -- as in the following statement in init -- returns an immutable copy.
self.tableList = [localList copy];
Cocoa documentation uses the word immutable to refer to read-only, can't-be-changed-after-initialization objects. Hence the subsequenct call to addObject: fails with an error message.
Note how the assignment statement above doesn't trigger any compiler warning. copy returns an id, which fits comfortably -- as far as the compiler is concerned -- in the NSMutableArray* tableList. There's no runtime error here either, as no messages get passed around; an NSArray pointer is just placed in an NSMutableArray pointer variable.
To obtain a mutable copy, use mutableCopy instead.
Note that both copy and mutableCopy create a new array and copy the content of the original to it. A change in the copy will not be reflected in the original. If you just need another reference to the original array, use retain instead.
You can find more detail in the discussion section of the copyWithZone reference and in the NSMutableCopying protocol reference.
You're running into, basically, the memory management rules of Cocoa (specifically, these details). If there is an object with an immutable version and a mutable version, then sending -copy to an object will return an immutable object.
Let's step through the relevant part.
NSMutableArray *localList = [[NSMutableArray alloc] init];
This creates a new, empty mutable array that you own. Fine.
self.tableList = [localList copy];
This creates an immutable copy of the empty array. Furthermore, you own this freshly created copy. That's two objects you own at the moment.
This also assigns your copied object to the tableList property. Let's look at the property declaration:
#property (nonatomic, copy, readwrite) NSMutableArray *tableList;
This property is declared with the copy attribute, so whenever a new value is assigned to it, another -copy method is sent to it. This third copy, however, is not owned by you—it's owned by the object.
[localList release];
That releases the original empty mutable array. Fine, but there's still the one you made in the second line floating around, unreleased. That's a memory leak.
If you actually need a mutable copy of something, you want the -mutableCopy method. (The documentation for these methods is found under NSCopying and NSMutableCopying.) However, you're never going to get a mutable version of something into a property with the copy attribute, since it will send -copy to whatever it is assigned. Your property should use the retain attribute instead of the copy attribute, and the code to initialize it should look something like this:
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.tableList = localList;
[localList release];
Or, a shorter version:
self.tableList = [NSMutableArray array];
There's no need to copy anything in this situation, you're just creating a fresh object.
If u are assigning localList from another object may be that is not Mutable in that case it can through this kind of error.
I hope it will be helpful.
self.tableList = [localList mutableCopy];
Hi instead of mutableCopy i believe "strong" can also be used to tackle this problem. I had similar problem in my code as well because of using "copy" instead of "strong." So the below line:
#property (copy, nonatomic) NSMutableArray *computers;
It should be:
#property (strong, nonatomic) NSMutableArray *computers;
Hope it will be of immense help for beginners making mistakes like me.
This will resolve the issue:
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.localList = [[NSMutableArray alloc]initWithArray:localList];
Related
I create a NSMutableArray that I need as long as my app lives, lets call it suseranArray, just after the #implementation of my main class. This Array will hold several objects of a class called Vassal. A Vassal is simply:
1) A NSMutableString
2) Another NSMutableString
3) A NSMutableArray
4) Another NSMutable Array
Each Vassal created is also needed for the life of the app, and they never change.
These objects are made as (retain) properties in an .h file, synthesized in the .m file, and each given an alloc+init whenever the object Vassal is created during the init function. Each vassal has data filled in and stored in the suzerain Array. the 3rd item always has several elements, and after a bug appeared, I put a line to check if it is ever empty, but it never is, and life is good.
Now, later on when a certain Vassal object is needed, we try to access its 3rd property to fetch the data in there, and sometimes that array empty... I checked to see if it disappeared somehow, but it is always there on the debug, carrying a nice address like 0x2319f8a0 which makes sense since the NSMutableString just above it is at address 0x2319fb40 - (I was expecting 00000000 after a lot of headache). What is happening? I my head, I am creating an RETAINed objects, which retains data put in by default, and that object is put inside another, but somehow the data inside the array vanishes. What possible scenario could lead to this? Thank you for your time :)
Note: the last array currently just holds one item at this stage of development, and curiously enough, that one item is never missing, despite the two arrays being 'brothers'
Vassal.h
#interface Vassal : NSObject
#property (retain) NSMutableString *wordBody;
#property (retain) NSMutableString *wordCode;
#property (retain) NSMutableArray *wordRelations;
#property (retain) NSMutableArray *wordLinks;
#end
Vassal.m
#implementation Vassal:NSObject
#synthesize wordBody;
#synthesize wordCode;
#synthesize wordRelations;
#synthesize wordLinks;
-(NSObject*) init
{
if(self=[super init])
{
wordBody=[[NSMutableString alloc] init];
wordCode=[[NSMutableString alloc] init];
wordRelations=[[NSMutableArray alloc] init];
wordLinks=[[NSMutableArray alloc] init];
}
return self;
}
//Somewhere in Suseran:
-(void)fillStuff
{
...
Vassal *vassal=[Vassal new];
for (int i=0;i<[originalDataString length];i++)
{
...
[vassal.wordRelations addObject:anItem];
...
}
int errorTest=[vassal.wordRelations count];
if (errorTest==0)
{
//breakpoint here. Program NEVER comes here
}
[bigArrayOfVassals addObject:vassal];
}
//these arrays are never touched again but here:
-(void) getVassalstuff:(NSMutableString*)codeOfDesiredVassal
{
Vassal *aVassal;
for (int i=0;i<[bigArrayOfVassals count];i++)
{
aVassal=bigArrayOfVassals[i];
if ([codeOfDesiredVassal isEqualToString:aVassal.wordCode)
{
int errorTest=[aVassal.wordRelations count];
if (errorTest==0)
{
//yay! this breakpoint sometimes is hit, sometimes not,
//depending on code's mood. Why is this happening to me? :,(
}
}
}
}
I see that that you have properties that are mutable (which is itself a bad idea except for specific cases) and that you are retaining them.
Mutability means that if you have set the array as a property based on some other array, and if that original array is changed, the array in your property is also changed. It may be, and I don't know because you haven't shown any code, that you are emptying the original array, and thus changing the array you have as a property
Solutions:
My preferred solution is to use the immutable versions of these classes for your properties; NSString, NSArray and instead of retain use copy
A second solution is to leave the properties as mutable, but write a custom setter for each of them that stores a mutableCopy of the object that you pass in.
In both of these cases, your property will be a copy of the object used to set the property, so that if the object is changed outside of your class it will not affect your class's properties.
edited to add, after a comment
If you declare your property as
#property (copy) NSArray wordRelations;
Then simply writing
vassal wordArray = tempArray;
will do the same thing and is cleaner and more readable..
I'm a little confused about synthesized properties. I have an array that I want to be accessible from other classes so this is my code:
MyClass.h
#interface MyClass : CCLayer {
NSMutableArray *myArray;
}
#property (nonatomic, retain) NSMutableArray *myArray;
MyClass.m
#synthesize myArray;
-(id)init
{
myArray = [[NSMutableArray alloc] init];
}
-(void)dealloc
{
[myArray release];
myArray = nil;
}
I am a little confused now..is myArray the same as self.myArray? Do I have to release self.myArray as well? Thanks.
You declared your property as retain, it means that it will be retained automatically if you will set is using self.myArray. So, you can simply create autoreleased array in your init method and set it as
myArray = [NSMutableArray array];
self.myArray = myArray;
in this case you are not have to release it in the dealloc method or anything else. And as dasblinkenlight said you have to use #synthesize if you want to be sure that self.myArray is linked with your myArray instance.
Assuming that your #synthesize directive looks like this
#synthesize myArray;
you do not need to do anything in addition to what you are already doing: your property stores its value in the instance variable of the same name.
EDITED : Removed the alternative that suggests setting self.myArray in the dealloc method.
Yes you do, the best method is to set the property nil and release your variable.
-(void)dealloc{
self.myArray = nil;
[myArray release];
[super dealloc];
}
The code you provided is not really correct.
No, accessing a property and accessing the field itself are not the same.
My guess is that you are looking at old obj C examples where it was necessary to create the field with the property.
You also have no #synthesize directive in your code.
In current obj C code there is no need to declare a field to back the property, the field and the getter and setter will be autosynthesized (generated by the compiler) for you.
The default field generation is the name of your property with an underscore in front of it.
When you access the field directly via _myArray you will bypass any retain or release code that is contained in the generated getter/setter and have to manually manage memory in a non ARC project.
So to sum up, you dont need your field definition, and you dont need a synthesize directive.
You access your field directly with _myArray, or the property via self.myArray
They are not the same thing, one goes through generated code which obeys your property definition as to retain, assign, copy and accessing the field directly bypasses these semantics altogether.
If you define your property as retain you will need to release it in dealloc
You can use either
self.myArray = nil;
which will handle the release or
[_myArray release];
_myArray = nil;
Although someone in a previous post said setting the property to nil in dealloc might cause a problem Ive never seen it actually happen in my apps, ymmv
To answer your questions:
I am a little confused now..is myArray the same as self.myArray?
Yes, but no. Both point to the same object, the same area in memory. If you read myArray or self.myArray, they're identical in behavior minus the message send overhead for self.myArray.
However if you assign to myArray, the object will not be retained. It will only be retained if you assign to self.myArray.
Do I have to release self.myArray as well?
No.
You can also choose to either release or set the property to nil. As long as the property is #synthesize'd both examples do the same thing:
-(void) dealloc
{
[super dealloc];
[myArray release];
}
-(void) dealloc
{
[super dealloc];
self.myArray = nil;
}
See here for a discussion of the pros/cons to each approach.
From the question I think you're the developer who should really be using ARC. You'll have less to learn and fewer technical problems down the road. I can't understate how important using ARC is in these days, specifically if you don't have much ObjC experience. Read this how to enable ARC for cocos2d or just use Kobold2D to be able to work with an ARC-enabled cocos2d out of the box.
In my last question (here), I had an issue where I was getting an EXC_BAD_ACCESS because I was releasing the variable I had just allocated:
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
[s release];
should have been
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
However, stack is a retained property of my class. It's declared like so:
#interface StateStack ()
#property (nonatomic, retain) NSMutableArray* stack;
#end
I was under the impression that when you assign a 'retain' variable, it automatically increments the retainCount of the object. So you are supposed to start by releasing your pointer (as here).
Why are these two cases different? Thanks!
Because you had to assign the property, not the instance variable. When you assign to the property it's going to retain the variable again and then you're not going to have the issue. Here's how your code should have been:
NSMutableArray* s = [[NSMutableArray alloc] init];
self.stack = s;
[s release];
This way you're not assigning to the variable, but using the property (that's, in fact, a method). If you did not release in this case then you'd have a memory leak in your code.
When you did stack = s you assigned directly to the instance variable and the array was never retained.
There is no such thing as a "retain variable". It's a retain property — meaning the setter method behind the property retains the new value and releases the old one. But assigning to a variable just assigns. In fact, the reason people generally recommend assigning directly to the instance variable in init is specifically so that it doesn't go through the setter, because the setter could conceivably have side effects you don't want in init (when your object isn't fully constructed yet).
Note: I'm talking about normal memory-management rules here. This is all different if you're using ARC. But I assume you would have mentioned if you were.
self.stack and stack are two completely different things. When you use stack, you are accessing an instance variable, not a property. This means that your accessor methods aren't called, which means automatic memory management isn't used. This is why you shouldn't release s in your example.
If you used self.stack instead, then you would be using a property. The compiler will treat self.stack = value exactly the same as [self setStack:value], and self.stack the same as [self stack]. Since accessors are being used, memory management will be taken care of to match the way you defined your property, and you should release a value after assigning it.
Maurício has the right answer: be sure to assign to the property to gain the benefits of #property. To clarify the point somewhat, try using code like this:
#interface StateStack : NSObject {
NSArray *_stack;
}
#property (nonatomic,retain) NSMutableArray *stack;
#end
#implementation StateStack
#synthesize stack=_stack;
#end
Now, if you try:
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
[s release];
You'll get an error, which will mean you tried to set the ivar rather than the property as intended. This mismatch between ivar name and property name is against Apple's recommendations, but it's a fine way to help you develop the habit of using property assignment when you intend to do so.
The following code is on Apple's website.
1) Inside the setMyArray method, is it necessary to release myArray before setting a new value to it? I thought setting the value of an object to a new object, will release the old object from memory.
2) Why does it say myArray = [newArray mutableCopy];, instead of simply saying myArray = newArray;? Is it necessary to pass a mutable copy to our property?
#interface MyClass : NSObject {
NSMutableArray *myArray;
}
#property (nonatomic, copy) NSMutableArray *myArray;
#end
#implementation MyClass
#synthesize myArray;
- (void)setMyArray:(NSMutableArray *)newArray {
if (myArray != newArray) {
[myArray release];
myArray = [newArray mutableCopy];
}
}
#end
EDIT:
Would it be the same if myArray was (nonatomic, retain)
Apple Documentation
copy
Specifies that a copy of the object should be used for assignment. (The default is assign.)
**The previous value is sent a release message**.
With respect to #1, yes you must release myArray before setting a new value to it, otherwise you'll leak. Note the property, it's labelled as a copy, meaning that myArray will hold an object with a reference count of one once its set.
And with regards to #2, because if you don't mutableCopy, you're not getting a copy of the object, you're simply pointing at the other thing. So, if the other thing goes away, you'll have a pointer to a dangling object. Bad things ensue from that point forward.
1 - In order to avoid a memory leak you must release the array before setting a new value to it.
2 - mutableCopy is required in the context you provided in order to geta copy of the object, and not just a pointer. If the original array goes away, you'd be left with a dirty pointer.
FYI, copies in Objective-C are shallow, not deep copies. This means that when you do [array copy] or [array mutableCopy], you get an array which is separate from the original array, but which still points to all the same objects. If you want to perform a deeper copy, you'll have to do it manually, for example by iterating through the original array, making copies of the contents, and adding those copies to a new array.
I have been developing an app for a while and now I have gotten to the "Instruments-Leaks" part.
I remember a thing that puzzled me about ivars a few months back when I was learning. I took the whole thing a bit on faith and just followed the way Apple and others did it. As far as I can read, the accessors generated by the SDK will take care of the memory management.
But how are ivars themselves initialized?
If I have an ivar like this in my interface;
#interface
{
NSArray *results;
}
#property(nonatomic, retain) NSArray *results;
#end
#implementation
#synthesize results;
If I during run time try to do this:
[self setResults:allReadyInitializedArray];
It will crash, telling me that this result object was not initialized. If I however do this:
self.results = [[NSArray alloc] init]; //Im assigning this property memory, but hasn't the SDK already done that?
[self setResults:allReadyInitializedArray];
it will work but it will apparently leak memory.
I was under the impression that using the generated
accessors would release the old value before setting the new, meaning
the above ought to come out with the old value released and the new with a +1 retain count.
Does it specifically have to do with the ivar being of type NSArray/NSMutableArray, I can't recall it has been a problem with other ivars.
The problem has been particular prominent in my XML parser, where I continuously need to set an ivar value, use it, overwrite this value, use the new value etc.
Would someone please help me outline the correct way "of going from": #property() -> #synthesize -> using the ivar -> to dealloc?
I have read the memory management documents, I have tried looking for some in debt documentation that was within my understanding, but it seems that even though I use ivars on a daily basis I don't understand what goes on behind the scenes.
All ivars initially set to nil so you need to instantiate them before use. It is really hard to say why setResults may produce errors without seeing its implementation.
self.results = [[NSArray alloc] init];
Here you create new array object using alloc method - its retain count equals 1. After that your setter method retains your array once more and so your 1st objects retain remains "unhandled" resulting in memory leak. To remove leak you can rewrite your code like:
self.results = [[[NSArray alloc] init] autorelease];
// or
self.results = [NSArray arrayWith...]; // any NSArray's convenience method that returns autoreleased object.
My understanding is that
self.results = anArray;
is the same as
[self setResults:anArray];
just because results is a property in this case.
The way setResults: is implemented is set by the #property (in this case it will retain the new value). So this means anArray will have a retain count of 1. After setting self.results, anArray will have a retain count of 2. This is why you want to release the previously used anArray.
That said, I don't understand why setResults: crashes when you're setting it. (Maybe it just crashes only when you try to use self.results, instead of setting it?)
I'm just a beginner myself, if something is wrong I strongly encourage everyone who reads this to let me know what is wrong or correct. Still learning this myself.
The way Apple would do this:
In the .h file
#property (nonatomic, retain) NSArray *results
In the .m file
#synthesize results;
- (id)init {
NSArray *anArray = [[NSArray alloc] init]; // retainCount = 1
self.results = anArray; // retainCount = 2
[anArray release]; // retainCount = 1, only one "left" is in self.results
}
- (void)dealloc {
[results release];
}