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
Related
Am trying to use an API class to fetch data from a URL and then have that data returned to a method inside the ViewController. I have the ViewController call the API method.
Have tried several places on SO already and come up with the following, but it is not working and as an Objective-C noobie have tried reading documentation and such but still not understanding what could be going wrong.
I have a viewcontroller calling an method 'fetch' inside a class 'api'.
I have the connection delegates inside api class and it works ok (correct data is printed inside connectionDidFinishLoading method).
I need a delegate to return the data to a method inside the viewcontroller class.
So far I have
ViewController.h
#import "Api.h"
#interface ViewController : UIViewController <apiDelegate>{
}
- (void)apiSucceeded:(NSString *)jsonString;
- (void)apiFailed:(NSString *)failedMessage;
#end
ViewController.m
#import "ViewController.h"
#import "Api.h"
- (void)apiSucceeded:(NSString *)jsonString {
NSLog(#"WORKED");
}
- (void)apiFailed:(NSString *)failedMessage {
NSLog(#"FAILED");
}
- (void)viewDidLoad {
[super viewDidLoad];
Api* myapi = [[Api alloc]init];
[myapi fetch];
}
#end
Api.h
#protocol apiDelegate
#required
- (void)apiSucceeded:(NSString *)jsonString;
- (void)apiFailed:(NSString *)failedMessage;
#end
#interface Api : NSObject {
id _delegate;
}
#property (nonatomic, assign) id _delegate;
-(void)fetch;
#end
Api.m
#import "Api.h"
#import "ViewController.h"
#synthesize _delegate;
- (void)fetch{
//connection setup....
[NSURLConnection connectionWithRequest:[request autorelease] delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.receivedData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// NSLog(#"append data");
[self.receivedData appendData:data];
// NSLog(#"Bytes: %d", [receivedData length]);
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//TODO error handling for connection
if ([_delegate respondsToSelector:#selector(apiFailed:)]) {
[_delegate apiFailed:[error localizedDescription]];
}
NSLog(#"Cannot Connect");
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DONE. Received Bytes: %d", [self.receivedData length]);
NSString *jsonString = [[NSString alloc] initWithBytes: [receivedData mutableBytes] length:[receivedData length] encoding:NSUTF8StringEncoding];
if ([_delegate respondsToSelector:#selector(apiSucceeded:)]) {
[_delegate apiSucceeded:jsonString];
}
}
#end
There is no error but it just does not run 'apiSucceeded' method.
Please dont misunderstand this as a question regarding 'connectionDidFinishLoading'. That bit works well, its the handing of the data BACK to a ViewController that is causing issues.
Can anybody see what I have done wrong please?
I seems that you forgot to set
myapi.delegate = self;
after
Api* myapi = [[Api alloc]init];
I can't seem to get a Delegate callback to work when I thread my code. I am trying to call a remote server using NSURLConnection to get an access_token. The access_token is received in connection:didReceiveResponse: delegate. This is not a problem until I thread it. I thought I was passing the correct objects for my delegates, but it does not reach connection:didReceiveResponse:
Can anyone see why connection:didReceiveResponse: in LoginViewController does not get called when I thread the call? Thanks.
LoginViewController.m
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSString *access_token = [self getTokenFromResponse: response];
[self.delegate didGetAccessToken:access_token];
}
- (void)fetchAccessTokenNoUI
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString: #"www.mysite.com"]];
[NSURLConnection connectionWithRequest:request delegate:self];
}
AccessToken.h
#interface AccessToken : NSObject
#property (atomic, strong) LoginViewController *loginViewController; // doing this to try to keep it persistent
- (void) fetchAccessTokenWithDelegate: (id <LoginDelegate>)delegate;
#end
AccessToken.m
- (void) fetchAccessTokenWithDelegate: (id < LoginDelegate >)delegate
{
dispatch_queue_t downloadQueue = dispatch_queue_create("Fetch access_token queue", NULL);
dispatch_async(downloadQueue, ^ {
// this works fine if I don't do it in a queue
self.loginViewController = [[LoginViewController alloc] init];
self.loginViewController.delegate = delegate;
[self.loginViewController fetchAccessTokenNoUI];
});
dispatch_release(downloadQueue);
}
CallingClass.m
- (void)didGetAccessToken:(NSString *)access_token
{
if (!access_token)
{
LoginViewController *userProfileViewController = [[LoginViewController alloc] init];
userProfileViewController.delegate = self;
[self.navigationController pushViewController:userProfileViewController animated:YES];
}
}
- (IBAction)favourite:(id)sender
{
AccessToken *accessToken = [[AccessToken alloc] init];
[accessToken fetchAccessTokenWithDelegate:self];
}
So after some more research, I found the solution.
I don't actually need AccessToken.h/m. I thought that the NSURLConnection call would block my UI thread. But NSURLConnection connectionWithRequest:delegate: is automatically run in a separate thread.
So all I have to do is delete AccessToken.h and AccessToken.m. Then, in CallingClass.m, I change favourte: to
- (IBAction)favourite:(id)sender
{
LoginViewController *loginViewController = [[LoginViewController alloc] init];
[loginViewController fetchAccessTokenWithDelegate:self];
}
That's it! Don't need to dispatch_queue_t at all.
Have you put stubs to implement the required protocol methods,
-(void)connectionDidFinishLoading:(NSURLConnection *)connection;
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
Because I had a similar problem which went away after implementing them.
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];
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
I am using custom delegate objects to do some cleanup tasks after a request finishes. ASIHTTPRequest doesn't retain delegates so I can't autorelease them. Right now this is how I am allocating and releasing the delegates.
App Delegate
MyDelegate *delegate = [[MyDelegate alloc] init];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:delegate];
MyDelegate.m
- (void)requestFinished:(ASIHTTPRequest *)request
{
[self release];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
[self release];
}
Is there a better way to do this? Having the delegates release themselves seems ugly and Xcode's build and analyze feels uncomfortable with what I'm doing.
A simple approach would be to maintain a mutable set of delgates for each active request in your main controller (the app delegate, in this case):
#interface MyAppController
{
NSMutableSet * activeDelegates;
}
#end
#implementation MyAppController
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
activeDelegates = [[NSMutableSet alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[activeDelegates release];
}
- (void)createRequest
{
MyDelegate *delegate = [[MyDelegate alloc] init];
[activeDelegates addObject:delegate];
[delegate release];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
...
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
MyDelegate *delegate = [request delegate];
[delegate doSomething];
[activeDelegates removeObject:delegate];
{
- (void)requestFailed:(ASIHTTPRequest *)request
{
[activeDelegates removeObject:[request delegate]];
}
#end
Why do you have a separate class purely to be a delegate? That's not how delegate objects typically work. Normally the controller that created the ASIHTTPRequest becomes the delegate, at which point you don't have to worry about releasing it because it will outlive the ASIHTTPRequest already (and if your controller gets dealloced before the ASIHTTPRequest is done, you need to cancel the request).
If You don't want to create a "controller" class for all your delegate instances, i would still at least follow the memory convention rules, and release the delegate immediately after setting it to asihhtprequest. Then i would include a propery in the delegate, something with a name managesItsOwnLifetime (BOOL) and on setting this to YES i would do a [self retain] ...