What is the equivalent of these operations on ios3
[NSOperationQueue mainQueue];
[NSOperationQueue currentQueue];
There really wasn't an equivalent for +currentQueue. For +mainQueue you'd call
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
with a method that contained the work that needed to be done on the main thread.
There is no other alternative other than rolling your own.
Something like this might work: (untested)
#interface NSOperationQueue(MainQueueAdditions)
+ (NSOperationQueue *) mainQueue;
#end
#implementation NSOperationQueue(MainQueueAdditions)
+ (NSOperationQueue *) mainQueue {
static NSOperationQueue *queue = nil;
if(queue == nil) queue = [[NSMainOperationQueue alloc] init];
return queue;
}
#end
#interface NSMainOperationQueue : NSOperationQueue {}
#end
#implementation NSMainOperationQueue
- (void) addOperation:(NSOperation *) operation {
[self queueOperationInternal:operation];
}
- (void) addOperationWithBlock:(void (^)(void))block {
[self queueOperationInternal:[NSBlockOperation blockOperationWithBlock:block]];
}
- (void) queueOperationInternal:(NSOperation *) operation {
[[NSRunLoop mainRunLoop] performSelector:#selector(start) target:operation argument:nil order:-[operation queuePriority] modes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
}
#end
Related
I'm trying to test a class that has a method responding asynchronously that makes a network call, stubbed by Nocilla.
The tests runs fine when I run the test alone. But as soon as I launch my whole test suite, it blocks here for a while and finishes with a:
Thread 1: signal SIGABRT
Here is my test class:
#interface SMIMyServiceTests : XCTestCase
#property (strong, nonatomic) SMIMyService *service;
#end
#implementation SMIMyServiceTests
+ (void)setUp {
[[LSNocilla sharedInstance] start];
}
+ (void)tearDown {
[[LSNocilla sharedInstance] stop];
}
- (void)setUp {
[super setUp];
self.service = [[SMIMyService alloc] init];
}
- (void)tearDown {
[[LSNocilla sharedInstance] clearStubs];
self.service = nil;
[super tearDown];
}
- (void)testFetch {
stubRequest(#"GET", #"http://mydevserver.192.168.1.15.xip.io/api/data.json").andReturn(200).withBody([MyUtil jsonFromFile:#"json-file" sender:self]);
XCTestExpectation *expectation = [self expectationWithDescription:#"Fetch"];
[self.service fetch:^(NSArray *data) {
XCTAssertTrue(data != nil);
XCTAssertEqual(data.count, 7);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
#end
Any idea what's going wrong?
I have an NSOperation subclass that I want to run concurrently.
My understanding is that for concurrent operations to work:
I need to define isConcurrent to return YES.
I need to define the start method
I need to send KVOs notification for isExecuting and isFinished when it's done.
Using #synthesize will automatically send the appropriate KVO notifications when the values for isExecuting and isFinished are changed.
Despite this, I have verified that my queue never moves on to the next item.
Here's the meat of my code:
#interface MyOperation()
#property (readwrite) BOOL isExecuting;
#property (readwrite) BOOL isFinished;
#end
#implementation MyOperation
- (void)start
{
#autoreleasepool {
self.isExecuting = YES;
self.HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];
_HTTPOperation.completionBlock = [^{
[self completed];
self.isExecuting = NO;
self.isFinished = YES;
} copy];
[_HTTPOperation start];
}
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)completed
{
}
#end
What am I missing?
(This is on an iPhone, but I can't imagine that matters.)
It looks like whatever KVO notifications #synthesize sends aren't enough for NSOperationQueue to move on.
Sending the notifications manually fixes the problem:
- (void)start
{
#autoreleasepool {
[self willChangeValueForKey:#"isExecuting"];
self.isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURLRequest *URLRequest = [self buildRequest];
if (!URLRequest) {
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
return;
}
self.HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];
_HTTPOperation.completionBlock = [^{
[self completed];
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
} copy];
[_HTTPOperation start];
}
}
See also:
Why does NSOperation disable automatic key-value observing?
What's your "queue" look like? Are you using an NSOperationQueue?
Anyway, I'll try to answer your question with what I understood :P
I would create a delegate for my NSOperation and have KVO take care of calling this.
Say for example your NSOperation class looks like this
#interface MyOperation : NSOperation
#property (assign) id<MyOperationDelegate> delegate;
Your implementation
#synthesize delegate;
#synthesize error;
-(id)init{
self = [super init];
if(self){
[self addObserver:self forKeyPath:#"isFinished"
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
-(void)dealloc{
[self removeObserver:self forKeyPath:#"isFinished"];
[super dealloc];
}
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if([keyPath isEqualToString:#"isFinished"] == YES){
if([self isCancelled] == NO){
if(delegate != nil && [delegate respondsToSelector:#selector(operationComplete:)]){
[delegate taskComplete:self];
}
}else{
if(delegate != nil && [delegate respondsToSelector:#selector(operationCancelled)]){
[delegate taskCancelled];
}
}
}
}
-(void)main{
[NSException exceptionWithName:kTaskException
reason:#"Only to be used with subclass"
userInfo:nil];
}
And finally your protocol
#class MyOperation;
#protocol MyOperationDelegate <NSObject>
#optional
-(void)operationComplete:(MyOperation*)operation;
-(void)operationCancelled;
Im using a threading class (.h/.m below) where the subclass is UIViewcontroller works without any issues.
#interface myFirstClass : UIViewController <MyOperationDelegate>{
However when I use it where the subclass is a NSobject to call a reachability class checking for internet connection, the App crashes when calling performSelectorOnMainThread? I dont understand why, there are no error when I build the App and when it crashes all i get is EXC_BAS_ACCESS. Is it not possible to do this when dealing with an NSObject? Any suggestion will be helpful for me.
#interface AppController : NSObject <MyOperationDelegate>{
myThreading.h
#protocol MyOperationDelegate
#required
-(void) updatedStatus:(NSArray*)items;
-(void) failedStatusWithError:(NSError*)error;
#end
#interface MyOperation : NSObject {
NSObject<MyOperationDelegate> * delegate;
NSOperationQueue *queue;
}
#property (retain) NSObject<MyOperationDelegate> *delegate;
-(void)load: (NSString *)stringUrlPath:(NSString *)functionAction;
#end
myThreading.m
#interface MyOperation (NSObject)
-(void)dispatchLoadingOperation:(NSDictionary *)aParameters;
#end
#implementation MyOperation
#synthesize delegate;
-(id)init
{
if ([super init]!=nil) {
queue = [NSOperationQueue new];
[queue setMaxConcurrentOperationCount:1];
}
return self;
}
-(void)load: (NSString *)stringUrlPath: (NSString *)functionAction {
[self dispatchLoadingOperation:[NSDictionary dictionaryWithObjectsAndKeys:
stringUrlPath, #"urlString", functionAction, #"action", nil]];
}
-(void)dealloc {
[queue cancelAllOperations];
self.delegate = nil;
[super dealloc];
}
-(void)dispatchLoadingOperation:(NSDictionary *)aParameters {
if([aParameters objectForKey:#"action"] == #"getStatus"){
#synchronized(self) {
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(fetchCheckStatus:)
object:aParameters];
[queue addOperation:operation];
[operation release];
}
}
}
-(void) fetchCheckStatus:(NSDictionary *)aParameters
{
NSData* data = [[NSMutableData alloc] initWithContentsOfURL:[NSURL URLWithString:[aParameters objectForKey:#"urlString"]] ];
NSError *error;
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (responseString != nil) {
NSMutableArray *rssItems;
[self.delegate performSelectorOnMainThread:#selector(updatedStatus:) withObject:[NSArray arrayWithObjects:rssItems, nil] waitUntilDone:NO];
} else {
[queue cancelAllOperations];
[self.delegate performSelectorOnMainThread:#selector(failedStatusWithError:) withObject:error waitUntilDone:NO];
}
[responseString autorelease];
[data release];
}
#end
The problem are these lines:
NSMutableArray *rssItems;
[self.delegate performSelectorOnMainThread:#selector(updatedStatus:) withObject:[NSArray arrayWithObjects:rssItems, nil] waitUntilDone:NO];
You declare a variable rssItems but don't set it. It will contain random garbage from the stack which will then be interpreted as a pointer. Maybe sometimes you're lucky and the value is actually a pointer to a living object, but more likely dereferencing it causes your crash.
You need to actually initialize the variable, e.g.:
NSMutableArray *rssItems = nil;
but I guess you really want:
NSMutableArray *rssItems = [NSMutableArray array];
I am a newbie to Objective-C. I'm currently working on threads.
I have to make a synchronous execution of threads. I'm using NSInvocationOperaion to spawn a thread.
I have two threads. I need to wait for the 1st thread to signal a event or the timeout.
Signalling a event can be done by NSConditionLock. How to signal a timeout. I could not use waitUntilDate method here as the timeout is not a fixed value.
Is there any way to do this?
EDITED
main.m
------
#import "PseudoSerialQueue.h"
#import "PseudoTask.h"
int main()
{
PseudoSerialQueue* q = [[[PseudoSerialQueue alloc] init] autorelease];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test2)];
[q quit];
return 0;
}
PseudoTask.h
-----------------
#import <Foundation/Foundation.h>
#interface PseudoTask : NSObject {
id target_;
SEL selector_;
id queue_;
}
#property(nonatomic,readonly)id target;
-(id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
-(void)exec;
#end
PseudoTask.m
-----------------
#import "PseudoTask.h"
#implementation PseudoTask
#synthesize target = target_;
-(id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue
{
self = [super init];
if (self) {
target_ = [target retain];
selector_ = selector;
queue_ = [queue retain];
}
return self;
}
-(void)exec
{
[target_ performSelector:selector_];
}
-(void)dealloc
{
[super dealloc];
[target_ release];
[queue_ release];
}
#end
PseudoSerialQueue.h
----------------------------
#import <Foundation/Foundation.h>
#import "PseudoTask.h"
#interface PseudoSerialQueue : NSObject {
NSCondition* condition_;
NSMutableArray* array_;
NSThread* thread_;
}
-(void)addTask:(id)target selector:(SEL)selector;
#end
PseudoSerialQueue.m
----------------------------
#import "PseudoSerialQueue.h"
#implementation PseudoSerialQueue
-(id)init
{
self = [super init];
if (self) {
array_ = [[NSMutableArray alloc]init];
condition_ = [[NSCondition alloc]init];
thread_ = [[NSThread alloc] initWithTarget:self selector:#selector(execQueue) object:nil];
[thread_ start];
}
return self;
}
-(void)addTask:(id)target selector:(SEL)selector
{
[condition_ lock];
PseudoTask* task = [[PseudoTask alloc] initWithTarget:target selector:selector queue:self];
[array_ addObject:task];
[condition_ signal];
[condition_ unlock];
}
-(void)quit
{
[self addTask:nil selector:nil];
}
-(void)execQueue
{
for(;;)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
[condition_ lock];
if (array_.count == 0) {
[condition_ wait];
}
PseudoTask* task = [array_ objectAtIndex:0];
[array_ removeObjectAtIndex:0];
[condition_ unlock];
if (!task.target) {
[pool drain];
break;
}
[task exec];
[task release];
[pool drain];
}
}
-(void)dealloc
{
[array_ release];
[condition_ release];
[super dealloc];
}
#end
I could not pass self from main.Hope i'm mistakenly calling it.
Error:'self' undeclared is coming.
I could not understand
-(void)exec
{
[target_ performSelector:selector_];
}
in PseudoTask.m
target_ is not a method and its an ivar.
I am not getting any error or warning.But i could not understand that code.
I am writing what i have understood from your program.Please correct me if i my way of understanding the program is wrong.
The Thread execQueue is spawned when the PseudoSerialQueue is initialised and it waits for the signal from the addTask method.
The addTask method is called in the quit method and the parameters passed are nil.I could not understand why to pass a nil parameter.
It would be helpful if you explain about it.Thanks.
You mean NSCondition? You can use waitUntilDate: as relative time.
[condition lock];
// wait 5 seconds.
[condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
[condition unlock];
EDITED:
My PseudoSerialQueue class requires to be called from a class that is derived from NSObject like the following.
#interface Test : NSObject
#end
#implementation Test
- (void)test0
{
}
- (void)test1
{
}
- (id)init
{
self = [super init];
return self;
}
- (void)exec
{
PseudoSerialQueue *q = [[PseudoSerialQueue alloc] init];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test0)];
[q quit];
}
#end
You can call it from main function.
Test *test = [[Test alloc] init];
[test exec];
I could not understand why to pass a nil parameter.
I just only chose it for the message of quitting the loop in the PseudoSerialQueue.
Let the 1st thread signal the 2nd one in both cases; then in the second thread you can tell in which case you are based on some read-only flag in the 1st controller or in your model (say, isDataAvailable).
I am using an NSURLConnection in an NSThread but none of the NSURLConnection's delegate methods are executed! I have a main method in my NSTread subclass and a while loop that keeps the thread active. Any help?
Sorry for all of this code but I think its the best way to describe my problem. So this is an object that does the async connection calling createConnectionWithPath:userObjectReference
#interface WSDAsyncURLConnection : NSObject
{
NSMutableData *receivedData;
NSDate *connectionTime;
NSURLConnection *connection;
id _theUserObject;
}
#property (nonatomic, retain) NSMutableData *receivedData;
#property (nonatomic, retain) NSDate *connectionTime;
#property (nonatomic, assign) NSURLConnection *connection;
- (void)createConnectionWithPath:(NSString *)thePath userObjectReference:(id)userObject;
#end
#import "WSDAsyncURLConnection.h"
#implementation WSDAsyncURLConnection
#synthesize connectionTime, receivedData, connection;
- (void) terminate
{
if (self.connection) {
[self.connection release];
self.connection = nil;
}
}
- (void) createConnectionWithPath:(NSString *)thePath userObjectReference:(id)userObject;
{
_theUserObject = userObject;
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:thePath]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60];
self.connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];
if (self.connection)
{
/* record the start time of the connection */
self.connectionTime = [NSDate date];
/* create an object to hold the received data */
self.receivedData = [NSMutableData data];
}
}
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.receivedData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
/* appends the new data to the received data */
[self.receivedData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self terminate];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
// displays the elapsed time in milliseconds
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:self.connectionTime];
// displayes the length of data received
NSUInteger length = [self.receivedData length];
NSString* aStr = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];
[self terminate];
[[NSNotificationCenter defaultCenter] postNotificationName:WSDAsynchURLConnectionDidFinished
object:_theUserObject
userInfo:[NSDictionary dictionaryWithObject:aStr forKey:#"urlResponseString"]];
NSLog(#"ti=%f, l=%d, response=%#", elapsedTime, length, aStr);
}
#end
This code is mostly from an apple's example project and it works fine outside an NSThread.
But when I use it in the following thread subclass no delegate method is executed !!
#implementation IncomingThread
- (void) main {
NSAutoreleasePool *poool = [[NSAutoreleasePool alloc] init];
// I start the URLConnection here ... But no delegate is executed !
[urlConn createConnectionWithPath:#"http://localhost:8888" userObjectReference:nil];
while (![self isCancelled]) {
[NSThread sleepForTimeInterval:3.];
}
[poool release];
}
- (id) init
{
self = [super init];
if (self != nil) {
urlConn = [[WSDAsyncURLConnection alloc] init];
}
return self;
}
- (void) dealloc {
NSLog(#"deallocating (%#)...", [self className]);
[urlConn release];
[super dealloc];
}
First of all: you don't need to use NSURLConnection in the separate thread. Since it is asynchronous it doesn't block the main thread.
Second: there is not processing of your connection because you always stop the execution of the thread's runloop with this peace of code:
while (![self isCancelled]) {
[NSThread sleepForTimeInterval:3.];
}
From the docs for the sleepForTimeInterval:
No run loop processing occurs while the thread is blocked.
You’re doing this the hard way. NSURLConnection does not play very nice with threads, since it needs a run loop to work. Your thread does not have a run loop and therefore the code does not work. Why not run the connection on the main thread? Or you can wrap the connection in an NSOperation, sample code here. And nowadays you also have the option to use a synchronous connection and dispatch it to a global GCD queue.
Did you remember to assign the delegate?
Something like:
self.connection.delegate = self;
Just because your class WSDAsyncURLConnection implements the delegate methods, doesn't mean they are being called.
Late but it may save others life :)
Solution link is : NSURLConnection delege methods does not work