iphone - performSelectorOnMainThread with return value - iphone

I have the following method:
- (NSMutableArray *)getElements:(NSString *)theURL;
And I wanted to know if there is a way to call that method using performSelectorOnMainThread so that I can get the return value. So far, I've tried with:
myArray = [object performSelectorOnMainThread:#selector(getElements:)
withObject:url waitUntilDone:YES];
but it doesn't work, since performSelectorOnMainThread returns void. How could I solve this?

Welcome to a multi-threaded environment my friend.
You'll need to store the return value in an instance variable or pass in an object by reference through the withObject parameter:
NSMutableDictionary *myDict;
[object performSelectorOnMainThread:#selector(getElements:)
withObject:&myDict waitUntilDone:YES];
Your method prototype should now look like this:
- (void)getElements:(NSMutableDictionary **)objects;

You can't do it directly, because, as you say, that method returns void.
So, you'd have to arrange another way to get a value back, for example by passing an NSDictionary instead of an NSString, and having the method store the result in the dictionary for you.

Had to implement this recently. Great candidate for adding a category to NSObject so that all your objects can do this:
#implementation NSObject (CallSelectorWithObjectOnMainThread)
- (id)resultFromSelectorOnMainThread:(SEL)selector object:(id)object {
NSMutableDictionary *resultDictionary = [NSMutableDictionary dictionaryWithCapacity:1];
NSMutableDictionary *callDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:resultDictionary, #"ResultDictionary", NSStringFromSelector(selector), #"Selector", nil];
if(object) [callDict setValue:object forKey:#"Object"];
[self performSelectorOnMainThread:#selector(callObject:) withObject:callDict waitUntilDone:YES];
return [resultDictionary objectForKey:#"Result"];
}
- (void)callObject:(NSMutableDictionary *)info {
id result;
SEL selector = NSSelectorFromString([info objectForKey:#"Selector"]);
id object = [info objectForKey:#"Object"];
NSMutableDictionary *resultDictionary = [info objectForKey:#"Dictionary"];
if(object)
result = [self performSelector:selector withObject:object];
else
result = [self performSelector:selector];
if(result)
[resultDictionary setValue:result forKey:#"Result"];
}
#end

I have universal solution for async blocking call with return value for any thread, not only main. This example for main thread like performSelectorOnMainThread:withObject:waitUntilDone:
__block id result = nil;
NSOperationQueue* targetQueue = [NSOperationQueue* mainQueue];
// targetQueue is main thread, but it may be queue on any thread.
[targetQueue addBlockOperation:^{
// performs on target thread.
result = [someObject someSelector];
}];
// wait until operations on targetQueue will finished.
[targetQueue waitUntilAllOperationsAreFinished];
// result is ready here.
I used this technique only in ARC environment.

Related

Allocating and initializing object in one function, sending it to other function and release it there

I Have this function that allocating and initializing object :
+(Item*)getItem:(NSString*)uid{
Item *file = [[Item alloc]init];
//do some stuff
return file;
}
and this is the call for this function:
Item *tmp = [LibraryScan getItem:itemid];
//do some stuff
[tmp release];
Now i want to release it after i get it,like i write above.
Have I done it right?
In my opinion, you should autorelease the object before returning it, letting the caller decide if he wants ownership on it :
+(Item*)getItem:(NSString*)uid{
Item *file = [[Item alloc]init];
//do some stuff
return [file autorelease];
}
So :
Item *tmp = [LibraryScan getItem:itemid];
//do some stuff
//[tmp release]; nothing to release
Yes your code is correct. But it also should meet the objective-c naming conventions that state that methods returning non-autoreleased objects (that is - the object that caller is responsible to release) should contain copy, alloc or new in method name.
So I would change your method name to newItem if you really want to return non-autoreleased object from it.
Its not wrong but its not a standard way.
Modify your implementation by this:
+(Item*)getItem:(NSString*)uid {
Item *file = [[[Item alloc]init]autorelease];
//do some stuff
return file;
}
and :
Item *tmp = [[LibraryScan getItem:itemid]retain];
//do some stuff
[tmp release];
It will help you to debug the memory leaks properly.

newbie question on iOs memory management

How to write right in this situation:
I have some method, that return NSMutableArray*. Because method not started with init, new or alloc, how write in apple memory-management guide, i return autorealese object.
-(NSMutableArray*)someMethod {
NSMutableArray *array = [NSMutableArray alloc] init] autorealese];
//Some code here
return array;
}
And i have some another methods, that call this one:
-(NSMutableArray*)method1 {
NSMutableArray *array = nil;
if(condition){
array = [self someMethod];
}
return array;
}
-(NSMutableArray*)method2 {
NSMutableArray *array = nil;
array = [self method1];
}
Code work.But XCode analyze tool says that in method2 i get object with count 0. So, how to write this code good?
There is nothing wrong with your code, except that the method2 will return the array that is autoreleased. Thus whatever is calling this method should retain the return value.
Creating an autoreleased NSMutableArray and returning it.
-(NSMutableArray*)someMethod {
NSMutableArray *array = [NSMutableArray alloc] init] autorealese];
//Some code here
return array;
}
Method1 uses autorelease NSMutableArray from someMethod and for the life of Method1 the array will not be autoreleased. That's one of the rules of memory management in objective-c that object lives through the method cycle.
-(NSMutableArray*)method1 {
NSMutableArray *array = nil;
if(condition){
array = [self someMethod];
}
return array;
}
Method2 uses still waiting to be autoreleased NSMutableArray from method1. It's important to notice that b/c you have a condition in method2 the array might be nil.
-(NSMutableArray*)method2 {
NSMutableArray *array = nil;
array = [self method1];
}
So in another words, you are passing autoreleased object along your methods. There is nothing wrong with this. You just have to remember that if you want to store the value of method2 you need to retain it, or it will get autoreleased.
Because of your condition in method1 the analyzer will complain b/c it's not guaranteed that the method1 will return an object, there is a possibility it will return nil.

What is the Ideal way to return data from function without leaking memory?

In IPhone Development ObjC , I wonder what is the right approach for functions to return some values in Dictionaries or Arrays. I mean should they return autorelease objects, but wouldn't it be bad if I am using too many functions to return values, like getUserRecordsFromDB() function will return all the user records, so should they return me autorelease objects? also when I am calling them multiple times, suppose with a span of 4 seconds to get the newely updated data. Can somebody let me an ideal scanario of how to get data from a function called frequently in the flow of program, and not leaking the memory.
We should always return autorelease objects from functions and ensure they get released by setting up Autorelease pool in the calling function as follows
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Call database function here...
[pool release];
Refer the auto release pool documentation here
If the method is in same class use global variable, if it is in some other class make sure it is allocated only one time, reuse the same memory every time it call that method.
Also check NSAutoreleasePool
Yes you should use autoreleased objects as return values.
You should stick to the cocoa programming guidelines.
This is a place to start, but there is a lot more to find on the subject.
You can always pass a mutable object to be filled in.
-(void) getMoreUsers:(NSMutableArray *)ioArray {
for ( ... ) {
NSMutableDictionary *user = [NSMutableDictionary new];
// fill in user ...
[ioArray addObject:user];
[user release]; // explicit release so only reference is owned by ioArray
}
}
This gives you explicit control when you know the lifetime of an object, but makes it easy to write a wrapper when you want to use autorelease.
-(NSArray *) getUsers {
NSMutableArray *result = [NSMutableArray array]; // implicit autorelease
[self getMoreUsers:result];
return result;
}
An example of using this with a known lifetime would be:
-(void) operateOnUsers {
NSMutableArray *array = [NSMutableArray new];
[self getMoreUsers:array];
// do stuff that will not retain array
[array release];
}

Memory Management: Releasing Custom Objects in constructor - Objective-C

I have an init method that takes values from a NSDictionary. This is within my Friend object.
My question is when do I release this particular instantiation of Friend?
- (id)initWithValue:(NSString *)value {
Friend *friend = [[Friend alloc] init];
friend.friendId = [value valueForKeyPath:#"id"];
friend.friendName = [value valueForKeyPath:#"name"];
return friend;
}
I call the init method below to add the Friend objects to a friends array
for (id value in dataDict) {
Friend *friend = [[Friend alloc] initWithValue:value];
[friends addObject:friend];
[friend release];
}
I then do the following in code:
Friend *friend = (Friend *)[friends objectAtIndex:indexPath.row];
If I autorelease the Friend object in the init method then I get a message was sent to deallocated instance when I use the above code to get the particular objects value based on the indexPath.row.
A couple of things:
an init method should not be calling alloc (see diwup's example for the right way to do an init)
it looks like what you're trying to do is make a convenience method that creates, initializes, and returns an (autoreleased) object (similar to +NSString stringWithFormat: etc.). You've almost got it, but you need to (a) not name it "init*", and (b) make it a class method, not an instance method.
Something like:
+ (Friend)friendWithValue:(NSString *)value {
Friend *friend = [[[Friend alloc] init] autorelease];
friend.friendId = [value valueForKeyPath:#"id"];
friend.friendName = [value valueForKeyPath:#"name"];
return friend;
}
You would use that like:
[friends addObject:[Friend friendWithValue:value]];
Not directly answering your question, but these lines of code are definitely dangerous:
- (id)initWithValue:(NSString *)value {
Friend *friend = [[Friend alloc] init];
Instead, you should write like this:
- (id)initWithValue:(NSString *)value {
[super init];
self.friendId = ...;
self.friendName = ...;
return self;
}
And directly answering it: Release your allocated objects ONLY when you are done with them. Create a method and call [object release]; for all your allocated objects. Doing so, you avoid having memory allocation inconsistencies, and this approach is better then calling autorelease.

iPhone: Can't set object received from thread in NSMutableDictionary

I've got a thread (specifically an NSOperation) that runs to load some images for me for a scroll view when my main view asks for them. Any number of these NSOperations can be queued at once. So it goes with the filepath I give it and loads the image from the disk (as UIImages) and then sends the object back to my mainview by using performSelectorOnMainThread: and passing my mainview an NSDictionary of the object, and an image ID value. My main view is then supposed to insert the image object and the image ID string into an NSMutableDictionary that it has for the mainview to be able to use. I've verified that the NSMutableDictionary is allocated and initialized fine, but when the method the NSOperation calls tries to add the objects to the dictionary nothing happens. I've verified that the object and string i get from the dictionary the thread sent me are not null or anything but yet it doesn't work. Am I not doing something right or using a bad technique? What would anyone suggest to do in a situation like this where I need to add UIImages to an NSMutableDictionary from a thread? Thanks so much!
Here's the NSOperation code I use:
- (void)main {
NSString *filePath = [applicaitonAPI getFilePathForCachedImageWithID:imageID andSize:imageSize];
UIImage *returnImage = [[UIImage alloc] initWithContentsOfFile:filePath];
if (returnImage) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:2];
[dict setObject:returnImage forKey:#"IMAGE"];
[dict setValue:[NSString stringWithFormat:#"%d", imageID] forKey:#"IMAGE_ID"];
NSDictionary *returnDict = [[NSDictionary alloc] initWithDictionary:dict];
[dict release];
[mainViewController performSelectorOnMainThread:#selector(imageLoaderLoadedImage:) withObject:returnDict waitUntilDone:NO];
[returnDict release];
}
}
And here's the method on the main thread:
- (void)imageLoaderLoadedImage:(NSDictionary *)dict {
UIImage *loadedImage = [dict objectForKey:#"IMAGE"];
NSString *loadedImage = [dict valueForKey:#"IMAGE_ID"];
[imagesInMemoryDictionary setObject:loadedImage forKey:loadedImageID];
[self drawItemsToScrollView];
}
[mainViewController performSelectorOnMainThread:#selector(imageLoaderLoadedImage:) withObject:nil waitUntilDone:NO];
You're not passing returnDict as the parameter to the method. You're passing nil.
A couple of other thoughts:
you don't need to create returnDict. You can just use dict as the method parameter.
you're leaking returnImage.
edit
Since you apparently are passing returnDict as the parameter to the method, my other guess would be that mainViewController is nil. Other than that, your code looks functional.