performSelectorOnMainThread throws message deallocated - iphone

While parsing of the twitter data, I used threads to call the main URL to download the data. It does the downloading perfectly but when I hit the back button while the data is downloading it throws performSelectorOnMainThread message deallocated. I know we can use isCancelled but its not working for me yet. Does anyone have come across this issue and have resolved it.
- (void)LoadTwitterData
{
NSString *urlString =#"http://search.twitter.com/search.json?q=tabc&result_type=recent&rpp=2500";
NSURL *url = [NSURL URLWithString:urlString];
NSString *jsonString = [NSString stringWithContentsOfURL:url];
NSDictionary *values = [jsonString JSONValue];
/**** Throws here *****/
[self performSelectorOnMainThread:#selector(didFinishLoadingResults:) withObject:values waitUntilDone:NO];
}

If you spin off a thread using a selector on self, you need to make sure that self is retained for the duration of that thread, otherwise (as in your case) self can be deallocated and your thread will try to call back into a zombie. The easiest way to do this is to pass self to the thread as an argument. If you use performSelectorInBackground:withObject: you should do something like this:
[self performSelectorInBackground:#selector(LoadTwitterData) withObject:self];
Or if you use NSThread you should pass self to the object: initializer argument.
In fact the safest way to use thread methods is to make the method static like this:
+ (void)LoadTwitterData:(id)arg
{
// ...
MyController *self = arg;
// ... do work
[self performSelectorOnMainThread:#selector(didFinishLoadingResults:)
withObject:values waitUntilDone:NO];
}
This way you are unable to access instance variables by accident which avoids various multi-threading issues. Any and all data the thread needs, including the self to callback to, should be passed in as 'arg', which can be an array or dictionary or whatever you need. This way you know that everything the thread needs will be retained for the duration of the thread, and because you aren't accessing instance variables through self, another thread can't go and change them around underneath you.
Another thing you should do in a thread method is wrap the whole thing with an autorelease pool:
- (void)LoadTwitterData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#try {
// ...
} #finally {
[pool drain];
}
}

