I have a sharedInstance which consists of many NSMutableArrays which holds all my form data that is spread across many views in my App.
An example of their declaration in .h looks like
#property (atomic, retain) NSMutableArray *i_date;
#property (atomic, retain) NSMutableArray *i_tailNumber;
#property (atomic, retain) NSMutableArray *i_pic;
#property (atomic, retain) NSMutableArray *i_sic;
and in .m (- init)
self = [super init];
_i_date = [NSMutableArray array];
_i_tailNumber = [NSMutableArray array];
_i_pic = [NSMutableArray array];
_i_sic = [NSMutableArray array];
The shared instance function is
+(id) sharedInstance
{
static id sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
EDIT:
I instantiate all the arrays with an empty string on the first view as seen below
[_fd.i_date addObject:#""];
[_fd.i_tailNumber addObject:#""];
Finally I interact with my data this way in the prepare for segue function
[_fd.i_date replaceObjectAtIndex:leg withObject:transferDate];
rinse and repeate
My problem, which I've spent many hours investigating today by reading various blogs, the documentation, and all the wonderful resources here at SO, is that my arrays lose any objects I place in them when I segue to another view.
When I hit the code line above, I get the classic
signal SIGABRT
error, which complains that my arrays are empty (0 objects), so it cannot replace anything.
I suspect I'm not handling my shared instance properly and that ... maybe, perhaps, multiple instances of my arrays are being allocated, but I'm certain that I'm turned around in my understanding.
Any ideas what is occurring? I think I provided all the necessary code snippets but if I forgotten something just ask. Thanks again!
Jesse
EDIT:
You guys are FAST! :D I created my OBJ-C file with ARC enabled, so I think I am using it. I'm a C++ guy, mostly so this optional hand holding mem stuff is natively ambiguous to me.
EDIT:
The complete error is
2013-04-10 20:25:03.239 ProjectName[4230:c07] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM replaceObjectAtIndex:withObject:]: index 0 beyond bounds for empty array'
*** First throw call stack:
(0x1caf012 0x10ece7e 0x1c65ba9 0x7c7e 0x478b87 0x478c14 0x1100705 0x342c0 0x270a64 0x1100705 0x342c0 0x34258 0xf5021 0xf557f 0xf46e8 0x63cef 0x63f02 0x41d4a 0x33698 0x1c0adf9 0x1c0aad0 0x1c24bf5 0x1c24962 0x1c55bb6 0x1c54f44 0x1c54e1b 0x1c097e3 0x1c09668 0x30ffc 0x222d 0x2155)
libc++abi.dylib: terminate called throwing an exception
Assuming you are not using ARC, your init method is using poor memory management. Since you are directly accessing the ivars (and not the properties) you need to retain the arrays:
_i_date = [[NSMutableArray alloc] init];
_i_tailNumber = [[NSMutableArray alloc] init];
_i_pic = [[NSMutableArray alloc] init];
_i_sic = [[NSMutableArray alloc] init];
You are not using the properties but you create autoreleased objects.
Either use the properties like
+(id) sharedInstance
{
static id sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
sharedInstance.i_date = [NSMutableArray array];
sharedInstance.i_tailNumber = [NSMutableArray array];
sharedInstance.i_pic = [NSMutableArray array];
sharedInstance.i_sic = [NSMutableArray array];
}
return sharedInstance;
}
or do as rmaddy suggested.
[_fd.i_date replaceObjectAtIndex:leg withObject:transferDate];
this lines crash, as the array yet doent have an object at index leg
try
[_fd.i_date addObject:transferDate];
You should alloc and init your NSMutableArrays:
_i_date = [[NSMutableArray alloc] init];
And then use your singleton class i.e.:
[[_fd sharedInstance] i_date] addObject...]
Unbeknownst, I used the function "didMoveToParentViewController", which is called after a new view is displayed if it's overloaded and used by the view. In it, I was clearing all the arrays, thinking that it would only be called WHEN moving BACK to the parent view, since the word "did" which is a past tense word. Since that isn't the case, I was clearing my arrays right after page load, which resulted in empty arrays during the segues.
Related
I have an NSMutableArray with the following property:
#property (nonatomic, strong) NSMutableArray *alarmTableArray;
alarmTableArray = [[NSMutableArray alloc]init];
FMDBDatabaseAccess *db = [[FMDBDatabaseAccess alloc] init];
alarmTableArray = [db getAlarm];
I tried releasing this array but I end up with EXC_BAD_ACCESS.
I am really worried about this.
How to release this array?
You're using the descriptor of "strong" which is an ARC term. This should be retain and if you just set the property to nil it will release it automatically. You should set it to nil in your viewDidUnload since your ViewWillDissappear only means your viewcontroller is leaving visibility and not that it is being destroyed.
Updated Answer
I think I know what you're trying to do. You want grab an array of rows from your SQL and store it in one of your array.
One of the techniques for getting rows of data from SQL and storing into a class instance variable array is to NOT return a temporary array but pass the class instance variable array as a reference into your method and modify the array directly.
So instead of this pseudo-code
-(NSMutableArray *)doSomething
{
NSMutableArray *tempArray;
while (DB select statement has found rows)
{
CockTail *objCT = [[CockTail alloc] init];
objCT.name = #"...";
objCT.price = #"...";
[tempArray addObject:objCT];
[objCT release];
}
return [tempArray autorelease];
}
// class instance variable array
instanceVarArray = [[NSMutableArray alloc] init];
instanceVarArray = [self doSomething]; // here is where you confusion arise
You can do it this way:
-(void)doSomething:(NSMutableArray *)paramArray
{
// remove previously fetched data
[paramArray removeAllObjects];
SQL select statement
while(has rows)
{
CockTail *objCT = [[CockTail alloc] init];
objCT.name = #"...";
objCT.price = #"...";
// NOTE: we are directly modifying our class instance variable array
// here since it was passed by reference :D
// and so there is no need to worry about releasing the array
[paramArray addObject:objCT];
[objCT release];
}
}
// Now all you do is pass in your class instance variable array
instanceVarArray = [[NSMutableArray alloc] init];
[self doSomething:instanceVarArray];
Original Answer
Um, maybe I am wrong but aren't you essentially throwing away that "alloc init" on the first line here when you assign the array something from your FMDBDatabaseAccess:
// LINE 1: this instance of NSMutableArray here is allocated
alarmTableArray = [[NSMutableArray alloc]init];
// LINE 2
FMDBDatabaseAccess *db = [[FMDBDatabaseAccess alloc] init];
// LINE 3:this line here essential breaks the pointer link point to the NSMutableArray instance on line 1
alarmTableArray = [db getAlarm];
Now unless you do
// LINE 4
[alarmTableArray retain];
Otherwise, your alarmTableArray was never allocated (since you overwrote the pointer link). And as a result, you've caused a memory leak as your profiler told you.
Doing a release now would give your that EXEC_BAD_ACCESS
What I think you want to do is this:
alarmTableArray = [[NSMutableArray alloc]init];
FMDBDatabaseAccess *db = [[FMDBDatabaseAccess alloc] init];
// this now uses the setter method (mutator method generated by #property) to do the copy
self.alarmTableArray = [db getAlarm];
Looking at your while loop, I have to ask why are you releasing a local scope variable?
CockTail *cocktailValues = [[CockTail alloc] init];
...
[cocktails addObject:cocktailValues];
[cocktailValues release];
Breakdown of each line of code above:
When you alloc and init the CockTail object the release/retain count is 0.
Adding the object to the NSMutableArray increases the release/retain count to 1.
Releasing the CockTail object after you added it to array reduce the release/retain count back down to 0.
Therefore, later when you release the NSMutableArray or try to access an object in it, the objects are already gone.
Remember the number one rule, only release what you retain.
#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
Hey all. I know this question's been asked but I still don't have a clear picture of memory management in Objective-C. I feel like I have a pretty good grasp of it, but I'd still like some correct answers for the following code. I have a series of examples that I'd love for someone(s) to clarify.
Setting a value for an instance variable:
Say I have an NSMutableArray variable. In my class, when I initialize it, do I need to call a retain on it?
Do I do
fooArray = [[[NSMutableArray alloc] init] retain];
or
fooArray = [[NSMutableArray alloc] init];
Does doing [[NSMutableArray alloc] init] already set the retain count to 1, so I wouldn't need to call retain on it? On the other hand, if I called a method that I know returns an autoreleased object, I would for sure have to call retain on it, right? Like so:
fooString = [[NSString stringWithFormat:#"%d items", someInt] retain];
Properties:
I ask about the retain because I'm a bit confused about how #property's automatic setter works.
If I had set fooArray to be a #property with retain set, Objective-C will automatically create the following setter, right?
- (void)setFooArray:(NSMutableArray *)anArray {
[fooArray release];
fooArray = [anArray retain];
}
So, if I had code like this: self.fooArray = [[NSMutableArray alloc] init]; (which I believe is valid code), Objective-C creates a setter method that calls retain on the value assigned to fooArray. In this case, will the retain count actually be 2?
Correct way of setting a value of a property:
I know there are questions on this and (possibly) debates, but which is the right way to set a #property?
This?
self.fooArray = [[NSMutableArray alloc] init];
Or this?
NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.fooArray = anArray;
[anArray release];
I'd love to get some clarification on these examples. Thanks!
According to Apple's Object Ownership Policy, any method that begins with the words alloc or new, or contains copy is owned by the caller.
To obtain ownership of an object, you must retain it.
So, in your first example, the retain is unnecessary because you already own the object.
The correct way to do this:
fooArray = [[NSMutableArray alloc] init];
Since autoreleased objects are owned by the current autorelease pool, you must call retain on them to gain ownership of them, so this example is correct:
fooString = [[NSString stringWithFormat:#"%d items", someInt] retain];
This would work fine as well:
self.fooString = [NSString stringWithFormat:#"%d items", someInt]; //retained by property setter
And for your last example using the property setter, this would be the correct way to do it:
NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.fooArray = anArray;
[anArray release];
Instead of having to do the above, I'd suggest the following solution:
self.fooArray = [NSMutableArray arrayWithCapacity:10];
arrayWithCapacity: will return an autoreleased NSMutableArray, which is the retain-ed by the property setter method. :)
Ideally you would want to use the accessors whenever possible, especially when dealing with objects as they help avoid many memory issues. So even for instance variables, you should do:
self.fooArray = ...;
instead of
fooArray = ...;
The reason why you should declare properties for object instance variables is because the memory management is slightly more complicated, and recreating it by hand each time is tricky. The correct setter for a nonatomic, retained property would look like:
- (void)setFoo:(NSArray *)aFoo {
if (foo == aFoo) {
return;
}
NSArray *oldFoo = foo;
foo = [aFoo retain];
[oldFoo release];
}
You are right about the instance variable having a retain count of 2 when you do something like this (assuming foo is retained):
self.foo = [[NSMutableArray alloc] init];
The first retain count is coming from alloc, and the second one from your synthesized setter. Any of these should work:
// longer, explicit version, releases immediately (more efficient)
NSMutableArray *aFoo = [[NSMutableArray alloc] init];
self.foo = aFoo;
[aFoo release];
// autoreleased, not so bad unless you're a memory management freak
self.foo = [[[NSMutableArray alloc] init] autorelease];
// an even shorter version of the above
self.foo = [NSMutableArray array];
To create private properties, you can declare them as a class extension in the .m implementation file. To give an example, consider a simple Person object, which has a name, and a boolean property didSave which simply indicates whether the object has been saved to some database or not. Since we don't want to expose this to the outside world, but still keep the benefits of properties inside the implementation file, we can create the header file will all instance variables (public, private, protected) and only public properties:
// Person.h
#interface Person {
NSString *name;
#private
BOOL didSave;
}
#property (nonatomic, retain) NSString *name;
#end
But declare private properties inside the implementation:
// Person.m
// property is declared as a class extension, making it
// invisible to the outside world.
#interface Person ()
#property BOOL didSave;
#end
#implementation
// synthesize as normal
#synthesize name, didSave;
#end
First of all, with this line:
fooArray = [[NSMutableArray alloc] init];
fooArray will automatically have a retain count of 1.
Second, yes, it's 2. And your guess on the setter implementation is correct.
Third, the latter one is right
I have a memory leak when i call a method that return me a string----
the method definition is as follows
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return dataArray;
}
this show a big memory leak
i also tried--- NSMutableArray *dataArray = [[[NSMutableArray alloc] init]autorelease];
but this time leack checking process gets hanged
i also cannot release that array before return
please help
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return dataArray;
}
Anything that uses the method read will expect to get back an object it does not own. However, as written here, dataArray is still owned at the point of return. You can't release it because that might make it go away altogether. You must, in this instance autorelease the array. You can either do this:
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[[NSMutableArray alloc] init] autorelease];
//picking data from database here
return dataArray;
}
or this
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return [dataArray autorelease];
}
You say "leak checking process get hanged" but I'm really not sure what you mean by that. Whether it hangs, crashes or plays the Botswana National Anthem, you definitely need to autorelease the returned array and any other problem is actually a different problem. Possibly, you are forgetting to retain the data elsewhere.
Another answer more...
There are many conventions in cocoa/cocoa-touch, there is one of them that says that if a method has the prefix init then you will have the ownership of that object (hence you have to release it)
This is NOT your case, hence if you do:
DatabaseReader *dbReader = [[DatabaseReader alloc] init];
NSMutableArray *mutArray = [dbReader read];
[dbReader release];
you are NOT supposed to release mutArray. BUT, the object created HAS to be released by someone. So you can do as JeremyP wrote. alloc/init and put it into a autorelease pool inside read method implementation. Or, you can do:
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [NSMutableArray array];
//IMPORTANT:
//Did you noticed that I am not using any method
//with init prefix for the creation of dataArray ?
//so I don't need to release by my self ;)
//picking data from database here
return dataArray;
}
Which is basically the same. ;)
Ownership of the returned object may be returned to the object that receives from this function. You may do some debugging with the object's retain count using something like this...
NSLog(#"Retain count: %i", [dataArray retainCount]);
Turn on the debugging console (Command + R in Xcode) to see the NSLog output.
I started dealing with NSOperations and (as usual with concurrency) I'm observing strange behaviour.
In my class I've got an instance variable:
NSMutableArray *postResultsArray;
when one button in the UI is pressed I initialize the array:
postResultsArray = [NSMutableArray array];
and setup the operations (together with dependencies).
In the operations I create a custom object and try to add to the array:
PostResult *result = [[PostResult alloc] initWithServiceName:#"Sth" andResult:someResult];
[self.postResultsArray addObject:result];
and while adding I get:
-[CFArray retain]: message sent to deallocated instance 0x3b40c30
which is strange as I don't release the array anywhere in my code (I did, but when the problem started to appear I commented all the release operations to be sure that they are not the case). I also used to have #synchronized section like below:
PostResult *result = [[PostResult alloc] initWithServiceName:#"Sth" andResult:someResult];
#synchronized (self.postResultsArray) {
[self.postResultsArray addObject:result];
}
but the problem was the same (however, the error was for the synchronized operation).
Any ideas what I may be doing wrong?
postResultsArray = [NSMutableArray array];
[NSMutableArray array] is a convient method which is equivalent to [[[NSMutableArray alloc] init] autorelease]. So there is an implicit (auto)release there. Since you're going to keep it, you have to use one of these 3 changes:
postResultsArray = [[NSMutableArray array] retain];
postResultsArray = [[NSMutableArray alloc] init];
Exploit the fact that a setter should retain the new value (if you declare as #property(retain)):
self.postResultsArray = [NSMutableArray array];