Beginner memory management - iphone

I need to know if the retaincount of objects should always be 0 if i want to have a good memory management in my code. I got the following code from a book. and there's a statement NSLog called after release = 2, So should i have to release it 2 more times so that the retaincount will be 0 ?
#import <Foundation/NSObject.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSValue.h>
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSNumber *myInt = [NSNumber numberWithInteger: 100];
NSNumber *myInt2;
NSMutableArray *myArr = [NSMutableArray array];
NSLog (#”myInt retain count = %lx”,
(unsigned long) [myInt retainCount]);
[myArr addObject: myInt];
NSLog (#”after adding to array = %lx”,
(unsigned long) [myInt retainCount]);
myInt2 = myInt;
NSLog (#”after asssignment to myInt2 = %lx”,
(unsigned long) [myInt retainCount]);
[myInt retain];
NSLog (#”myInt after retain = %lx”,
(unsigned long) [myInt retainCount]);
NSLog (#”myInt2 after retain = %lx”,
(unsigned long) [myInt2 retainCount]);
[myInt release];
NSLog (#”after release = %lx”,
(unsigned long) [myInt retainCount]);
[myArr removeObjectAtIndex: 0];
NSLog (#”after removal from array = %lx”,
(unsigned long) [myInt retainCount]);
[pool drain];
return 0;
}
Program Output
myInt retain count = 1
after adding to array = 2
after asssignment to myInt2 = 2
myInt after retain = 3
myInt2 after retain = 3
after release = 2
after removal from array = 1
UPDATE
The following code was taken from the Apples memory management document. They have retained a NSNumber object and its never been released, is this OK ?
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
// Make the new assignment.
_count = newCount;
}

You shouldn't be worried about the retain count of your objects, especially because the the NSArray or other objects might retain and then only later release things you pass to them. I'd strongly suggest focusing instead on Objective-C and Cocoa's memory management rules, which ensure that things are cleaned up when needed.

This is not to take away from existing answers, but to address your edit (Which, by the way, should REALLY be a separate question.):
They have retained a NSNumber object and its never been released, is this OK ?
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
// Make the new assignment.
_count = newCount;
}
Well it wouldn't be, if that was what they did.
You are confusing pointers with objects. Do you see the (NSNumber *) just before newCount. That '*' means that newCount is a pointer to an NSNumber object. Allow me to translate.
[newCount retain];
Retain the object referred to by the pointer 'newCount'.
[_count release];
Send a release message to the object currently referred to by the '_count' pointer.
_count = newCount;
Make the pointer '_count' refer to the object that is referred to by the pointer 'newCount'
Think of it like this, if you have a car you likely just call it myCar. When you say myCar you mean your car, the car you own. Let's say you buy a new car for a while you will call it newCar so that you can tell the difference. So you take ownership of your new car [newCar retain], and for a brief moment you own two cars. Now you sell your old car, which you still call myCar, [myCar release](very sad). But now when you say myCar you mean your newCar myCar = newCar. You no longer own your old car. And now your newCar is just myCar and you own, retain, that one. When you die, dealloc, your car will be sold,[myCar release].

Related

retaincount will be increased after copy an array

