GCD-created thread throws abort after completing block message - iphone

I developed a simple RSS fetcher application that parses an XML RSS feed from a programmer-provided website and displays the articles in the Master View Controller and the UIWebView in a Detail View Controller. I custom set-up a personal server to render the RSS article selected in the master table view (for certain reasons) as a PDF. However, this obviously takes some time on the server end, unless the selected table view cell has already been rendered as a PDF on the server end. My server and blog don't talk to one another (and can't, for other reasons), so I can't pre-render the PDFs as I create the blog posts. The PDF render must be done from the application itself.
I decided to use Grand Central Dispatch to create a separate thread to talk to the server and render the PDFs before the user can select an arbitrary cell to see the post. Here is the code I used to create the queue.
dispatch_queue_t networkQueue = dispatch_queue_create("com.company.networkQueue", NULL);
...and the code I used to create the new thread...
dispatch_async(networkQueue, ^{ [self cachePDFRequests]; });
...here is my cachePDFRequests method that is called in the block request...
- (void) cachePDFRequests {
NSURL *myURL;
NSString *cacheUrl;
NSURLRequest *request;
for (int i = 0; i <= feeds.count; i++) {
cacheUrl = [feeds[i] objectForKey:#"link"];
cacheUrl = [cacheUrl stringByReplacingOccurrencesOfString:#" " withString:#""];
cacheUrl = [cacheUrl stringByReplacingOccurrencesOfString:#"\n" withString:#""];
NSString *fullUrl = [NSString stringWithFormat:#"http://myserver.com/render.php?url=%#", cacheUrl];
myURL = [NSURL URLWithString:fullUrl];
request = [NSURLRequest requestWithURL:myURL];
[cacheView loadRequest:request];
}
}
Note: cacheView is a UIWebView that is not on any UI...it is just an ivar of my Master VC class.
So, when I run the dispatch_async() function in -[viewDidLoad], it runs the -[cachePDFRequests] method and the for() loop inside of it, then throws SIGABRT on my newly-created thread. Please ask any questions as necessary. Let me know if I need to include any code that I haven't already provided.
Here is a picture of the SIGABRT error that appears whenever I run the GCD thread:
Thanks in advance!
P.S. I used to run cacheView = [[UIWebView alloc] init]; in an if() loop if the cacheView was set to nil...that used to throw the SIGABRT error. Now, it has no references to code in my Master View Controller after I removed that line.
EDIT: Here is the code for what the feeds mutable array is containing:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"item"]) {
[item setObject:title forKey:#"title"];
[item setObject:link forKey:#"link"];
[feeds addObject:[item copy]];
}

It looks like you are accessing feeds out of bounds. NSArray will throw an exception if you try to access it with an index that is outside of its bounds, and that is exactly what the backtrace indicates. This is the correct way to loop over an array with indices starting with 0:
for (int i = 0; i < feeds.count; i++) // Notice that it uses < instead of <= as comparator
As a side note, there is no such thing as an if() loop.

Related

performSelector enters method on all instances, but only one thread finishes

This may be a naive question, but I'll ask it anyway as I cannot find any documentation that clears up this issue in my head.
I'm running iOS5.1 both on device and in the simulator with Xcode45-DP4.
I have a loop that iterates over an array of a number of instances of a class. In that loop I use performSelector on the instances to start a thread that does some relatively slow network operations — pulling down data that I'd rather do in the background.
[arrayOfFriends enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Friend *f = (Friend*)obj;
iOSSLog(#"%d", idx);
[f performSelectorInBackground:#selector(showDescription) withObject:nil];
-(void)fetchTwitterStatus
{
iOSSLog(#"Trying to fetch twitterstatus %# %#", self.hash, self.twitterUserName);
[mLocalTwitterUser fetchTwitterAPIUserStatusWithScreenName:twitterUserName
withCompletionHandler:^(NSArray *arrayOfStatus, NSError *error) {
if(error) {
iOSSLog(#"%#", error);
} else {
iOSSLog(#"Got twitterstatus %# %d", self.twitterUserName, [arrayOfStatus count]);
#synchronized(items) {
[items addObjectsFromArray:arrayOfStatus];
}
}
}];
}
In my test case there are four instances. Each instance gets its selector, you know..selected. The first three definitely start but only the last actually completes as indicated by the log line that says "Got twitterstatus..." Which is weird.
I can also verify that the method the selector calls "fetchTwitterStatus"
What is the little fundamental nugget of multithreading that I'm missing here?
EDIT: here's fetchTwitterAPIUserStatusWithScreenName...quite a bit here, but effectively it's calling the Twitter API Endpoint user_timeline with a JSON response.
- (void)fetchTwitterUserStatusWithScreenName:(NSString *)screenname
excludeReplies:(BOOL)excludeReplies
withCompletionHandler:(OtterTwitterSearchHandler)completionHandler
{
self.twitterAPIStatusHandler = completionHandler;
//self.fetchTwitterUserStatusHandler = completionHandler;
NSString *urlString = [NSString stringWithFormat:#"https://api.twitter.com/1/statuses/user_timeline.json?screen_name=%#&include_rts=true&include_entities=true&exclude_replies=%#&count=50", screenname, excludeReplies?#"true":#"false"];
NSURL *url = [NSURL URLWithString:urlString];
#warning this isn't the way to do it - just checking the cache for refresh of the scroller
[[ASIDownloadCache sharedCache]removeCachedDataForURL:url];
iOSSRequest *request = [[iOSSRequest alloc] initWithURL:url
parameters:nil
requestMethod:iOSSRequestMethodGET];
NSMutableDictionary *oauthParams = [NSMutableDictionary dictionary];
[oauthParams setObject:[[Twitter sharedService] apiKey] forKey:kASIOAuthConsumerKey];
[oauthParams setObject:[[Twitter sharedService] apiSecret] forKey:kASIOAuthConsumerSecret];
[oauthParams setObject:[self oAuthAccessToken] forKey:kASIOAuthTokenKey];
[oauthParams setObject:kASIOAuthSignatureMethodHMAC_SHA1 forKey:kASIOAuthSignatureMethodKey];
[oauthParams setObject:#"1.0" forKey:kASIOAuthVersionKey];
[oauthParams setObject:[self oAuthAccessTokenSecret] forKey:kASIOAuthTokenSecretKey];
request.oauth_params = oauthParams;
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (error) {
if (self.twitterAPIStatusHandler) {
self.twitterAPIStatusHandler(nil, error);
self.twitterAPIStatusHandler = nil;
}
} else {
NSMutableArray *recentStatusForTwitterUser = [[NSMutableArray alloc]init];
NSArray *array = [Twitter JSONFromData:responseData];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
TwitterStatus *twitterStatus = nil;
twitterStatus = [[TwitterStatus alloc]initWithDictionary:obj];
[recentStatusForTwitterUser addObject:twitterStatus];
}];
if (self.twitterAPIStatusHandler) {
self.twitterAPIStatusHandler(recentStatusForTwitterUser, nil);
self.twitterAPIStatusHandler = nil;
}
}
}];
}
I'd suggest using the asynchronous abstractions already provided where possible. It would be a fairly rare/unique situation where you need to deal with threads directly.
I've found treating each network-based background task as a synchronous NSOperation on a queue works really well.
Get a new instance of NSOperationQueue, configure it, add tasks to it, and manage the queue. The benefit of this approach is that each task can be implemented as a simple synchronous task, and the queue takes care of concurrency. Optionally you can set dependencies (this task must complete before that one).
What is the little fundamental nugget of multithreading that I'm
missing here?
That taking non-multithreaded code and spinning off a random number of threads by performing an arbitrary method in the background is doomed to failure.
Concurrency is a design pattern that must be carefully considered from the start (or is a monumental refactoring effort).
First, you don't want to spawn a thread per network connection. Secondly, given that these are just HTTP requests, you would want to use the systems built in classes for asynchronous HTTP communications. Finally, your concurrency model must exactly specify how you are keeping all data in isolation until you hit whatever mechanism you are using to synchronize the data back into the central store.
Hard to say where that code is going off the rails without seeing more information.

How do you remove a UIButton/keyboard/UIAlertView from the view while json or other data is being loaded?

I'm using a UISearchBar in my application and the problem is when I call a few json methods searchBarSearchButtonClicked seems to not resign the keyboard until the other methods are done loading the data. I've tried alternatively using UIAlertView and UIButtons to replace the searchBarSearchButtonClicked function but they appear to literally freeze and stay in a "pressed down" state too. I was also wondering if this would be a reason why [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; wouldn't show an activity indicator in the device's status bar.
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
self.args = searchBar.text;
[self grabData];
[self fillVars];
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
[self grabData] is where I grab the JSON data and [self fillVars] just fills a few things that are later used.
-(void)grabData{
self.args = [self.args stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
urlString = [NSString stringWithFormat:#"%#%#?key=%#&q=%#",baseUrl,func,apiKey,args];
url = [NSURL URLWithString:urlString];
NSData *jsonData = [NSData dataWithContentsOfURL:url];
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
self.matches = [json objectForKey:#"matches"];
[UIApplication sharedApplication].networkActivityIndicatorVisible=YES;
}
You will have to use threading. All manipulation with your interface happens on the main thread, so when you perform a lengthy task on the main thread, the interface won't be able to update itself before the task has completed.
In a UIViewController you can do [self performSelectorInBackground:#selector(grabData) withObject:self], which is a convenience method for dispatching a new queue (thread) using grand central dispact.
You could also do that manually, using the GCD API. You would do something along the lines of this:
dispatch_queue_t jsonQueue = dispatch_queue_create("JSON Queue", NULL);
dispatch_async(jsonQueue, ^{
// fetch JSON data ...
dispatch_async(dispatch_get_main_queue(), ^{
// perhaps do something back on the main queue once you're done!
});
});

nsurlconnection asynchronous request

First of all the questions are failry simiple.. if you just want to see what they are skip to the bottom of this post and you will see them in bold.. for more detail then you can read the rest of this post...
I am just trying to iron out my NSURLConnection so that its working smoothly and I understand this properly. There is a profound lack of example/tutorials for Asynchronous connections on the internet or not any that I can find that explaine what is going on with any level of depth other than getting the connection up and running which after working on it seems pretty simple. Hopefully this question can full the void that I feel is out there for other users.
So, in my .h file i have imported the foundations headers and declared the methods required for the received or lack of received data (errors etc).
.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h> //add foundations
//.. other headers can be imported here
#interface MyViewController: UITableViewController {
//Im not setting any delegates to access the methods because Is all happening in the same
//place so I just use the key word 'self' when accessing the methods declared below
//I'm not sure if this is the best thing to do but I wasn't able to get my head around declaring the delegate or how it would help me with the way I have set up my request etc.
}
- (IBAction)setRequestString:(NSString *)string; //this method sets the request and connection methods
//these methods receive the response from my async nsurlconnection
- (void)receivedData:(NSData *)data;
- (void)emptyReply;
- (void)timedOut;
- (void)downloadError:(NSError *)error;
So thats my header file.. pretty simple not much explaining needed.
.m
//call setRequestString from some other method attached to a button click or something
[self setRequestString:#"rss.xml"];
//..
- (IBAction)setRequestString:(NSString *)string
{
//Set database address
NSMutableString *databaseURL = [[NSMutableString alloc] initWithString:#"http:www.comicbookresources/feeds/"]; // address not real jsut example
//append the string coming in to the end of the databaseURL
[databaseURL appendString:string];
//prepare NSURL with newly created string
NSURL *url = [NSURL URLWithString:databaseURL];
//AsynchronousRequest to grab the data
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil){
[self receivedData:data];
}else if ([data length] == 0 && error == nil){
[self emptyReply];
}else if (error != nil && error.code == NSURLErrorTimedOut){ //used this NSURLErrorTimedOut from foundation error responses
[self timedOut];
}else if (error != nil){
[self downloadError:error];
}
}];
}
now set up the methods that were initialized in the .h file and called in the if statement above
- (void)receivedData:(NSData *)data
{
NSString* newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", newStr); //logs recived data
//now you can see what data is coming in on your log
//do what you want with your data here (i.e. start parsing methods
}
- (void)emptyReply
{
//not sure what to do here yet?
}
- (void)timedOut
{
//also not sure what to do here yet?
}
- (void)downloadError:(NSError *)error
{
NSLog(#"%#", error);
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error!" message:#"A connection failure occurred." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[errorAlert show];
}
Cool so that pretty much the basics of what I have done right there.. now the questions I have are as follows.
Question one:
Where I call NSURLConnection like so
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
What is happening here what is the ^ for is that executing that whole block (including the if statements) on a different thread or something? because it looks alot like grand central dispatch formatting but slightly different.
Question two:
what should I be doing inside emptyReply & timedOut methods?
Question three:
How would I incorporate caching into this? I would like to cache the responses I get back from different requests. i.e. with my setRequestString you will see there is a string input parameter, so i can request different rss feeds with the same method.. I need to figure out how to cache these responses into individual caches.. but im not sure where to start with it.
Finally
If you have made it this far, thank you very much for reading my question. Hopefully with your responses we can get a pretty nice solution going here.. that other people can use for themselves and pick and choose the bits and peices they need that works for there own solution..
Anyway thank you very much for reading and I look forward to your replies.. even if they are just refrences to tutorials or examples you think might help me.. anything is good I just want to fully understand whats going on and whats a good solution.
Read about blocks in Apple documentation. Its new. Or you can read here
You can show errors such as request timed out etc. You don't really have to handle them separately than the error one unless you have special logic.
Try this for caching
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:TIMEOUT_INTERVAL];

update ui while running in a loop

I have a read-only text field that I use as a log display. I have a operation that removes all the files in app's document directory. I want to insert a line of log when I remove each file. But the text field is only updated when the whole operation got finished. How do I fix this?
Here is my code:
NSFileManager *fm = [[[NSFileManager alloc] init] autorelease];
NSError *error = nil;
for (NSString *fileName in array) {
NSString *filePath = [DOCUMENT_PATH_VALUE stringByAppendingFormat:#"/%#", fileName];
[fm removeItemAtPath:filePath error:&error];
if (!error) {
NSString *log = [NSString stringWithFormat:#"removed success: %#", fileName];
[self logThis:log];
}else{
NSString *log = [NSString stringWithFormat:#"remove failed: %#, %#", fileName, [error localizedDescription] ];
[self logThis:log];
-(void)logThis:(NSString*) text{
NSRange range = NSMakeRange([updateLogTextView.text length], [text length]);
updateLogTextView.text = [updateLogTextView.text stringByAppendingFormat:#"%#\n", text];
[updateLogTextView scrollRangeToVisible:range];
}
You need to move your long-running operation into a background thread/queue and make sure that your UI-updating code is always executed on the main thread.
Example:
- (void)processFiles:(NSArray *)array
{
//You need to create your own autorelease pool in background threads:
NSAutoreleasePool *pool = [NSAutoreleasePool new];
//...
NSString *log = ...;
[self performSelectorOnMainThread:#selector(logThis:) withObject:log waitUntilDone:NO];
//...
[pool release];
}
You would then start your operation using:
[self performSelectorInBackground:#selector(processFiles:) withObject:files];
Using Grand Central Dispatch with blocks would be another option.
Refer to the Threading Programming Guide and the Concurrency Programming Guide for more in-depth information.
You need to look into Threading and making that call Asynchronous. You are currently blocking your main thread (assuming it's called from there). You need to separate the two so a separate thread does the heavy operation while your main thread is updated with the new text in the UI.
I've just started ios dev but sounds like you need to redraw that UIText I'm not sure how though I'd assume that when your log writes that you could dealloc your original UIText object the realloc it and it should redraw with the new log message. Only problem is that it will most likely update very very fast so redrawing wouldnt be worth it unless ios has a sleep function?
Something like:
Alloc and init text
Then in for loop after each write or new message in log dealloc your text and realloc and init your text
This should redraw the UIText object and update the text, if it doesnt expect a memory write error/app crash

iPhone: Parsing multiple XML with NSXMLParser in background disturbing each other

I have a strange issue, when it comes to parsing XML with NSXMLParser on the iPhone. When starting the app, I want to preload 4 table-views, that are populated by RSS-Feeds in the background.
When I init the table-views one-by-one, than loading, parsing and displaying all works like a charm. But when I try to init all view at once (at the same time), than it seems, that the XML-parser-instances are disturbing each other. Somehow data from one XML-Feed are "broadcasted" into other xml-parser instances, where they do not belong. Example: there is a "teammember" item, with "This is my name". When this bug occurs, there is a string from another xml-feed added, i.e. resulting in: "This is my name58", where 58 is the chart-position of something from the other view. "58" seems to miss then on the other instance.
It looks to me, that this bug occurs because of the NSXMLParser-delegate method:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (!currentStringValue) {
currentStringValue = [[NSMutableString alloc] initWithCapacity:50];
}
[currentStringValue appendString:string];
}
In this case "by coincidence" bytes are appended to strings, where they do not belong to.
The strange thing is, that every instance of NSXMLParser is unique, got its own unique delegates, that are attached to their own ViewController. Every parsing-requests spawns it own background-task, with its own (also also unique named) Autorelease-pool.
I am calling the NSXMLParser like this in the ViewController:
// prepare XML saving and parsing
currentStringValue = [[[NSMutableString alloc] initWithCapacity:50] retain];
charts = [[NSMutableArray alloc] init];
NSURL *url = [[NSURL alloc] initWithString:#"http://(SOME XML URL)"];
xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] retain];
//Set delegate
[xmlParser setDelegate:self];
//loading indicator
progressWheel = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(150.0,170.0,20.0,20.0)] autorelease];
progressWheel.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.view addSubview:progressWheel];
[progressWheel startAnimating];
// start loading and parsing the xml-feed in the background
//[self performSelectorInBackground:#selector(parse:) withObject:xmlParser]; -> I also tried this
[NSThread detachNewThreadSelector:#selector(parse:) toTarget:self withObject:xmlParser];
And this is one of the background-tasks, parsing the feed:
-(void)parse:(NSXMLParser*)myParser {
NSAutoreleasePool *schedulePool = [[NSAutoreleasePool alloc] init];
BOOL success = [myParser parse];
if(success) {
NSLog(#"No Errors. xmlParser got: %#", myParser);
(POST-PROCESSING DETAILS OF THE DATA RETURNED)
[self.tableView reloadData];
} else {
NSLog(#"Couldn't initalize XMLparser");
}
[progressWheel stopAnimating];
[schedulePool drain];
[myParser release];
}
What could cause this issue? Am I calling the background-task in the right way? Why is this bug approaching, since every XML-Parser got its own, unique instance?
You should not be updating UI elements (like progressWheel) from inside a background thread. UI updates should be done on the main thread.
Use -performSelectorOnMainThread:withObject:waitUntilDone: to update UI elements from within a background thread.
I've released an open source RSS/Atom Parser for iPhone and it makes reading and parsing web feeds extremely easy.
You can set it to download the data asynchronously, or you could run it in a background thread synchronously to collect the feed data.
Hope this helps!