How to handle returned object from function to avoid memory leaks? - iphone

Suppose I have a function
- (NSString *)fullNameCopy {
return [[NSString alloc] initWithFormat:#"%# %#", self.firstName, self.LastName];
}
Can somebody tell me how to call this function, how to assign its value to a new object, and how then to release it, avoiding memory leaks, and bad access.
Would it be like
NSSting *abc = [object fullNameCopy];
// Use it and release
[abc release];
or I should alloc abc string too ?
Update:
The point here, Can I return non-autorelease objects from a function and then release them in the calling function. As per Obj-C function naming conventions, a function name containing alloc or copy should return object assuming that calling function has the ownership.
As in above case, my function "fullNameCopy" return a non-autoreleased abject, and I want to release them in the calling function.

You are right. Since the method name contains the word ‘copy’, Cocoa convention dictates that the method returns an object that is owned by the caller. Since the caller owns that object, it is responsible for releasing it. For example:
- (void)someMethod {
NSString *abc = [object fullNameCopy];
// do something with abc
[abc release];
}
Alternatively, you could use -autorelease instead of -release:
- (void)someMethod {
NSString *abc = [[object fullNameCopy] autorelease];
// do something with abc
}

Refer this post
UPDATE:
- (NSString *)fullNameCopy {
NSString *returnString = [NSString stringWithFormat:#"%# %#", self.firstName, self.LastName]; // Autorelease object.
return returnString;
}
-(void) someFunction {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *fullName = [self fullNameCopy];
[pool release]
}

Like This:
- (NSString *)fullName {
NSString * retVal = [[NSString alloc] initWithFormat:#"%# %#", self.firstName, self.LastName];
return [retVal autoRelease];
}
Then
NSSting *abc = [object fullName];

return [[[NSString alloc] initWithFormat:#"%# %#", self.firstName, self.LastName]autorelease];

Related

Return a variable from inside a block

Below I have some code which should return an array, the response from the server happens in a block:
- (NSMutableArray *)getArray
{
NSMutableDictionary* params =[NSMutableDictionary dictionaryWithObjectsAndKeys:
#"pending", #"command",
#"2" , #"userID",
nil];
[[API sharedInstance] commandWithParams:params
onCompletion:^(NSDictionary *json) {
//result returned
if ([json objectForKey:#"error"]==nil) {
NSMutableArray *res = [[NSMutableArray alloc] init];
[res addObject:#"1234"];
RETURN HERE
} else {
//error
[UIAlertView title:#"Error" withMessage:[json objectForKey:#"error"]];
}
}];
}
After parsing the data and creating an array I want to return the array I have created for the getArray method call. So far, after hours of trying I have not had any luck, even with trying some suggestions from previous questions on stackoverflow. Any help would be appreciated.
pass the block as a param to the function
- (NSMutableArray *)getArray:(void (^)(NSArray *))block {}
And then replace the RETURN HERE with block(res);
I'd create a method in somewhere in the class let's say - (void)arrayFetched:(NSArray *)fetchedArray.
Then you modify your code like this:
//...
if ([json objectForKey:#"error"]==nil) {
__weak NSMutableArray *res = [[NSMutableArray alloc] init];
[res addObject:#"1234"];
dispatch_async(dispatch_get_main_queue(), ^{
[self arrayFetched:[res copy]];
});
//...
and then do want you want with the array in the arrayFetched: method.

Adding custom defined objects to NSMutableArray overwrite the whole array

-(id) initWithData:(NSString *)lastName: (NSString *)firstName{
self->firstname = firstName;
self->lastname = lastName;
return self;
}
-(void) printData {
NSLog(#"firstname: %#", firstname);
NSLog(#"lastname: %#", lastname);
}
so whenever I create a new object using the above init function. And Add objects to a NSMutableArray, using the addObject function.
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
CustomObject *tempObject = [[CustomObject alloc] initWithData: #"smith": #"john"];
CustomObject *tempObjectb = [[CustomObject alloc] initWithData: #"brown": #"william"];
[objectArray addObject:tempObject];
[objectArray addObject:tempObjectb];
[[objectArray objectAtIndex:0] printData];
[[objectArray objectAtIndex:1] printData];
objects at index 1, and 0 always equal the whichever object was added to the array last.
This also happens if I use a for loop, or have more than 2 objects, all values when printed, turn to the values of the last added object to the objectArray. Let me know if there is any information that I am missing.
Is there something that I am missing?
Fix your initWithData:lastName: implementation as following:
-(id) initWithData:(NSString *)lastName: (NSString *)firstName
{
self = [super init];
if ( nil != self ) {
self->firstname = [firstName retain];
self->lastname = [lastName retain];
}
return self;
}

Invalid Selector sent to instance - objectForKey:

I get an error when running my code. The culprit is me accessing a string from a plist below:
NSString *sImageFile = [dictionary objectForKey:#"answerCorrect"];
NSLog(#"%#",sImageFile);
I have this in my cocos2d Init shown here:
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
NSUserDefaults *sud = [NSUserDefaults standardUserDefaults];
NSString *ctlLang = [sud valueForKey:#"ctlLang"];
NSNumber *questionState = [sud valueForKey:#"questionState"];
NSNumber *scoreState = [sud valueForKey:#"scoreState"];
gameArray = (NSMutableArray *)[sud valueForKey:#"gameArray"];
for (NSString *element in gameArray) {
NSLog(#"\nQL gameArray value=%d\n", [element intValue]);
}
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:ctlLang];
dictionary = [NSDictionary dictionaryWithContentsOfFile:finalPath];
NSString *sImageFile = [dictionary objectForKey:#"answerCorrect"];
NSLog(#"%#",sImageFile);
}
}
The printing of the string works fine in the init section of the scene. The problem occurs in a method I define later. For some reason it is not returning the string in the method shown here:
-(void) checkAnswer: (id) sender {
CGSize size = [[CCDirector sharedDirector] winSize];
CCMenuItemSprite *sAnswer = (CCMenuItemSprite *)sender;
NSLog(#"Checking Answer Tag is ---> %d",sAnswer.tag);
NSString *sImageFile = [dictionary objectForKey:#"answerCorrect"];
NSLog(#"%#",sImageFile);
if ([question.answer integerValue] == sAnswer.tag) {
//...
}
}
What am I missing here? the program bombs at the NSLog statement.
You assign the object returned by dictionaryWithContentsOfFile: to the dictionary ivar but you do not claim ownership of it by sending it a retain message to it:
dictionary = [NSDictionary dictionaryWithContentsOfFile:finalPath];
The method dictionaryWithContentsOfFile: returns an object you do not own. Probably, by the time checkAnswer: is executed the object has already been deallocated. You need to retain it:
dictionary = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
Or use alloc-initWithContentsOfFile: instead, which returns an object you own:
dictionary = [[NSDictionary alloc] initWithContentsOfFile:finalPath];
And the same goes for the gameplay ivar. You do not own the object returned by valueForKey: and you need to retain it. So this line:
gameArray = (NSMutableArray *)[sud valueForKey:#"gameArray"];
should be:
gameArray = [[sud valueForKey:#"gameArray"] retain];
I don't think you are retaining the autoreleased dictionary created in your init method. You set the dictionary ivar but don't retain it. When you access it later, it is likely not valid anymore

IPhone - copyWithZone leak

Testing my app on the device it returns a leak whe i call the copy of a custom object ande i can't understand why.
this is the call:
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:5];
for (SinglePart *sp in [copyFrom partList]) {
[arr addObject:[sp copy]];
}
self.partList = arr;
[arr release];
this is the method:
- (id)copyWithZone:(NSZone *)zone {
SinglePart *copy = [[[self class] allocWithZone:zone] initWithSinglePart:self];
[copy loadImage];
return copy;
}
this is the method that is called by copyWithZone:
- (id)initWithSinglePart:(SinglePart *)copyFrom {
if (self = [super init]) {
self.imagePath = [copyFrom.imagePath copy];
self.color = [UIColor colorWithCGColor:copyFrom.color.CGColor];
self.hasOwnColor = copyFrom.hasOwnColor;
self.blendingMode = copyFrom.blendingMode;
}
return self;
}
copy returns a new object with retain count 1. Meaning you need to release the new object, which you are not doing.
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:5];
for (SinglePart *sp in [copyFrom partList]) {
SingPart *theCopy = [sp copy];
[arr addObject:theCopy];
[theCopy release];
}
self.partList = arr;
[arr release];
Even your custom copyWithZone: method inits an object, but does not autorelease it, which is the expected behavior of a copy method. Copy must be balanced just like a retain or init, meaning you must balance it with release at some point.
Lastly, your initWithSinglePart: method leaks the imagePath as well. In this case if you declare the imagePath property as copy instead of retain then you don't need to do this manually at all. Then you simply assign the value and let the property setter do it for you.
// Header
#property (copy) NSString *imagePath;
// Now this will do the copy for you
self.imagePath = copyFrom.imagePath;
Also, is the property imagePath defined with retain or copy semantics?
If so you need to add an autorelease here:
self.imagePath = [[copyFrom.imagePath copy] autorelease];
because the default setter will retain/copy it too.
So, you either need to autorelease, or omit the "self." to bypass the default setter.
You are making a copy of sp and then adding it to the array. The array then retains the object so your retain count is now 2.
In the end you release arr, thus making the retain count of it's items 1.
You should either add another release to the sp objects, or not use copy.
Try this:
self.partList = [NSMutableArray arrayWithCapacity:5];
for (SinglePart *sp in [copyFrom partList]) {
[arr addObject:sp];
}

Iphone: Passing objects and Multiple threads

Im having some trouble passing an NSNumber object to different threads.
I call a function on viewDidload that loads up some objects from core data as a background process. which calls another function which loops through the loaded objects to see if there are any images associated with it alredy downloaded. if its not present, download the images asynchronously and save it locally. The thing is I need to perform startDownloadFor:atIndex: on the main thread. But the application crashes because of the NSNumber object thats being passed. here is the code..
- (void)viewDidLoad {
...
...
[self performSelectorInBackground:#selector(loadImages) withObject:nil];
}
-(void)loadImages{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
...
...
[self fillInImages];
[pool release];
}
-(void)fillInImages{
NSString *imageURL;
for (int i=0; i < [dataManager.objectList count]; i++) {
...
if ([dataManager.RelatedImages Image] == nil) {
//[self startDownloadFor:imageURL atIndex:[NSNumber numberWithInt:i]; // << WORKS FINE
[self performSelectorOnMainThread:#selector(startDownloadFor:atIndex:) withObject:(imageURL, [NSNumber numberWithInt:i]) waitUntilDone:YES]; // << CRASHES
...
}else {
...
}
...
}
...
}
-(void)startDownloadFor:(NSString*)imageUrl atIndex:(int)indexPath{
NSString *indexKey = [NSString stringWithFormat:#"key%d",indexPath];
...
}
what is the right way of doing this?
Thanks
I've never seen that syntax passing more than one object to a selector - is that valid objective-c code? also, in your startDownloadFor:atIndex: you're passing in an NSNumber but the type for the second parameter on that selector is (int) - that can't be good ;)
The docs for performSelectorOnMainThread: say that the selector should take only one argument of type id. You're passing an invalid selector so I think that it's getting very confused about where the NSNumber is.
To fix it, pass an NSDictionary conatining the number and the image URL i.e.
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:imageURL, #"imageURL", [NSNumber numberWithInt:i], #"number", nil];
[self performSelectorOnMainThread:#selector(startDownload:) withObject:dict waitUntilDone:YES];
and
//-(void)startDownloadFor:(NSString*)imageUrl atIndex:(int)indexPath{
- (void)startdownload:(NSDictionary *)dict {
NSURL *imageURL = [dict objectForKey:#"imageURL"];
int indexPath = [[dict objectforKey:#"number"] intValue];
You are trying to pass 2 arguments into performSelectorOnMainThread:withObject:waitUntilDone: while the method only supports passing one argument.
You need to use NSInvocation to send more arguments (or use an NSDictionary like dean proposed).
SEL theSelector;
NSMethodSignature *aSignature;
NSInvocation *anInvocation;
theSelector = #selector(startDownloadFor:atIndex:);
aSignature = [self instanceMethodSignatureForSelector:theSelector];
anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector:theSelector];
[anInvocation setTarget:self];
// indexes for arguments start at 2, 0 = self, 1 = _cmd
[anInvocation setArgument:&imageUrl atIndex:2];
[anInvocation setArgument:&i atIndex:3];
[anInvocation performSelectorOnMainThread:#selector(invoke) withObject:NULL waitUntilDone:YES];