Экземпляры NSObject, выступающие в качестве делегата NSUrlConnection, по-видимому, не изолированы - PullRequest
1 голос
/ 03 ноября 2011

Первый пост здесь, так что я надеюсь, что он достаточно подробный.

При разработке приложения для Iphone я сталкиваюсь со странным поведением. Кажется, переменная-член определенного экземпляра моего класса "WebserviceConnection" получает значение, которое я присваиваю другим экземплярам того же класса.

Для иллюстрации: это отрывок из моего журнала. Я предполагаю , что 0x000000 - это идентификатор экземпляра. Четвертый ответ должен быть «<-: 1». </p>

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>

Класс является делегатом NSUrlConnection, который демонстрирует такое поведение, когда два соединения открыты одновременно.

Этот класс: WebserviceConnection.h

(Тип соединения - это перечисление)

#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

Используется так:

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

Когда другой ViewController со своим собственным экземпляром webserviceConnection использует свой экземпляр (аналогичный getAllAlerts), все становится грушевидным!

Есть мысли у кого-нибудь?

С уважением, Bert

Ответы [ 2 ]

1 голос
/ 03 ноября 2011

Похоже, проблема возникает из-за того, как вы объявляете свои переменные, такие как connectionType. Если вы хотите, чтобы они были объявлены как переменные экземпляра, вы должны поместить их в объявление интерфейса:

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

Объявляя их в блоке @implementation, вы фактически создаете глобальные переменные, а не переменные экземпляра.

См. этот пост для получения дополнительной информации

1 голос
/ 03 ноября 2011

Блок определения:

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

Объявляет эти четыре вещи глобальными переменными, точно так, как если бы они не были в блоке @implementation. Простое помещение вещей внутрь @implementation не делает их локальными для объекта - оно просто объясняет, какому объекту принадлежат все последующие реализации метода.

Если вы не возражаете против включения спецификаций реализации в заголовочные файлы, вы можете переместить их в объявление @interface, например,

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

// etc

@end

Вы можете хранить их исключительно внутри реализации за счет некоторого повторяющегося синтаксиса, добавляя их в свой класс через категорию, например

#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...

В сторону: метод с именем getJsonFromData: должен возвращать не владеющую ссылку в соответствии с соглашениями о присвоении имен Какао, так как он не содержит «new», «alloc», «retain» или «create». Который, если бы вы повиновались, оставил бы вас с висящим указателем в result в представленном коде.

...