Very simple code:
queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(#"%#", [NSThread mainThread]? #"main" : #"not main");
}];
prints "main".
Why? Isn't it suppose to run in bg thread asynchronously, unless i call [NSOperationQueue mainQueue] ?
[NSThread mainThread] always returns an object (and thus yielding YES when cast to BOOL), since there is a main thread when your program is running.
If you want to check whether the current thread is the main thread or not, you need to use the currentThread method of NSThread.
NSLog(#"%#", [[NSThread currentThread] isEqual:[NSThread mainThread]]
? #"main" : #"not main");
NSThread has a better method; it seems you can use the isMainThread method to check whether the current thread is the main thread or not:
if ([[NSThread currentThread] isMainThread]) {
//
}
Well as user #borrrden pointed out, you just need to use [NSThread isMainThread],
if([NSThread isMainThread]){
//
}
See the NSThread documentation.
Related
Cause NSThread can't be joinable I tried next method, it seems works ok, but is still very bad solution or good enough?
// init thread
NSThread *mythread = [[NSThread alloc] initWithTarget:self selector:#selector(runThread:) object: nil];
// start thread
mythread.start;
// JOIN NSThread custom implementation
// wait until thread will finish execution
if (mythread.isExecuting) {
while(mythread.isExecuting) {
sleep(0);
}
} else if (!mythread.isCancelled && !mythread.isFinished) {
while(!mythread.isExecuting) {
sleep(0);
}
while(mythread.isExecuting) {
sleep(0);
}
}
A live lock like this is a bad idea on an iPhone, because it eats battery and CPU without doing anything, even though calling sleep(0) might give it a little bit of rest.
You could use NSCondition to implement joining. The idea is that the parent thread will wait on the NSCondition, and the worker thread would signal on that condition when it finishes:
- (void)main1 {
// thread 1: start up
_joinCond = [NSCondition new];
[mythread start];
// thread 1: join, i.e. wait until thread 2 finishes
[_joinCond lock];
[_joinCond wait];
[_joinCond unlock];
}
- (void)main2 {
// thread 2 (mythread):
// ... work, work, work ...
// now we're done, notify:
[_joinCond lock];
[_joinCond signal];
[_joinCond unlock];
}
I'm curious whether it is safe to finish custom NSOperation on different thread that their origin thread e.g.:
I have my custom operation class which is executed on different thread let say thread B (not main thread), then in this operation class I have obviously start() method, where on very beginning I'm invoking [self markAsExecuting]; method to indicate that operation already starts their work and of course after some stuff I have to invoke [self markAsFinished]; to indicate that all work has been done and operation is finished.
My question is: whether it is safe to invoke [self markAsFinished]; method on different thread that my operation itself is execute let say thread C?
Some snipped code:
- (void)start {
#autoreleasepool {
// *** Thread B
[self markAsExecuting];
[apiManager fetchData completion:^(NSDictionary *data, NSError *error) {
if (error == nil) {
// As we know in this case when we do not indicate that AFNetworking response handle should be executed on different thread by default it will be executed on main thread, so that why I'm dispatching expensive work to the background
self.queue = dispatch_queue_create("com.something.myapp.backgroundQueue", 0);
dispatch_async(self.queue, ^{
// Some expensive work
// *** Thread C
[weakSelf markAsFinished];
});
} else {
// *** Main Thread
[weakSelf markAsFinished];
}
}];
}
}
I'm hope that my problem explanation was clear enough.
My question was maybe not precisely enough, markAsFinish method only set the Operation state to .finish so it doesn't make big different from which thread it is called in this case
I have created NSOperation class in that class i am calling NSURLConnection to fetch some data.
I am calling NSURLConnection using main thread inside NSOperation class.
NSURLConnection's delegate is set to NSOperation class object.
Call from NSURLConnection comes on main thread.
I need to process this data using the same operation thread. how do i achieve this ??
#implementation ModelCreationSearchOperation {
int try;
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)start
{
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
dispatch_async(dispatch_get_main_queue(), ^{
if (self.isCancelled) {
[self finish];
return;
}
});
[self fetchData];
}
-(void)fetchData {
dispatch_async(dispatch_get_main_queue(), ^{
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
});
}
- (void)finish
{
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
[self cancel];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//Main thread
//Want to perform parsing of response data on operation thread ....
}
You say that you "want to perform parsing of response data on operation thread." Do you really need to run it on the operation thread, or do you just need to get it off the main thread? The operation queue doesn't necessarily have a single, dedicated thread, so the question doesn't quite make sense. (It's one of the beauties of dispatch queues and operation queues, that it manages the threads for us and we generally don't have to get involved in those details.)
If you simply want the code in connectionDidFinishLoading to run on a background thread (if, for example, you're doing something exceptionally slow in this delegate method), just dispatch it to a background thread (you could use a global queue for that). If you want a serial queue for these connectionDidFinishLoading calls, create your own serial queue for that and dispatch this code to that queue. But if it's not too computationally intensive (e.g. parsing JSON or something like that), you can often just let it run on the main thread without incident.
As an aside, you can, if you really want, create a dedicated thread for your NSURLConnection delegate calls, and schedule the connection on that thread, but it's generally overkill. But see AFNetworking code for example of this implementation. This is illustrated in How do I start an Asychronous NSURLConnection inside an NSOperation?
I'm trying to figure out what is the difference between those 2.
Does it mean currentQueue cannot be mainQueue or it's a wrong assumption?
currentQueue return mainQueue if you call it in main-thread.
new is new queue with new threads
new is initializer method which NSOperationQueue inherits from NSObject
Documentation says:
This method is a combination of alloc and init. Like alloc, it initializes the isa instance variable of the new object so it points to the class data structure. It then invokes the init method to complete the initialization process.
This means calling +new creates new instance of NSOperationQueue
currentQueue returns you the queue on which the method was called.
NSOperationQueue docs of currentQueue says:
The operation queue that started the operation or nil if the queue could not be determined
I.e. if method calling +currentQueue is mainQueue, it can return mainQueue
prove of concept write by #Cy-4AH. Hop this can clarify a bit.
-(void) viewDidLoad {
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^(void){
//this block will executed in a separate thread (not the main thread)
if ([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]) {
NSLog(#"= in block");
} else {
NSLog(#"not = in block"); //This will be log
}
}];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:blockOperation];
//this block will executed in the main thread
if ([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]) {
NSLog(#"= outside of block"); //This will be log
} else {
NSLog(#"not = outside of block");
}
}
This is essentially what I'm doing to run an asynchronous method synchronously:
This essentially works when called once, but when called multiple times, it will eventually stay inside the while loop and never get signaled. Any ideas on how to set a timer to eventually time out after sometime?
__block SomeClass *result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
dispatch_semaphore_signal(semaphore);
}];
});
// wait with a time limit
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
dispatch_release(semaphore);
Thanks
That looks kind of like GCD abuse to me. ;) Are you running the run loop because this is executing on the main thread? Why not just use a dispatch_async() from your completion handler to invoke a handler on the main thread? eg:
- (void)handleDataReady: (id) results error: (NSError *) error {
// update your app
}
- (void)performAsyncUpdate {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self handleDataReady:responseObject error:error];
}];
});
}
If you really want to make it synchronous, i.e. blocking the calling thread until the operation completes then use the following pattern (of course you want to avoid blocking threads if possible)
NSCondition *waitCondtion = [NSCondition new];
__block BOOL completed = NO;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
[waitCondtion lock];
completed = YES;
[waitCondition signal];
[waitCondition unlock];
}];
});
[waitCondtion lock];
if (!completed)
[waitCondtion wait];
[waitCondition unlock];
You can also use "waitUntilDate:" to timeout the wait after a period.
However, this pattern only works as long as the "someMethodWithCallback does not call its callback block on the same thread that is being blocked. I have copied your code because it is not obvious how "someMethodWithCallback" is implemented. Since this method is using an asynchronous pattern, then it must be doing something asynchronously therefore why are you calling it inside a dispatch_async? What thread will it call its callback block on?
You should "fill" the completion handler with whatever code you require to process the result when the completion handler finished (and also completely removing that run loop).
In order to "abort" an asynchronous operation, you should provide a cancel message which you send the asynchronous result provider.
In your case, since you have a singleton, the cancel message would have to be send like this:
[[SomeManager sharedInstance] cancel];
When the operation receives the cancel message, it should as soon as possible abort its task and call the completion handler with an appropriate NSError object indicating that it has been cancelled.
Note, that cancel messages may be asynchronous - that means, when it returns, the receiver may still execute the task.
You may achieve a "timeout" with setting up a timer, which sends the cancel message the operation, unless it has been invalidated when the operation finished.