I am developing one application. In that I am performing search operation once.
see my search method:
-(void)searchData
{
//[A release];
B =[A copy];
[A release];
NSLog(#"A retain count %i",[A retainCount]);
NSLog(#"B retain count %i",[B retainCount]);
C = [[NSMutableArray alloc]init];
[C removeAllObjects];
for (int i=0; i<[B count]; i++) {
DataBaseFields *data = [B objectAtIndex:i];
NSRange range= [data.DBtitle rangeOfString:searchEvents.text options:NSCaseInsensitiveSearch];
if (range.length>0) {
[C addObject:data];
}
}
NSLog(#"shouldChangeTextInRange text is %#",searchEvents.text);
A = [C copy];
NSLog(#"A retain count %i",[A retainCount]);
NSLog(#"C retain count %i",[C retainCount]);
[tableView1 reloadData];
}
In this code A,B,C are three NSMutableArrays. I am not allocating the memory and didn't write properties for that arrays. In this if search operation display any data then Array A shows the retainCount at last 1. If there is no result then Array A shows the retainCount at last 32 or 46. So please tell me how to solve this one.
retainCount is useless. Don't call it.
Beyond being generically meaningless, you are seeing odd retain counts in this code because, as an implementation detail, the frameworks are likely calling retain/autorelease on the objects. Since the retain count doesn't reflect the # of times an object has been autoreleased, the retain count is extra meaningless in your code.
Note also that the removeAllObjects in this is unnecessary.
C = [[NSMutableArray alloc]init];
[C removeAllObjects];
Better yet: Since it appears that you are relatively new to iOS development, turn on ARC and be done with all those retain/release/autorelease calls entirely.

Memory Leak Field Variable

I do not undserstand why I cannot simply add [sortedArray release]; at the end of this method. Everytime I do, it crashes.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSString *workoutName = [event.assignedWorkout valueForKey:#"name"];
self.title = workoutName ;
NSMutableArray *sortedArray = [[NSMutableArray alloc]init];
// Sort ExerciseArray
int y = 0;
int x= 0;
do{
if ([[[exerciseArray objectAtIndex:y] index]intValue] == x){
[sortedArray addObject:[exerciseArray objectAtIndex:y] ];
x++;
}
if (y<[exerciseArray count]-1){
y++;
}else {
y=0;
}
}
while(x <= [exerciseArray count]-1);
exerciseArray = sortedArray;
[self.tableView reloadData];
}
If anyone can point me in the right direction or refer me to some documentation, it would be very much appreciated.
You are assigning the array to the exerciseArray variable and then you are releasing the array. The next time you try to access the exerciseArray variable, the array will be gone, because assigning it to the exerciseArray variable doesn't increase its retain count.
The array object's retain count is 1 when you first call alloc. When you assign it to exerciseArray, its retain value is still 1. Then, when you release it, its retain count drops to 0 and you can't expect to access the array again.
You should either not release it (my preference), or else explicitly retain it by calling:
[exerciseArray release];
exerciseArray = [sortedArray retain];
[sortedArray release];
Note the order of those two messages. The first line releases any existing object pointed to by the exerciseArray variable. The second line raises the new array's retain count to 2, and the second then drops it to 1, so the array is still retained. If you release sortedArray before assigning the new array to the exerciseArray variable, the retain count becomes 0 and you will lose it before you can assign it to the new variable.
You probably wanted to do:
[exerciseArray release];
exerciseArray = sortedArray;
[self.tableView reloadData];
BTW, you can sort an Array using a block with sortedArrayUsingComparator.

iOS - Passing a NSMutableArray via method return

How would one pass a NSMutableArray via method return.
I have it passing the array "spaces" so an array of 10 objects passes the 10 blocks but none of the information contained in those objects.
Thanks in advance
Edit: Basically I created another class that contains path information because my controller was getting a bit cluttered. So this new class I want call the "create" method which returns an NSMutableArray. The array is created fine in the path class but when the return statement fires it only passes the spaces and not the values or even a pointer.
currently it's
return path;
I've tried
return &path;
and that fails epically.
Edit2: Here is the issue I'm having unfortunately.
Still crashing
calling
newNode = [newNode copy];
causes a crash
- (NSMutableArray *) mutableFloobizwits {
NSMutableArray *array = [NSMutableArray array];
for (NSInteger i = 0; i < TheAnswerToTheUltimateQuestion; ++i) {
void(^MyBlock)(void) = ^{
NSLog(#"captured i: %ld", i);
};
MyBlock = [MyBlock copy]; //move the block from off the stack and onto the heap
[array addObject:[Floobizwit floobizwithWithBlock:MyBlock]];
[MyBlock release]; //the Floobizwit should've -retained the block, so we release it
}
return array;
}
I would set up your other class that returns the array of path objects as follows:
#implementation PathFactory
- (NSMutableArray*) create
{
// In your PathFactory object you create an array and make it autorelease so
// it becomes the callers responsibility to free the memory
NSMutableArray * pathArray = [[[NSMutableArray alloc] init] autorelease];
// Create a bunch of PathObject objects and add them to the mutable array
// also set these to autorelease because the NSMutableArray will retain objects
// added to the collection (ie It is the NSMutableArray's responsibility to ensure
// the objects remain allocated).
for (int i = 0; i < numberOfPaths; i++)
[pathArray addObject:[[[PathObject alloc] init] autorelease]];
// Just return the pointer to the NSMutableArray. The caller will need to
// call the retain message on the pointer it gets back (see next)
return pathArray;
}
#end
So in your caller code:
// create a tempory PathFactory (autorelease will make sure it is cleaned up when we
// are finished here)
PathFactory * newPathFactory = [[[PathFactory alloc] init] autorelease];
// grab the new array of Path objects and retain the memory. _newPathArray
// is a member of this class that you will need to release later.
_newPathArray = [[newPathFactory create] retain];

Big time Leaking in Objective-C Category

I created a custom NSString Category which lets me find all strings between two other strings. I'm now running into the problem of finding that there are a lot of kBs leaking from my script. Please see code below:
#import "MyStringBetween.h"
#implementation NSString (MyStringBetween)
-(NSArray *)mystringBetween:(NSString *)aString and:(NSString *)bString;
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
NSArray *firstlist = [self componentsSeparatedByString:bString];
NSMutableArray *finalArray = [[NSMutableArray alloc] init];
for (int y = 0; y < firstlist.count - 1 ; y++) {
NSString *firstObject = [firstlist objectAtIndex:y];
NSMutableArray *secondlist = [firstObject componentsSeparatedByString:aString];
if(secondlist.count > 1){
[finalArray addObject:[secondlist objectAtIndex:secondlist.count - 1]];
}
}
[autoreleasepool release];
return finalArray;
}
#end
I admit that I'm not super good at releasing objects, but I had believed that the NSAutoreleasePool handled things for me.
The line that is leaking:
NSMutableArray *secondlist = [firstObject componentsSeparatedByString:aString];
Manually releasing secondlist raises an exception.
Thanks in advance!
No, this is the line that is leaking:
NSMutableArray *secondlist = [[NSMutableArray alloc] init];
And it isn't that big of a leak (just an empty mutable array). Still, don't do that.
In particular, the line:
secondlist = [[firstlist objectAtIndex:y] componentsSeparatedByString:aString];
Is assigning over the reference to the empty mutable array.
Also FinalArray should be named finalArray.
finalArray is leaking. You should autorelease it before returning it but make sure you do it either before allocating the autorelease pool or after releasing it.

Executing Blocks From NSArray?

I was just thinking, as you can treat Blocks like objects if I create two of them and then add them to an NSArray is there a way to execute them from the array?
int (^Block_001)(void) = ^{ return 101; };
int (^Block_002)(void) = ^{ return 202; };
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];
EDIT: Update for clarity Per #davedelong's excellent answer
int (^Block_001)(void) = [^{ return 101; } copy];
int (^Block_002)(void) = [^{ return 202; } copy];
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];
[Block_001 release];
[Block_002 release];
#KennyTM and #David are correct, but your code is potentially wrong. Here's why:
When creating an NSArray with objects, it will retain the objects put into it. In the case of blocks, it's using the Block_retain function. This means that the array has retained the blocks that you created, but that live on the stack (blocks are one of the very rare examples of Objective-C objects that can be created on the stack without delving into absurd tricks). That means that as soon as this method exits, your array now points to garbage, because the blocks it was pointing to no longer exist. To do this properly, you should do:
int (^Block_001)(void) = [^{ return 101; } copy];
int (^Block_002)(void) = [^{ return 202; } copy];
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];
[Block_001 release];
[Block_002 release];
By invoking copy on the block, you're explicitly moving the block off of the stack and onto the heap, where it can safely remain after the method/function exits. Then after you've added the blocks to the array, you have to balance your copy (because of the NARC rule) with a subsequent call to release. Make sense?
Sure, you just invoke it with () like any other block, but you need to typecast the value you retrieve from NSArray. Here's an example (with an added typedef, because otherwise my head hurts):
typedef int (^IntBlock)(void);
IntBlock Block_001 = ^{ return 101; };
IntBlock Block_002 = ^{ return 202; };
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];
int x = ((IntBlock)[array objectAtIndex:0]) (); // now x == 101
Of course you can.
int (^x)(void) = [array objectAtIndex:0];
printf("%d\n", x()); // prints 101.