I recently started using Ben Gottlieb's Twitter-OAuth-iPhone class to post status updates in app. I have successfully done so, when executed on the application's main thread. But, when I throw it in a "NSThread detachNewThreadSelector" the post never makes it to twitter. It processes fine and I get a proper [connection identifier], but the results never make it on twitter. Any idea how I can get this to run in a thread without bringing it back to the main thread?
Code added
Tweet method
-(void)tweet{
if([_engine isAuthorized]){
[_engine sendUpdate:#"Tweeting"];
}
}
Doesn't work:
[NSThread detachNewThreadSelector:#selector(tweet) toTarget:self withObject:nil];
Does work (from main app):
[self tweet];
Does work (from within a thread):
[self performSelectorOnMainThread:#selector(tweet) withObject:nil waitUntilDone:NO];
Related
I have been looking over the internet, but could not find the perfect tutorial for threading in iOS, currently i am working on iOS 5 , tutorial found on web were old, my app needs to fetch data from webservices, so I want to call network operation in a thread, I have a tabbar, in each tab it loads different type of data from web, as it start fetching data , I dont want my app to stuck on that tab, i want that user can navigate to other tab meanwhile its fetching data for that tab. how can it be possible
EDIT: I want to have a flow :
//in a thread
fetchDataFromWeb(){//during this call activity indicator
//fetch and make an array of ojbects
}
when data is loaded trigger the populate funciton
laoddata(){//remove activity indicator
//load tableview and other views
}
how will i know my thread finished its process
Take a look to NSOperation, NSThread or Grand Central Dispatch
Edit: NSThread example
//on main thread on some action
NSDictionary *myParameterDitionary = [..];
[NSThread detachNewThreadSelector:#selector(aBackThreadMethod:) toTarget:self withObject:myParameterDitionary];
//this is on back thread called
- (void)aBackThreadMethod:(NSDictionary *)variablesDictionary{
#autoreleasepool {
//presess data
NSDictionary *responseDictionary = [..];
[self performSelectorOnMainThread:#selector(didABackThreadMethod:) withObject:responseDictionary waitUntilDone:YES];
}
}
//call back on main thread
- (void)didFinishABackThreadMethod:(NSDictionary *)responseDictionary{
//do stuff ex update views
}
The #autoreleasepool is for ARC. If you use manual memory management you have to do:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release];
I am using the NSURLConnection to download a video file from the server, at the same time playing the video by passing different url link to the movieplayer.
The problem is some blocking of the UI. During downloading we are unable to interact with the UI, like player zoom, pause button are blocked.
Code is like this for connection:_
connection1=[[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]] delegate:self];
in same mithod we are calling the
[playerInstance Play]
Please can you explain me where the problem is.
I can't understand your problem exactly. But I think you doing the two works (downloadig, playing file) on a same thread, probably main thread. So this may happen. So try to run the two processes in a separate threads.
[self performSelectorOnMainThread:#selector(playfile:) withObject:nil waitUntilDone:NO];
[self performSelectorInBackground:#selector(downloadfile:) withObject:nil];
You should make function for NSUrl operation and if it is already made call this function in following way for asynchronous communication.
[self performSelector:#selector(method) withObject:nil afterDelay:1];
I am downloading data from my server it is taking like 5 to 6 minutes for downloading..while downloading I am not able to do any work in my application. How to send downloading process on background so that user can navigate within application and after download. we will inform user downloading completed..
Thank you.
Create the new thread:
[NSThread detachNewThreadSelector:#selector(myMethod)
toTarget:self
withObject:nil];
Create the method that is called by the new thread:
- (void)myMethod
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/*** code that should be run in the new thread goes here ***/
[pool release];
}
What if you need to do something to the main thread from inside your new thread (for example, show a loading symbol)? Use performSelectorOnMainThread.
[self performSelectorOnMainThread:#selector(myMethod)
withObject:nil
waitUntilDone:false];
What you need to do is have your downloading code on a separate thread (NSThread on iOS). Here's a tutorial to get you started.
I had the UIActivityIndicatorView working fine in simulator and other 3.0 devices in my app. But I found out that it was not spinning (or showing) in the new iphone 4. Basically I need to show the activity indicator when a button is clicked and hide it when the button click event is complete. I was using the approach below.
[NSThread detachNewThreadSelector: #selector(spinBegin) toTarget:self withObject:nil];
from this link. As mentioned, it correctly spins the activity indicator on all except 4.*.. not sure why. To get around this, I also followed another approach something like (from developer.apple.com)
`
(IBAction)syncOnThreadAction:(id)sender
{
[self willStartJob];
[self performSelectorInBackground:
#selector(inThreadStartDoJob:)
withObject:theJobToDo
];
}
(void)inThreadStartDoJob:(id)theJobToDo
{
NSAutoreleasePool * pool;
NSString * status;
pool = [[NSAutoreleasePool alloc] init];
assert(pool != nil);
status = [... do long running job specified by theJobToDo ...]
[self performSelectorOnMainThread:
#selector(didStopJobWithStatus:)
withObject:status
waitUntilDone:NO
];
[pool drain];
}
`
The problem with this was that, it is showing the acitivityVIewIndicator spinning correctly (at least on the simulator) but after it stops, the built in activity indicator in the top bar (where it shows the battery% etc) is still spinning.
I'm new to objective C. I have finished my app completely but for this silly thing. I realize there is no way to display UIActivityView without starting another thread. and finally, just to rant, I don't understand why they have to make it so complicated. I mean they knew it was going to have this problem, why not provide a sample code everyone can use rather than deriving their own solutions.
Finally, can anyone please provide me with a direction or some sample code. I would really appreciate it. I have been searching for a few hours now and have not found anything really that works!
Why are you starting/stopping the indicator on a separate thread? Any methods you send to your UIActivityIndicatorView must be sent on the main (UI) thread.
Any events sent by a button pressed will automatically be run on the main thread. If you're using background threads to complete the process, you could do something like:
- (IBAction)buttonPressed:(id)sender {
// This runs on the main thread
[activityIndicator startAnimating];
[self performSelectorInBackground:#selector(inThreadStartDoJob:) withObject:theJobToDo];
}
- (void)inThreadStartDoJob:(id)theJobToDo {
// Set up autorelease pool
...
// Run your long-running action
...
// Stop the spinner. Since we're in a background thread,
// we need to push this to the UI Thread
[activityIndicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:YES];
}
Edit: As for the activity indicator in the top bar (where the battery is), doesn't this automatically start/stop based on network activity?
I have got a memory bug that seems to boil down to something happening in a thread. I am having difficulties troubleshooting this.
I have a UIViewController, that when active, i.e. the user is using its view, retrieves updates from a web service in an NSThread.
This is done every 3 minutes and this delay is controlled by a:
[self performSelector:#selector(timerDone) withObject:nil afterDelay:180.0];
The timerDone method now starts the NSThread that retrieves the web service data and also it sends the performSelector message again. This is a little "check for updates, populate views, shut everything down, repeat" routine that works just fine.
Now, the user can of course suddenly tap a button an load up a second UIViewController. When this happens I call:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(timerDone) object:nil];
And do my cleaning up in the dealloc method.
My question is now: What happens if the NSThread was running while the user changed the view and set in motion the deconstruction of this object that is the starting point of the NSThread?
Should I keep a BOOL around that tells me if the NSThread is still active, and if so, what to do with the NSThread if this is the case.
The threading is done like this:
- (void) runTimer {
[self performSelector:#selector(timerDone) withObject:nil afterDelay:180];
}
- (void) timerDone {
[self performSelector:#selector(runTimer) withObject:nil afterDelay:2];
[NSThread detachNewThreadSelector:#selector(updateAllVisibleElements) toTarget:self withObject:nil];
}
- (void) updateAllVisibleElements {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//call approiate web service
[pool release];
}
You have two problems here: first, you're using performSelector:withObject:afterDelay: to do what an NSTimer does best (periodic callback). cancelPreviousPerformRequestsWithTarget:selector:object: can be quite expensive, and because of your threading is likely creating race conditions.
Second problem: each thread has its own run loop, and both mechanisms (performSelector:... and NSTimer) and are tied to the current thread's run loop.
Here's what I recommend: Create a single, long-lived NSThread with its own explicit run loop for all your update needs. Look at the Threading Programming Guide for some good example code of this. On that thread, set up a 3-minute repeating NSTimer. Every 3 minutes, update.
If you need to schedule an update outside the three-minute cycle, then you use performSelector:onThread:withObject:waitUntilDone: to call your updateAllVisibileElements. The way I generally do this is to encapsulate all of the thread logic into a single object (WebServiceController or whatever). It creates it own NSThread and saves it in an ivar. Then I use code like this:
- (void)requestUpdate
{
if ([NSThread currentThread] != self.thread)
{
[self performSelector:#selector(update) onThread:self.thread withObject:nil waitUntilDone:NO];
return;
}
else
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//call approiate web service
[pool drain];
}
}
One more note: you mention that the background thread "populates views." A background thread should never call into UIKit. UIKit is not thread safe and should only be called on the main thread. I typically achieve this by posting notifications onto the main thread which the view controllers observe. The "updating" object should not know anything about the UI. That breaks the Model-View-Controller paradigm of Cocoa.