Instance of method leaking, iPhone - iphone

The following method shows up as leaking while performing a memory-leaks test with Instruments:
- (NSDictionary*) initSigTrkLstWithNiv:(int)pm_SigTrkNiv SigTrkSig:(int)pm_SigTrkSig SigResIdt:(int)pm_SigResIdt SigResVal:(int)pm_SigResVal {
NSArray *objectArray;
NSArray *keyArray;
if (self = [super init]) {
self.SigTrkNiv = [NSNumber numberWithInt:pm_SigTrkNiv];
self.SigTrkSig = [NSNumber numberWithInt:pm_SigTrkSig];
self.SigResIdt = [NSNumber numberWithInt:pm_SigResIdt];
self.SigResVal = [NSNumber numberWithInt:pm_SigResVal];
objectArray = [NSArray arrayWithObjects:SigTrkNiv,SigTrkSig,SigResIdt,SigResVal, nil];
keyArray = [NSArray arrayWithObjects:#"SigTrkNiv", #"SigTrkSig", #"SigResIdt", #"SigResVal", nil];
self = [NSDictionary dictionaryWithObjects:objectArray forKeys:keyArray];
}
return self;
}
code that invokes the instance:
NSDictionary *lv_SigTrkLst = [[SigTrkLst alloc]initSigTrkLstWithNiv:[[tempDict objectForKey:#"SigTrkNiv"] intValue]
SigTrkSig:[[tempDict objectForKey:#"SigTrkSig"] intValue]
SigResIdt:[[tempDict objectForKey:#"SigResIdt"] intValue]
SigResVal:[[tempDict objectForKey:#"SigResVal"] intValue]];
[[QBDataContainer sharedDataContainer].SigTrkLstArray addObject:lv_SigTrkLst];
[lv_SigTrkLst release];
Instruments informs that 'SigTrkLst' is leaking. Even though I have released the instance?
(I know that adding it to the array increments the retainCount by 1 but releasing it twice removes it from the array?)

You are initializing the instance (self), but then replacing it with a dictionary. Assuming that is what you actually want to do (see part 2), you need to release the old self before assigning a new one, and then you must retain the new dictionary so that your init method preserves the retain count. Note that this code returns a NSDictionary object, when your calling code expects a SigTrkLst, which is almost certainly a bad idea.
- (NSDictionary*) initSigTrkLstWithNiv:(int)pm_SigTrkNiv
SigTrkSig:(int)pm_SigTrkSig
SigResIdt:(int)pm_SigResIdt
SigResVal:(int)pm_SigResVal {
NSArray *objectArray;
NSArray *keyArray;
if (self = [super init]) {
self.SigTrkNiv = [NSNumber numberWithInt:pm_SigTrkNiv];
self.SigTrkSig = [NSNumber numberWithInt:pm_SigTrkSig];
self.SigResIdt = [NSNumber numberWithInt:pm_SigResIdt];
self.SigResVal = [NSNumber numberWithInt:pm_SigResVal];
objectArray = [NSArray arrayWithObjects:
SigTrkNiv,SigTrkSig,SigResIdt,SigResVal, nil];
keyArray = [NSArray arrayWithObjects:
#"SigTrkNiv", #"SigTrkSig", #"SigResIdt", #"SigResVal", nil];
[self release];
self = [NSDictionary dictionaryWithObjects:objectArray forKeys:keyArray];
[self retain];
}
return self;
}
Part 2: What is this supposed to do? Normally speaking, an -init... method should return an initialized class instance, starting from an allocated chunk of memory. The first part of your -init method looks right, other than the return value. For example, the following is a normal -init method:
- (id)initSigTrkLstWithNiv:(int)pm_SigTrkNiv
SigTrkSig:(int)pm_SigTrkSig
SigResIdt:(int)pm_SigResIdt
SigResVal:(int)pm_SigResVal {
if (self = [super init]) {
self.SigTrkNiv = [NSNumber numberWithInt:pm_SigTrkNiv];
self.SigTrkSig = [NSNumber numberWithInt:pm_SigTrkSig];
self.SigResIdt = [NSNumber numberWithInt:pm_SigResIdt];
self.SigResVal = [NSNumber numberWithInt:pm_SigResVal];
}
return self;
}

self = [NSDictionary dictionaryWithObjects:objectArray forKeys:keyArray];
When you are doing this what is happening to the SigTrkLst object that self supposed to point? Don't are you loosing reference to SigTrkLst object that was allocated and pointed by self? That object is never released as after this line there is no reference to that.
Though it is technically possible for an init method to return an object of different type, most probably this is not most of the people want and might indicate a potential bug.
EDIT: It seems that you are missing some basics of memory management for iOS. I strongly request you to read Memory Management Programming Guide. This may save you from lots of troubles.

Related

NSMutableArray release causes crash

I am using XCode for developing an iPhone app. I am new to this platform and need some help with a particular issue...
I have a method that processes some data and returns two integer values as NSNumber wrapped into a NSMutableArray.
Here is the method:
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray = [[NSMutableArray alloc] initWithCapacity:3];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
return [mutArray autorelease];
}
I call the above method from another method, where I copy the NSNumber stuff into local variables and then release the local copy of NSMutable array.
But the app crashes when releasing this NSMutable array (variable 'mutArray').
Here is the method:
-(void)doNinjaAction
{
NSMutableArray* mutArray = [self processPoints: x :y];
NSNumber* s1 = [[mutArray objectAtIndex:0] retain];
NSNumber* s2 = [[mutArray objectAtIndex:1] retain];
x = [s1 integerValue];
y = [s2 integerValue];
//...proceed with other stuff...
[mutArray autorelease]; //this is where the system crashes. same for 'release'
//instead of 'autorelease'
}
Can you please explain where I am going wrong with the process of memory release.
My understanding of the process is a bit shaky. Please help.
Because you're overreleasing the array. You alloc-init it in processPoints:, then you autorelease it - that's correct, this is how you dispose of its ownership.
After that, you don't need to and must not autorelease or release it once again. This is not malloc() from the standard library.
when you call the statement
NSMutableArray* mutArray = [self processPoints: x :y];
This itself acts as autorelease.
Hence releasing the array explicitly will cause the app to crash.
You are releasing mutArray more then once. Once in processPoints function and again in doNinjaAction.
To resolve the crash remove :
[mutArray autorelease];
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray = [[NSMutableArray alloc] initWithCapacity:3];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
[mutArray autorelase];
return mutArray;
}
try this one it'll resolve it.
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray =[[[NSMutableArray alloc] initWithCapacity:3]autorelease];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
return mutArray;
}
As #H2CO3 and #AppleDelegate suggested, it is right.
Still I would suggest to use ARC and convert your project to ARC enabled.
Go to Edit->Refactor->Convert to Objectiv-C ARC
Then you dont need to do any releases anywhere. It will take care itself of all the releases :)

Can I init an NSArray in a Singleton with specific values (like a const)?

I have a Singleton and I'd like to declare an NSArray, but I'd like to init it with 5 (non -consecutive) integers.
Right now it's declared in the .h>Interface, .h>#property, and .m>#synthesize.
How can I do this?
Thanks.
In your singleton's -init method, simply create and initialize it there:
- (id)init
{
_array = [[NSArray arrayWithObjects:[NSNumber numberWithInt:5], [NSNumber numberWithInt:50], [NSNumber numberWithInt:3.72], [NSNumber numberWithInt:5], [NSNumber numberWithInt:96], nil] retain];
return self;
}

How to define a static table of strings in iPhone / XCode?

I want a table of state-names for debug/trace messages as I develop my custom gesture. What is wrong with this declaration and its usage?
static NSDictionary *dictStateNames = nil;
#implementation MyCustomGesture
#synthesize state;
+(void)initStateNames {
if (dictStateNames == nil) {
dictStateNames = [NSDictionary dictionaryWithObjectsAndKeys:
#"StateBegan", [NSNumber numberWithInt:UIGestureRecognizerStateBegan],
#"StateCancelled", [NSNumber numberWithInt:UIGestureRecognizerStateCancelled],
#"StateChanged", [NSNumber numberWithInt:UIGestureRecognizerStateChanged],
#"StateEnded", [NSNumber numberWithInt:UIGestureRecognizerStateEnded],
#"StateFailed", [NSNumber numberWithInt:UIGestureRecognizerStateFailed],
#"StatePossible", [NSNumber numberWithInt:UIGestureRecognizerStatePossible],
#"StateRecognized", [NSNumber numberWithInt:UIGestureRecognizerStateRecognized],
nil];
}
}
-(id) init {
self = [super init];
if (self) {
[MyCustomGesture initStateNames];
state = UIGestureRecognizerStatePossible;
}
return self;
}
-(id) initWithTarget:(id)target action:(SEL)action {
self = [super initWithTarget:target action:action];
if (self) {
[MyCustomGesture initStateNames];
state = UIGestureRecognizerStatePossible;
}
return self;
}
+(NSString*) stateName: (UIGestureRecognizerState) state {
NSString *retName = [dictStateNames objectForKey:[NSNumber numberWithInt:state]];
if (retName == nil) {
return [NSString stringWithFormat:#"Unknown state (%#)", state];
} else {
return retName;
}
}
-(NSString*) currentStateName {
return [MyCustomGesture stateName:state];
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"%s (%#): %#", __FUNCTION__, [self currentStateName], event);
}
When you store a reference to an object in a static variable, you need to ensure that it doesn't get deallocated. So you can either send it a retain message, or create with alloc instead of a convenience creation method. For example:
dictStateNames = [[NSDictionary dictionaryWithObjectsAndKeys:
// List of objects and keys...
nil] retain];
or this...
dictStateNames = [NSDictionary alloc] initWithObjectsAndKeys:
// List of objects and keys...
nil];
Also, you can coalesce your stateNames getter and the initialization code into a single method, so you'll typically see Objective-C developers write a method like this:
+ (NSDictionary *)stateNames
{
static NSDictionary *stateNames;
if (stateNames == nil) {
stateNames = [NSDictionary alloc] initWithObjectsAndKeys:
// List of objects and keys...
nil];
}
return stateNames;
}
That way, there's no need to call it in an instance method (which would be wrong anyway, since a new dictionary would be created each time an instance is initialized, and unless you handled it differently, the previous one would be leaked).
On another (unrelated) note, consider rewriting your init method as follows:
- (id)init
{
return [self initWithTarget:nil action:NULL];
}
[NSDictionary dictionaryWithObjectsAndKeys] takes an object first followed by its key (as the method name suggests), which seems a bit backward but there it is.

NSArray - Memory Leak how to?

I have function which return NSArray, but it's generating memory leak, since i can't release the array after return line how can I release it?
Thanks.
-(NSArray *)readDataFromDatabase
{
return NSArray;
}
autorelease the array before returning:
- (NSArray*) readDataFromDatabase
{
// option 1: create an auto-released array
NSArray* a = [NSArray arrayWithObjects: ...];
return a;
// option 2: autorelease manually
NSArray* aa = [[[NSArray alloc] initWithObjects: ...] autorelease];
return aa;
}
Check apple's docs for autorelease

Leaking Memory on iPhone :(

I'm a beginner at C, Obj-C and the iPhone, and I'm trying getting to grips with a lot of terminology used. I hope one of ye can help with a problem I have been struggling with for a few days now.
My code below is a method which call up a nib containing a search field and a table. The table is populated from a search of the array created for 'theList' below. Using 'Instruments', I am getting a Leak at the line:
NSDictionary *theItem = [NSDictionary dictionaryWithObjectsAndKeys:clientName,#"Name",clientId,#"Id",nil]; , but I can't figure out why :(
I know it's probably a difficult question to answer, but if any one can be of any help!
- (void)editClient:(id)sender {
if (pickList == nil) {
pickList = [[PickFromListViewController alloc] initWithNibName:#"PickList" bundle:nil];
}
TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *theList = [[NSMutableArray alloc] init];
int i;
for (i=0;i < [appDelegate.clients count];i++) {
Client *thisClient = [appDelegate.clients objectAtIndex:i];
NSString *clientName = [[NSString alloc] initWithString: thisClient.clientsName];
NSNumber *clientId = [[NSNumber alloc] init];
clientId = [NSNumber numberWithInt:thisClient.clientsId];
NSDictionary *theItem = [NSDictionary dictionaryWithObjectsAndKeys:clientName,#"Name",clientId,#"Id",nil];
[theList addObject:theItem];
theItem = nil;
[clientName release];
[clientId release];
}
[pickList createSearchItems:theList :NSLocalizedString(#"Client",nil)];
[theList release];
appDelegate.returningID = [NSNumber numberWithInt: projectsClientsId];
[self.navigationController pushViewController:pickList animated:YES];
}
Thanks in advance!
This returns allocated NSNumber instance.
NSNumber *clientId = [[NSNumber alloc] init];
This line overwrites the above clientId with another instance of NSNumber, numberWithInt returns autoreleased object, since you haven't allocated memory for it you should not call release, it will be released automatically.
clientId = [NSNumber numberWithInt:thisClient.clientsId];
You are calling release on clientId so you get memory problem.
To fix it remove the first line above which is useless in this case and update the second one to:
NSNumber * clientId = [NSNumber numberWithInt:thisClient.clientsId];
Then remove the:
[clientId release]
Because the clientId will be released automatically.
EDIT: Re still have problems ...
I'm not sure how to you manipulate the clients in app delegate, otherwise the code should work ok, I created small example, omitting the parts that I can't see (app delegate and clients):
// command line utility - foundation tool project:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray * theList = [[NSMutableArray alloc] init];
int i = 0;
for (i = 0; i < 10; ++i)
{
NSString * clientName = [NSString stringWithString:#"client"]; //no need to release
NSNumber * clientId = [NSNumber numberWithInt:i];
NSDictionary * theItem = [NSDictionary dictionaryWithObjectsAndKeys:
clientName, #"name",
clientId, #"id",
nil];
[theList addObject:theItem];
}
for (id item in theList) for (id key in item) NSLog(#"%# - %#", key, [item objectForKey:key]);
[theList release];
[pool drain];
return 0;
}
You are creating clientID with [[NSNumber alloc] init], and then immediately overwriting it with an autoreleased NSNumber instance [NSNumber numberWithInt], and then you are releasing it later in your code, which you shouldn't do. Get rid of the [[NSNumber alloc] init] line and the [clientId release] line and that should fix it up a little.
Aside from the obvious leak of the NSNumber, there are a few other things I'd fix that may help. Most are fairly minor, but in my experience with Objective-C, less code == clearer code, something that is not equally true for languages like Bash or Perl. ;-)
- (void) editClient:(id)sender {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pickList == nil) {
pickList = [[PickFromListViewController alloc] initWithNibName:#"PickList" bundle:nil];
}
TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate*)[[UIApplication sharedApplication] delegate];
NSMutableArray *searchItems = [NSMutableArray array];
NSMutableDictionary *itemDict = [NSMutableDictionary dictionary];
for (Client *client in appDelegate.clients) {
[itemDict setObject:[client.clientsName copy] forKey:#"Name"];
[itemDict setObject:[NSNumber numberWithInt:client.clientsId] forKey:#"Id"];
[searchItems addObject:[[itemDict copy] autorelease]];
}
[pickList createSearchItems:searchItems :NSLocalizedString(#"Client",nil)];
[self.navigationController pushViewController:pickList animated:YES];
appDelegate.returningID = [NSNumber numberWithInt: projectsClientsId];
[pool drain];
}
There are a few mysterious points that make me suspicious:
The line just after the for loop tells pickList to do something with the NSMutableArray. That method should retain the new array, as well as release the old array if one exists. If you just overwrite the pointer, the old array will be leaked. (Also, this method is poorly named. Anonymous arguments (a colon with no preceding text) are legal in Objective-C, but considered extremely bad practice. Consider renaming the method to better express what it does.)
The next line seems to associate the pick list with a navigation controller. If it is custom code, make sure the -pushViewController:animated: method properly releases an existing pick list when a new one is specified.
Assigning to appDelegate.returningID is assumed to call the setter for a returningID property. Be sure that property retains or copies the NSNumber as necessary.
Memory leaks can be tricky to track down, even in Instruments, and you'll often find that it looks like Foundation classes (such as NSDictionary) are leaking like a sieve, but I have always been able to trace it back to an abnormality in my code. :-)