If LoadTwitterData: is in a background thread, you need to create an Auto release pool (If you haven't already). Surround your code with-
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
//your code
[pool drain];

Related

IPhone: File Upload using dispatch_async for background upload

I want to perform a image upload while the running application at the background. I am able to upload the image to the server using the code on this link.
How can I upload a photo to a server with the iPhone?
I heard the NSUrlConnection can be asynchronous and it was used in the EPUploader. In my code, I add some extra method that will create a file in the application directory used for the EPUploader. During this creation of the file, I don't want it to create on the main thread of the application so I wrap all the code including the EPUploader itself with the
dispatch_async on the global queue. That way I won't block the main thread while the file are creating.
It has no problem if I use dispatch_sync but dispatch_async I find something weird when I placed the breakpoint at NSUrlConnection connection :
- (void)upload
{
NSData *data = [NSData dataWithContentsOfFile:filePath];
//ASSERT(data);
if (!data) {
[self uploadSucceeded:NO];
return;
}
if ([data length] == 0) {
// There's no data, treat this the same as no file.
[self uploadSucceeded:YES];
return;
} /* blah blah */
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (!connection) {
[self uploadSucceeded:NO];
return;
}
else
return;
I went to debug at the breakpoint and instead of going to if statement, the debugger jumps to the first return statement of this method. After that, the selectors I passed on to this class never gets called. This happen only on dispatch_async and it work on dispatch_sync on the global queue.
Does anybody know how to solve this issue?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
self.uploadIndex = 0;
ALAsset *asset = [self.assets objectAtIndex:0];
[[FileUploader alloc] initWithAsset:[NSURL URLWithString:#"http://192.168.0.3:4159/default.aspx"]
asset:asset
delegate:self
doneSelector:#selector(onUploadDone:)
errorSelector:#selector(onUploadError:)];
//[self singleUpload:self.uploadIndex];
[pool release];
});
There are a couple of things that should be changed.
Remove the NSAutoreleasePool, it is not needed.
Copy the block to the heap because it's life will probably exceed that of the calling code.
Example:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, [[^{
self.uploadIndex = 0;
ALAsset *asset = [self.assets objectAtIndex:0];
[[FileUploader alloc] initWithAsset:[NSURL URLWithString:#"http://192.168.0.3:4159/default.aspx"]
asset:asset
delegate:self
doneSelector:#selector(onUploadDone:)
errorSelector:#selector(onUploadError:)];
} copy] autorelease]);
If you are using ARC (which you certainly are since you should be) there is no need for the copy or autorelease.

EXC_BAD_ACCESS error when switching back and forth between tableviews

A brief over view of what I am trying to do.
I am using the tableView:didSelectRowAtIndexPath: method inside my UITableViewController subclass which is catching a row selection from that view like so...
//..... inside tableView:didSelectRowAtIndexPath:
if (indexPath.section == 0) {
//--- Get the subview ready for use
VehicleSearchResponseTableViewController *vehicleSearchResponseTableViewController = [[VehicleSearchResponseTableViewController alloc] initWithNibName:#"VehicleSearchResponseTableViewController" bundle:nil];
// ...
//--- Sets the back button for the new view that loads
self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style: UIBarButtonItemStyleBordered target:nil action:nil] autorelease];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:vehicleSearchResponseTableViewController animated:YES];
if(indexPath.row == 0) {
vehicleSearchResponseTableViewController.title = #"Mans";
EngineRequests *engineRequest = [[EngineRequests alloc] init];
[engineRequest getMans];
[engineRequest release];
}
if(indexPath.row == 1) {
//.... etc etc
As you can see in this method I set up a few things, pushing the new view onto the viewstack and changing the back buttons text, then I go into catching the different rows and then initiating a method in a subclass of nsobject where I want to have all my connection/request stuff going on.
Inside my NSObject I have several different methods for the different cells that you can select on the UITableViewController, basicly they specify different strings that will then initialize my ASIHTTPRequest wrapper to make a connection to the php script and catch all the data that will come back from the database.. NSObject looks like this.
//.... NSObject.m
- (IBAction) getMans
{
NSString *mansString = [[NSString alloc] initWithString:#"mans.php"];
[self grabURLInBackground:mansString];
[manusString release];
}
//....cont....
//--- Connect to server and send request ---------------->>
- (IBAction)grabURLInBackground:(NSString *)setUrlString
{
NSString *startURL = [NSString stringWithFormat:#"http://127.0.0.1:8888/CodeTest/%#", setUrlString];
NSURL *url = [NSURL URLWithString:startURL];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString]; //Pass request text from server over to NSString
NSData *responseData = [responseString dataUsingEncoding:NSUTF8StringEncoding]; //Create NSData object for Parser Delegate and load with responseString
NSLog(#"stuff %#",responseData);
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
NSLog(#"%#", error);
}
From here I would like to pass the data I am getting from the requestFinished method back over to the newly pushed UITableView.. However I have an error before I am able to get this far that I need to solve... if I run the simulator and click back and forth between the views (the main UITableViewController with the cells and then the newly popped view where I want to put the data) the application falls over and pops up an error in main.m Thread 1: program receive signal EXC_BAD_ACCESS.. I just don;t know whats causing because from what I can tell my code is not so bad.
Also when I debug my application I notice that once grabURLInBackground method has finished it bounces out back to the getMans method then goes back over to the UITableViewController and continues through the if statements, completely neglecting the requestFinished and requestFailed methods, and I just cannot figure out why.
I guess I am not sure if I am calling the methods and functions I need to use in the right places so if you have any suggestions or answers on how I can improve or if you know where my error is coming form that would be greatly appreciated.
There's a few issues with the code above but I'd guess that your bad access exception is due to the handling of your EngineRequests and use of AsiHttpRequest.
The code here
EngineRequests *engineRequest = [[EngineRequests alloc] init];
[engineRequest getMans];
[engineRequest release];
effectively creates an object then deallocates as soon as getMans has finished running.
Then inside the engineRequest object this code
[request setDelegate:self];
[request startAsynchronous];
requests that AsiHttpRequest notify the almost certainly released object once the request has completed.
There may be other issues at work here but I'd start by restructuring to try to keep this object around until at least after it's received the response from AsiHttpRequest.
Hard to tell from the brief overview, but generally when you bad_access and end up in the main application method, it's usually because you autoreleased something, then released it, and it craps out when the autorelease pool is drained. Might want to turn on NSZombiesEnabled and look for memory problems.
Who does receive your request?
The sender (and receiver) object is engineRequest.
But you release Engine Request in that very moment after you issued the async request (by mens of the getMans Method.
I would suggest that you
1. move the code
vehicleSearchResponseTableViewController.title = #"Mans";
EngineRequests *engineRequest = [[EngineRequests alloc] init];
[engineRequest getMans];
[engineRequest release];
from your UITableViewController's didSelectRowAtIndexPath method to your vehicleSearchResponseTableViewController's viewDidLoad method.
2. to retain your EngineRequests object and keep it in some instance variable within vehicleSearchResponseTableViewControllerand do not release it before the request is completely processed, either successfully or in error.

How to release object when using block callback

this is probably a newbie question regarding memory manegment.
How can i release an object when using blocks as callback in objective c?
(Updated code)
#implementation ObjectWithCallback
- (void)dealloc {
[_completionHandler release];
[super dealloc];
}
- (void)doTaskWithCompletionHandler:(void(^)(void))handler {
_completionHandler = [handler copy];
// Start tasks...
}
- (void)tasksDone {
// Do callback block
_completionHandler();
// Delete reference to block
[_completionHandler release];
_completionHandler = nil;
}
// Use of the ObjectWithCallback
ObjectWithCallback *request = [[ObjectWithCallback alloc] init];
[request doTaskWithCompletionHandler:^(void){
// Callback called and task is ready.
}];
Quick, incomplete answer: [request autorelease]
The problem with this is that blocks implicitly retain any objects that are referenced inside the body of the block. So the block retains request, and request retains the block, leading to a retain cycle and nobody getting deallocated.
To remedy that, you declare your request variable as __block, which prevents the block from retaining the captured object:
__block ObjectWithCallback *request = [[ObjectWithCallback alloc] init];
Recommended reading:
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html
http://www.mikeash.com/pyblog/friday-qa-2010-04-30-dealing-with-retain-cycles.html
http://castirony.com/post/3936227677/block-retain-cycle

iPhone - Threading and Delegation

I'm running some code in a background thread to get a text file from a service. That code fires a delegate at some point. It throws as SIGABRT error once the delegate is being called and well, my concept doesn't sound convincing to me either.
The code running at the background thread:
- (void)FetchStores
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Fetch from service
NSString *serviceURL = #"http://../index.html";
NSURL *myURL = [NSURL URLWithString:serviceURL];
NSData *dataRep = [NSData dataWithContentsOfURL:myURL];
storesList = [[Stores alloc] init];
storesList.storesDelegate = self;
[storesList FetchWithNSData:dataRep];
[pool release];
}
The storesList object will fire a delegate once all the stores have been extracted from the service. The delegate is getting caught by a function at the main thread.
Do you have any suggestions what am I doing wrong ?
Thank you,
f.
When calling the delegate, somewhere, you should make the switch to the main thread.
Especially if somewhere, you are updating the UI based on the data fetched.
You can use
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
to make the switch.
Maybe like this:
storesList = [[Stores alloc] init];
storesList.storesDelegate = self;
[storesList performSelectorOnMainThread:#selector(FetchWithNSData:) withObject:dataRep waitUntilDone:TRUE];
In your case, you should use waitUntilDone:TRUE so that the FetchWithNSData method gets a chance to retain the data.
It seems quite likely that FetchWithNSData: does not retain the passed dataRep and the data gets deallocated on the next line where you drain the local autorelease pool?

iPhone Autoreleasepool and allocations

I've been reading about autoreleasepool but there is a point which is a bit unclear to me. I have some functionality using threads that required seperate memory managment using autoreleasepool.
In the following example is correct
-(void) doSomething {
NSAutorelease *pool = [[NSAutorelasepool alloc] init];
NSString *myString = #"Hello";
[pool release];
}
Is this correct?
-(void) doSomething {
NSAutorelease *pool = [[NSAutorelasepool alloc] init];
NSString *myString = [[NSString alloc] initWithString:#"Hello"];
[pool release];
}
or this?
-(void) doSomething {
NSAutorelease *pool = [[NSAutorelasepool alloc] init];
NSString *myString = [[NSString alloc] initWithString:#"Hello"];
[myString release];
[pool release];
}
My question is owned objects created in the scope of the autorelease pool need to be relased specifically or are the taken care of when the autorelasepool is been released?
Teo
Autorelease pool handles the autoreleased objects. If you own an object (via alloc or copy or retain) then you must release it. So your 2nd example is not correct. As you have allocated the string, you own it and you must release it.
An autorelease pool is created for the main thread. (You can look into the main function if you want). Every thread need its own autorelease pool to manage autoreleased objects. That's why if you create another thread then you must create an autorelease pool for that thread. Even if you don't create autoreleased object in the thread, you should create this as the library calls in that thread may create autoreleased objects. Even if you are sure that no library calls are making autoreleased objects then you also should create them as that is the best practice, specially if you are working on big project which is developed and maintained by multiple people.
You only need to create your own autorelease pool when you are creating a bunch of
autoreleased objects you want to garbage collect immediately. However, you are correct in that you don't want to reference any "autoreleased" objects you create after you release the pool. Autoreleased objects (which you don't retain) are destroyed when the pool is drained.
Since none of the objects in your example are autoreleased, creating your own autorelease pool is essentially a no-op.
Neither of your examples needs an autorelease pool. Autorelease pools only take care of autoreleased objects:
NSArray *foo = [NSArray array];
NSObject *bar = [[[NSObject alloc] init] autorelease];
Your first string is initialized using a string literal and therefore is probably special with respect to memory management (maybe someone else knows more). Your second string leaks, the pool does not make a difference. Your third string is released correctly, again the pool does not make a difference.
This is where you would need a pool:
- (void) someMethodThatRunsOnAThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *foo = [#"foo" uppercaseString];
[pool drain];
}
Here the foo string would leak if the pool wasn’t there. Note that I’m calling drain instead of release on the pool – on iOS there’s not a difference, but in garbage-collected environments the two differ, so it’s probably better to get in the habit of calling the right one.
Also note that you may need a pool even though you don’t autorelease any objects yourself, there could be many memory operations done somewhere in the code you’re calling in your method.
Think that this should be something like this:
-(void) doSomething {
NSAutorelease *pool = [[NSAutorelasepool alloc] init];
NSString *myString = [[[NSString alloc] initWithString:#"Hello"] autorelease];
// or create string like this (automatically autoreleased)
NSString *myString = [NSString stringWithString:#"Hello"];
[pool release];
}
You must send autorelease message, to objects inside autorelease pool. They will be released when release message is sent to pool.