1

First post here, so I hope it is detailed enough.

While developing an Iphone App I am confronted with some strange behavour. A member variable of a certain instance of my "WebserviceConnection" class seems to obtain the value I assign to another instances of the same class.

For illustration: This is en excerpt of my log. I assume the 0x000000 is an instance ID. The fourth response should be "<-: 1".

2011-11-03 16:25:13.227 Dashboard[540:707] ->: 1, <WebserviceConnection: 0x11f950>
2011-11-03 16:25:13.256 Dashboard[540:707] ->: 0, <WebserviceConnection: 0x323db0>
2011-11-03 16:25:15.318 Dashboard[540:707] <-: 0, <WebserviceConnection: 0x323db0>
2011-11-03 16:25:15.325 Dashboard[540:707] <-: 0, <WebserviceConnection: 0x11f950>

The class is a NSUrlConnection delegate which exhibits this behavour when two connections are open at the same time.

This class: WebserviceConnection.h

(The ConnectionType is an enum)

#import "WebserviceConnection.h"
#import "WebserviceUtility.h"

@implementation WebserviceConnection

BOOL isCanceled;
NSDictionary *result;
ConnectionType connectionType;
id <WebserviceConnectionDelegate> delegate;

- (id)initWithDelegate:(id)webServiceDelegate connectionType:(ConnectionType) type {
    delegate = webServiceDelegate;
    connectionType = type;
    isCanceled = NO;
    NSLog(@"->: %i, %@", connectionType, self);
    return self;
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

    switch (connectionType) {
        case GetAllAlerts:
            result = [WebserviceUtility getJsonFromData:data]; 
            break;
        case GetServerAlerts:
            result = [WebserviceUtility getJsonFromData:data]; 
            break;
        case GetServers:
            result = [WebserviceUtility getJsonFromData:data]; 
            break;
        default:
            result = nil;
            break;
    }
}

- (void)displayErrorAlert {
    UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:@"Fout" message:@"Verbinding met webservice niet mogelijk" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
    [errorMessage show];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    if(!isCanceled) {
        @try {
            [delegate connection:connection ofType:connectionType didFinishWithError: [NSDictionary dictionaryWithObject:@"error" forKey:@"WebserverConnectionFailed"]];
        }
        @catch (NSException *e) {}
        @finally {}
        [self displayErrorAlert];
    }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSLog(@"<-: %i, %@", connectionType, self);
    if(!isCanceled) {
        [delegate connection:connection ofType:connectionType didFinishWithResult:result];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    NSURLCredential *credential = [WebserviceUtility getCredentials];
    if ([challenge previousFailureCount] == 0) {
        [[challenge sender] useCredential:credential
               forAuthenticationChallenge:challenge];
    }
    else {
        [delegate connection:connection ofType:connectionType didFinishWithError: [NSDictionary dictionaryWithObject:@"error" forKey:@"WebserverConnectionFailed"]];
        [self displayErrorAlert];

    }
}

- (void)delegateDidDealloc {
    NSLog(@"!!: %i, %@", connectionType, self);
    isCanceled = YES;
}

@end

Used like this:

- (void) getAllAlerts {
    NSURLRequest *request = [WebserviceUtility getRequestForPath:@"/dashboard/DashboardAppleConnector.asmx/GetActiveAlerts"];
    webserviceConnection = [[WebserviceConnection alloc] initWithDelegate:self connectionType:GetAllAlerts];
    connection = [[NSURLConnection alloc] initWithRequest:request delegate: webserviceConnection];
}

When another ViewController with its own webserviceConnection instance uses its instance (similar to getAllAlerts) all goed pearshaped!

Any thoughts anyone?

Regards, Bert

Berdus
  • 633
  • 5
  • 11

2 Answers2

1

The definition block:

BOOL isCanceled;
NSDictionary *result;
ConnectionType connectionType;
id <WebserviceConnectionDelegate> delegate;

Declares those four things to be global variables, exactly as if they weren't in the @implementation block. Simply putting things inside @implementation doesn't make them local to the object — it just explains which object all the subsequent method implementations belong to.

If you don't mind putting implementation specifics into your header files, you could move them into the @interface declaration, e.g.

@interface WebserviceConnection
{
    BOOL isCanceled;
    NSDictionary *result;
    ConnectionType connectionType;
    id <WebserviceConnectionDelegate> delegate;
}

// etc

@end

You can keep them purely internal to the implementation at the cost of some repetitive syntax by adding them to your class through a category, e.g.

#import "WebserviceConnection.h"
#import "WebserviceUtility.h"

@interface WebserviceConnection() // a category to add additional properties
@property (nonatomic, assign) BOOL isCanceled;
@property (nonatomic, retain) NSDictionary *result;
@property (nonatomic, assign) ConnectionType connectionType;
@property (nonatomic, assign) id <WebserviceConnectionDelegate> delegate;
@end

@implementation WebserviceConnection

// synthesising the properties also adds the named properties as instance variables
@synthesize isCanceled;
@synthesize result;
@synthesize connectionType;
@synthesize delegate;

- (id)initWithDelegate:(id)webServiceDelegate ... etc...

Aside: a method called getJsonFromData: should return a non-owning reference according to Cocoa naming conventions since it doesn't contain 'new', 'alloc', 'retain' or 'create'. Which, if you were to obey, would leave you with a dangling pointer in result in the code as presented.

Tommy
  • 99,986
  • 12
  • 185
  • 204
1

It looks like the problem is happening because of the way you are declaring your variables like connectionType. If you want them to be declared as instance variables, you should be putting them in the interface declaration:

@interface WebServiceConnection {
    BOOL isCanceled;
    NSDictionary *result;
    ConnectionType connectionType;
    id <WebserviceConnectionDelegate> delegate;
}
@end

By declaring them in the @implementation block you are actually creating global variables, not instance variables.

See this SO post for more information

Community
  • 1
  • 1
Tim Dean
  • 8,253
  • 2
  • 32
  • 59
  • Works! Thanks... Not only an answer to my question but an insight to obj-c too! One learns as one goes... – Berdus Nov 03 '11 at 16:15