memory management: am i doing something wrong here? - objective-c

In a very small number of cases in my iphone app, I get a crash after the for loop in the code below:
ABAddressBookRef addressBookInit = ABAddressBookCreate();
CFMutableArrayRef abContacts =
(CFMutableArrayRef)ABAddressBookCopyArrayOfAllPeople(addressBookInit); // get array of all contacts
CFArraySortValues (abContacts, CFRangeMake(0, CFArrayGetCount(abContacts)), (CFComparatorFunction)ABPersonComparePeopleByName, (void *)ABPersonGetSortOrdering());
NSArray *copypeople = (NSArray *) abContacts;
NSMutableArray *tempTheadlist = [[NSMutableArray alloc] init];
for (int i=0; i < copypeople.count; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
ABRecordRef record = [copypeople objectAtIndex:i];
if (blah blah)
[tempThreadList addObject: someObject];
[pool release];
}
// POINT OF CRASH AFTER LOOP ENDS
if (tempTheadlist.count > 0)
[NSThread detachNewThreadSelector: #selector(loading_pictures:) toTarget:self withObject:tempTheadlist];
[tempTheadlist release];
[copypeople release];
CFRelease(addressBookInit);
Any reason why it should crash at any point here?

(1) ABAddressBookCopyArrayOfAllPeople() returns a CFArrayRef, not a CFMutableArrayRef.
(2) All the casts between CFArrayRef, CFMutableArrayRef, and NSArray are irrelevant. NSArray and CFArray are synonymous.
(3) The autorelease pool in the for() loop is irrelevant. There aren't any new objects being created in the for() loop and, thus, nothing to fall in the pool. Nor will objectAtIndex: retain/autorelease the objects.
(4) That you are sorting the array returned by ABAddressBookCopyArrayOfAllPeople() may likely be a crash trigger. That function is declared as returning a CFArrayRef() and it should be treated as immutable.
If the app is crashing, post the backtrace of the crash. Without that, it is hard to tell what is specifically triggering the crash. Is it crashing in the thread that ran the above code? ... or crashing in the newly created thread?

Related

Using Autoreleased Objects in iOS apps

To return a NSArray or NSDictionary, I have seen most people use the below implementation and this is also what some books suggest. (iOS Development A Practical Approach - )
OPTION 1
-(NSArray*)listOfStudents{
NSMutableArray *temp = [[NSMUtableArray alloc] init];
//Add elements to the array
//
//
//
NSArray *students = [NSArray arrayWithArray:temp];
return students;
}
-(void)viewWillAppear{
self.studentsList = [self listOfStudents];
}
But can this same be done by the below way also?
OPTION 2
-(NSArray*)newListOfStudents{
NSMutableArray *temp = [[NSMUtableArray alloc] init];
NSArray *students = [[NSArray alloc]initWithArray:temp];
[temp release];
//Add elements to the array
//
//
//
return students;
}
-(void)viewWillAppear{
NSArray *array = [self newListOfStudents];
self.studentsList = array;
[array release];
}
Assume these methods are called in the main thread itself.
Interms of memory usage , I think that the second option is good, because it does not create autoreleased objects, because they are released only at when the autorelease pool is drained.
I assume that the main autorelease pool is drained only when the app quits. So if the method in OPTION 1 is used many times ,(since they are getting called in ViewWillAppear) I think that many lists will be in autorelease pool being released only when the app quits.
So is the OPTION 2 approach the better approach?
UPDATE:
I have updated the viewWillAppear implementation for better clarity.
I think in the second example you meant to call
self.studentsList = [self newListOfStudents];
In case that studentsList is a retained property, this would leak now.
Also, that temp array in both examples is just useless overhead. In the second example it's plain nonsense.
The cleanest solution is
-(NSArray *)listOfStudents {
NSMutableArray *list = [NSMutableArray array];
// Add things to array
return list;
}
Two more advices:
1) you might run the static analyzer over your code, which will point to memory issues.
2) if you feel more confident with memory management, switch over to ARC.

Why does my object still work after countless releases?

