I am using https://github.com/st3fan/iphone-bitly classes and as the example i want to do something for which i want your help.
- (void) demo
{
URLShortener* shortener = [[URLShortener new] autorelease];
if (shortener != nil) {
shortener.delegate = self;
shortener.login = #"LOGIN-REPLACE-ME";
shortener.key = #"KEY-REPLACE-ME";
shortener.url = [NSURL URLWithString: #"http://stefan.arentz.ca"];
[shortener execute];
///// I want to get result on here not in the delegate for further usage in my function
}
}
any help or suggestion
What you're requesting would block the current thread. If you ran that on the main thread, you'd hang your app. The framework you've linked only provides an asynchronous interface for that reason.
If you're already running this on a background thread, you can re-implement execute to use [NSURLConnection sendSynchronousRequest:returningResponse:error:]. But never run that on your main thread.
Related
what's is more prefered way to write multi threaded apps. I see two ways.
Implement method with GCD inside and then just simple call (myMethodA), or just implement method and then call it with GCD? Thanks in advance.
My point:
ClassA / method implementation
- (void)myMethodA
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// doSomething1
// doSomething2
});
}
- (void)myMethodB
{
// doSomething1
// doSomething2
}
ClassB / method call
{
[myClassA methodA];
// or
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[myClassA methodB];
};
}
IMHO, neither.
The preferred way should be having an object which knowns where to execute its actions:
completion_block_t completionHandler = ^(id result) { ... };
AsyncOperation* op = [AsyncOperation alloc] initWithCompletion:completionHandler];
[op start]; // executes its actions on a private execution context
Then, one can wrap those AsyncOperation objects into a convenient method:
- (void) fetchUsersWithCompletion:(completion_block_t)completionHandler
{
NSDictionary* params = ...;
self.currentOperation = [[HTTPOperation alloc] initWithParams:params
completion:completionHandler];
[self.currentOperation start];
}
The client may only be interested in specifying where its completionHandler should be executed. The API may be enhanced as follows:
- (void) fetchUsersWithQueue:(NSOperationQueue*)handlerQueue
withCompletion:(completion_block_t)completionHandler
{
NSDictionary* params = ...;
self.currentOperation = [[HTTPOperation alloc] initWithParams:params
completion:^(id result){
// As per the documentation of HTTPOperation, the handler will be executed
// on an _unspecified_ execution context.
// Ensure to execute the client's handler on the specified operation queue:
[handlerQueue:addOperationWithBlock:^{
completionHandler(result);
}];
}];
[self.currentOperation start];
}
The latter API can be used as this:
[self fetchUsersWithQueue:[NSOperation mainQueue] completion:^(id result){
self.users = result;
[self.tableView reloadData];
}];
Personal preference. Choose whichever makes the code more readable / understandable / obvious. Also, consideration of whether the code should be possible to run on the 'current' thread or whether it should always be run on a background thread. You need to design your threading configuration, describe it and then implement with that in mind. If you're calling methods between classes like in your example then I'd generally say that any threading should be handled inside that class, not inside the calling class. But that's about distribution of knowledge.
It doesn't make much of a difference - it just depends on what you want to do.
If you want to execute the method on different queues each time, then the myMethodB system is more appropriate. If, however, you always want to run the method on the same queue, then myMethodA will save you time writing code (you only have to write the GCD code once).
I try to use UINavigationController to navigate from one view controller to another.
In both viewController , the viewWillAppear
I call asihttp using async mode.
but this always cause the app crash.
I try to use the function of asihttp '[request cancel]'
in the wiewWillDisaaoear, but the error still exist
Welcome any comment
I used Following code in my app and i also did forth-back using UINavigationController but it didn't give me any crash. I suggest you to show your code here so it will help you a lot to find out your resolution.
Here, I am showing you my code..see if it will help you.
-(void)viewWillAppear:(BOOL)animated
{
[self willAnimateRotationToInterfaceOrientation:self.interfaceOrientation duration:1];
NSURL *urlf = [NSURL URLWithString:#"Your url"];
ASIHTTPRequest *requestdiff = [[ASIHTTPRequest alloc]initWithURL:urlf];
[requestdiff setDelegate:self];
[requestdiff release];
[self willAnimateRotationToInterfaceOrientation:self.interfaceOrientation duration:1];
}
I want to return information from a turn based game from the game center servers, which is all fine, but I want the player alias which is acquired using the asynchronous method:
[GKPlayer loadPlayersForIdentifiers:singleOpponentArray withCompletionHandler:^(NSArray *players, NSError *error) {
GKPlayer *returnedPlayer = [players objectAtIndex:0];
NSString *aliasToAdd = [NSString stringWithString:returnedPlayer.alias];
NSString *idToAdd = [NSString stringWithString:returnedPlayer.playerID];
NSDictionary *dictionaryToAddToAliasArray = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:aliasToAdd, idToAdd, nil] forKeys:[NSArray arrayWithObjects:#"alias", #"id", nil]];
[self.aliasArray addObject:dictionaryToAddToAliasArray];
}];
But the UI uses this information and it does't arrive in time. How can I make that method execute synchronously on the main thread?
Thanks.
Any UI related code must execute on the main thread.
If your app must wait for the asynchronous call to return, then first disable the UI. For example, set userInteractionEnabled = NO on your UIView.
Then, when the asynchronous methods returns, re-enable the UIView.
In the meantime, display some sort of activity indicator, e.g. UIActivityIndicatorView.
Of course, only do the above in a case where you can't perform the task in the background. Never needlessly block the UI. I'm sure you know that already of course but it's worth restating for any people new to the platform that might be reading this.
To invoke on the main thread, use one of the variants of NSObject's performSelectorOnMainThread method. Or, alternatively, queue it on gcd using the main queue by calling the dispatch_get_main_queue function.
You can do this using GCD functions:
// Show an UILoadingView, etc
[GKPlayer loadPlayersForIdentifiers:singleOpponentArray
withCompletionHandler:^(NSArray *players, NSError *error) {
// Define a block that will do your thing
void (^doTheThing)(void) = ^(void){
// this block will be run in the main thread....
// Stop the UILoadingView and do your thing here
};
// Check the queue this block is called in
dispatch_queue_t main_q = dispatch_get_main_queue();
dispatch_queue_t cur_q = dispatch_get_current_queue();
if (main_q != cur_q) {
// If current block is not called in the main queue change to it and then do your thing
dispatch_async(main_q, doTheThing);
} else {
// If current block is called in the main queue, simply do your thing
doTheThing();
}
}];
I'm trying to figure out the optimal way to perform an NSURLConnection Async Request and have the UI shielded with an alert view while the request is being fulfilled. I've had a lot of trouble getting this to work with a synchronous request because I could not figure out how to use the multi threading features or operation queues effectively with a synchronous request, so I figure this is the best way to go. Pseudo code or actual code is fine, I just need to know which direction to go. So far I figure:
Create a UIAlertView property
Create a void function that initiates the NSURLConnection, and display the view right after it initiates
Use the delegate method to close the AlertView window -(void)connectionDidFinishLoading or something like that.
Is it this simple, or am I missing something?
Probably the easiest way to do it is to use the UIApplication.networkActivityIndicatorVisible property, and do a sync request in a background thread.
-(void)loadURLInBackground:(NSURL*)url {
NSURLRequest* req = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
NSURLResponse* response = nil;
NSError* err = nil;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData* data = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&err];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if( data != nil ) {
[self performSelectorOnMainThread:#selector(processData:) withObject:data waitUntilDone:NO];
} else {
[self performSelectorOnMainThread:#selector(processError:) withObject:err waitUntilDone:NO];
}
}
Use [self performSelectorInBackground:#selector(loadURLInBackground:) withObject:url]; to call the method, then just implement processData: and processError:.
You do not want to use a UIAlertView - that is a modal dialog.
You want to use something like UIActivityIndicatorView to show the spinner while the background activity is going on.
Then, as you say, your delegate method can stopAnimating the activity indicator view.
If you want to show a message like "Downloading ...", then you can wrap your activity indicator inside another view, display that view, and remove it when the delegate calls back.
I've a multi-threading application in which each thread has to do some job, but at a certain point some code needs to be executed serially (like writing into sqlite3 database), so I'm calling that code to be performed on main thread using:
[self performSelectorOnMainThread:#selector(serialJob:) withObject:object waitUntilDone:YES];
and every thing went just fine except that when that code needs some time the user interaction with the application gets disabled until that code has been finished, so is there any way to make another ONE thread that can be run on background and can be called whenever I need it just like the main one so I can replace the previous call with:
[self performSelector:#selector(serialJob:) onThread:REQUIRED_THREAD withObject:object waitUntilDone:YES];
this thread should be some class's static data member to be accessed from all over the code.
any help would be very appreciated, and many thanks in advance...
This is quite easy to do, just spawn your thread and let it run it's runloop using [[NSRunLoop currentRunLoop] run]. That's all that is required to be able to use performSelector:onThread: with a custom thread.
If you are on iOS 4 or newer you should consider using Grand Central Dispatch queues instead of threads though. The GCD APIs are much easier to use and can utilize the system resources much better.
Like Sven mentioned, look into Grand Central Dispatch.
You can create a queue like this:
dispatch_queue_t myQueue = dispatch_queue_create("com.yourcompany.myDataQueue", NULL);
Now you can call blocks on that queue:
dispatch_async(myQueue, ^{
// Your code to write to DB.
});
When you're done, don't forget to release the queue:
dispatch_release(myQueue);
Due to the my question that I need the current thread to be blocked until the database job has been finished, I've tried these two solutions and they worked perfectly. You can either use critical sections or NSOperationQueue and I prefer the first one, here is the code for both of them:
define some class "DatabaseController" and add this code to its implementation:
static NSString * DatabaseLock = nil;
+ (void)initialize {
[super initialize];
DatabaseLock = [[NSString alloc] initWithString:#"Database-Lock"];
}
+ (NSString *)databaseLock {
return DatabaseLock;
}
- (void)writeToDatabase1 {
#synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
- (void)writeToDatabase2 {
#synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
OR to use the NSOperationQueue you can use:
static NSOperationQueue * DatabaseQueue = nil;
+ (void)initialize {
[super initialize];
DatabaseQueue = [[NSOperationQueue alloc] init];
[DatabaseQueue setMaxConcurrentOperationCount:1];
}
+ (NSOperationQueue *)databaseQueue {
return DatabaseQueue;
}
- (void)writeToDatabase {
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(FUNCTION_THAT_WRITES_TO_DATABASE) object:nil];
[operation setQueuePriority:NSOperationQueuePriorityHigh];
[[DatabaseController databaseQueue] addOperations:[NSArray arrayWithObject:operation] waitUntilFinished:YES];
[operation release];
}
these two solutions block the current thread until the writing to database is finished which you may consider in most of the cases.