I can never seem to deallocate my NSMutableString as shown below. The initial retain count should be 1, but after releasing several times, the string is still usable like nothing happened!
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
NSMutableString* s = [[NSString alloc]initWithString:#"AAA"];
[s release];
[s release];
[s release];
[s release];
[s release];
NSLog(#"%#",s);
[pool drain];
return 0;
}
Of course, if I use Analyze it still tells me that I release a released object on the second release.
Scott's answer is the correct general one, but in this particular case, the reason is that NSString literals (i.e. #"") are uniqued compile-time constants and do not actually do anything at all when retained and released. Your assignment of it to an NSMutableString* does not actually make it an NSMutableString, so what you've written is equivalent to
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
Releasing an object tells the runtime that it can destroy the object, at least as far as you're concerned, but it doesn't require that the object be destroyed immediately: After your first [s release], Cocoa is free to do whatever it pleases with the memory formerly used by s. It might give that memory to the next object that does an alloc, in which case your later attempts to access s will result in a fiery runtime crash… or it might not need that memory right away, in which case you might get away with accessing a released object.
The rule of thumb is less "I've released this object, which means it no longer exists" and more "I've released this object, which means it's no longer guaranteed to exist."

Assign nil to NSString within Autorelease pool

I'm making a great deal of NSString manipulations within an autorelease pool. Problem is my program will sometimes crash before the pool drains. I'm wondering if there is a way to circumvent this problem by assigning nil to NSString. The assignment to userLetters happens a lot. See code below
Before
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
usersLetters = [usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"];
[pool drain];
After
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *temp = [usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"]; //remove that found character so it can't be reused again
usersLetters = nil;
usersLetters = temp;
temp = nil;
[pool drain];
I doubt what assigning to nil will help in what you want to achieve.
(I assume you mean that your program crashes because the memory is exhausted, otherwise, it is much more likely that you released to often somewhere, you should also run the Status Analyzer over your code.)
What you can do is to send a retain message to all objects that you still need (in your case usersLetters) and drain the pool afterwards. The objects that you still need should then have a retain count of 1, all other autoreleased objects should have been deallocated.
In your case, this would be
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
usersLetters = [[usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"] retain]; //sending retain to an object increases its retain count by 1 and returns the object itself
// some more stuff that needs memory
[pool drain];
// ...
[usersLetters release];
[pool release];
You don't mention if your project targets iOS or Mac OS X. If the latter, the best way to resolve this issue may be to simply use garbage collection.
If GC is not an option (which it is not on iOS), the idiomatic way to deal with this is to wrap a nested autorelease pool around your inner, fast-allocating operations. In this case you must retain any objects which need to outlive the nested pool, as mrueg has explained.

When to release. Instruments says I have a leak

Sometimes I wonder where I would be able to get great information other than from the stack overflow community. I suppose the Objective-C memory management handbook wouldn't be bad, but I feel like you guys can tell me why as opposed to just what to do.
I have the following code:
NSString* rawTickerData = [[NSString alloc] initWithData: op.requestData encoding:NSUTF8StringEncoding];
NSArray* lines = [rawTickerData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for(NSString* line in lines)
{
NSArray* fields = [line componentsSeparatedByString:#","];
if([fields count] > 1)
{
[self.tickerData addObject:fields];
}
}
[rawTickerData release]
Instruments tells me that fields is leaking, but trying to release it after the if statement or doing an autorelease gives an EXC_BAD_ACCESS.
The same thing happens for lines. Releasing it anywhere or trying to do autorelease gives EXC_BAD_ACCESS or "trying to double free" (for autorelease).
Yet, instruments is still saying that these are leaking. Am I missing something?
Unless I'm missing something, the code you've posted is correct. fields is an autoreleased object that gets retained when added to self.tickerData. lines is also autoreleased, so it isn't leaking (at least not in the code shown).
If you're leaking anywhere, it's because you're not properly cleaning up self.tickerData. If you comment out the [self.tickerData addObject:fields]; line, are you still getting leaks reported? If not, make sure you're calling [tickerData release] (or something similar, like self.tickerData = nil) somewhere, probably in your dealloc implementation.
In these cases the best practice is to release your object's at the end of each loop, if you retained them. In this case your fields variable gets an autorelease object, the best option is to use an NSAutoReleasePool to release your objects at the end of the loop.
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
NSString* rawTickerData = [[NSString alloc] initWithData: op.requestData encoding:NSUTF8StringEncoding];
NSArray* lines = [rawTickerData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for(NSString* line in lines)
{
NSArray* fields = [line componentsSeparatedByString:#","];
if([fields count] > 1)
{
[self.tickerData addObject:fields];
}
[pool drain];
}
[rawTickerData release];
This will flushes all the autoreleased objects if they are don't needed. If you dont use autorelease pool, your objects are living until your method terminates.
If this doesn't resolve your leak problem i suggest to initialize your array like this:
NSArray *lines = [[NSArray alloc]initWithArray:[line componentsSeparatedByString:#","]];
//and release at the end of the loop
[lines release];

Function works even after pool drain please help

we add an object to the pool and when the pool is drained the objects are flushed out of memory, and if i don't add them into the pool they will remain into the memory and can be accessed after the calling of pool drain method. What i have done in my code is that i have not added the object of my class into the pool and have called the method after the pool drain here's my code
#import <Foundation/Foundation.h>
#interface Myclass : NSObject
{
}
-(void)fun;
#end
#implementation Myclass
-(void)fun
{
NSMutableArray *arr = [[NSMutableArray alloc]init];
char ch[10];
NSString *str;
for(int i =0;i<3;i++)
{
scanf("%s",ch);
str = [NSString stringWithCString:ch];
[arr addObject:str];
}
for(int i =0;i<3;i++)
{
NSLog(#"The values of mutable array are: %#", [arr objectAtIndex:i]);
}
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Myclass *obj = [[Myclass alloc]init];
[obj fun];
// insert code here...
NSLog(#"\nEnter pool drain");
[pool drain];
[obj fun];
return 0;
}
now when the fun method is called after pool drain each time i add a value to the array i get an error which says
NSautoreleaseNoPool(): Object 0x105a80 of class NSCFString autoreleased with no pool in place - just leaking Stack:(0x4dlfof ox3de442)
but even after this msg is shown i continued to add the data to my array it was working fine but every time showed the above msg. Can you please tell me why is this so?
Also i wanted to know is their any function with the help of which we can clear the console screen i tried ncurses.h but was not able to do that.
Please help me out regarding these two problems
You don't add any of your objects to the autorelease pool. When it is drained, it is empty. To add objects to the autorelease pool you must autorelease them. e.g. in fun
NSMutableArray *arr = [[[NSMutableArray alloc]init] autorelease];
In main:
Myclass *obj = [[[Myclass alloc]init] autorelease];
With those two modifications, you will see one of two things happen:
on the second [obj fun] after the drain you might get an exception for sending a message to a dealloc'd object.
you might get lucky and the memory for obj is still intact on the second [obj fun] in which case you should see a message posted to the console log saying that arr will leak because there is no autorelease pool in place.
Edit following the comment
This message
NSautoreleaseNoPool(): Object 0x105a80 of class NSCFString autoreleased with no pool in place - just leaking Stack:(0x4dlfof ox3de442)
is occurring because the method invoked by this line:
str = [NSString stringWithCString:ch];
tries to put the string into an autorelease pool before returning it to you. However, you have already drained the only pool you ever had, so the attempt fails. The string will therefore leak.