Помощь в создании NSURLConnection в пользовательском классе

#objective-c #nsurlconnection

#objective-c #nsurlconnection

Вопрос:

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

Обновите следующий код, теперь он работает нормально….

Вот .h

 #import <Foundation/Foundation.h>
#import "CJSONDeserializer.h"

@interface StateFinder : NSObject 
{
    NSString *userState;
    NSURLConnection *connection;
}

-(id)initwithLatitude:(NSString *)latitude andLongitude:(NSString *)longitude;
@property (nonatomic, retain) NSString *userState;
@property (nonatomic, retain) NSURLConnection *connection;

@end
  

и .m

 #import "StateFinder.h"

@implementation StateFinder

@synthesize userState;
@synthesize connection;

-(id)initwithLatitude:(NSString *)latitude andLongitude:(NSString *)longitude
{
    if(self = [super init])
    {
        NSString *lat = latitude;
        NSString *lon = longitude;

        NSString *stateURLFinder = [NSString stringWithFormat:@"http://where.yahooapis.com/geocode?q=%@, %@amp;gflags=Ramp;flags=Jamp;appid=zqoGxo7k", lat, lon];

        //NSLog(stateURLFinder);

        NSURL *stateURL = [NSURL URLWithString:stateURLFinder];


        NSURLRequest *request = [[NSURLRequest alloc] initWithURL: stateURL];

        connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

        [request release];
    }
    return self;
}


-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"didReceiveResponse");
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"didFinishLoading");
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"didFailWithError");
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
    // Store incoming data into a string
    NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    NSLog(jsonString);
    // Yes, this is incomplete, but I was waiting for the method to fire before going
        // any further. This will at least show me the JSON data being returned from yahoo 
        // in string format so I can output it to the console via NSLog
}

- (void)dealloc 
{
    [userState release];
    [connection release];
    [super dealloc];
}

@end
  

Это текущий код, который я использую, и он отлично работает. Все, что я сделал, это включил методы connectionDidFinishLoading и didFailWithError в исходный код. Что касается соединения, которое было разорвано до его создания, я использовал приведенный выше код как есть без ранее упомянутых методов, и ни didReceiveData / didReceiveResponse не сработали. Только после того, как эти 2 метода были включены, методы начали вызываться. Не уверен, как, не уверен, почему, но это было единственное изменение из всех предложенных, которое сработало. Большое спасибо @Jiva DeVoe, @XJones, @jlehr и @Aby за все советы / намеки / suggestions!

Комментарии:

1. Согласно нескольким предложениям, я прокомментировал освобождение соединения, хотя, казалось, оно работало в других частях моего кода, используемого для вызова моей базы данных. Комментируя выпуск, я все еще не попал в методы didReceiveData или didReceiveResponse внутри этого класса. Есть другие предложения?

Ответ №1:

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

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

Бонусный совет:

Предполагаю, что вы, вероятно, делаете это для iOS или Mac и пишете приложения с графическим интерфейсом. Эти приложения имеют циклы запуска. Когда вы используете синхронную сеть, как предлагается в предыдущем ответе, вы предотвращаете выполнение этого цикла выполнения. Это означает, что если запрос занимает больше нескольких секунд, ваше приложение для iOS будет отключено операционной системой, а ваше приложение для Mac будет отображаться как не отвечающее. Ни один из этих результатов не является хорошим.

Комментарии:

1. Хм, насколько я понимаю, didReceiveData был вызван сразу после установления NSURLConnection. В моем контроллере представления, который выполняет вызов моей базы данных, так, как я его закодировал выше, и он возвращается без проблем.

2. Что касается приложения, оно будет выполняться только один раз (пользовательский класс вызывается один раз в методе init контроллера представления, а соединение внутри пользовательского класса также находится в методе init внутри пользовательского класса, поэтому его никогда не следует вызывать несколько раз.

3. Во-первых, не имеет значения, вызывается ли оно несколько раз. Не уверен, что вы предлагаете с этим. Во-вторых, WRT немедленно вызвал receiveData… ну, это вызывается, как только NSURLConnection получает данные … но поскольку вы немедленно выпускаете NSURLConnection, у него никогда не будет возможности запустить runloop, и поэтому вы даже не пытаетесь установить соединение.

4. О, кроме того, здесь ОГРОМНАЯ проблема… не уверен, почему я не видел этого раньше … ваш метод инициализации совершенно неверен. Вам нужно делать что-то вроде: if((self = [super init])) { … весь ваш код там … } внутри него.

5. Хорошо, я отредактировал свой код, чтобы включить предложенный вами оператор if. Кроме того, я попытался просто прокомментировать освобождение соединения (беспокоясь об утечке памяти, как только я увидел, что это сработало), и didReceiveData по-прежнему не вызывается. Есть мысли о том, почему этого не было бы?

Ответ №2:

Вы отправляете release сообщение соединению до того, как у него появился шанс запуститься; не делайте этого. Что касается JSON, строка, о которой вы сказали, была возвращена с сервера, является JSON. Вы ожидали чего-то другого?

Комментарии:

1. Хорошо, я прокомментировал выпуск (я буду беспокоиться об утечке памяти позже), но didReceiveData по-прежнему не вызывается.

Ответ №3:

Избавьтесь от строки:

 [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
  

Как только вы инициализируете соединение с запросом, оно запускается автоматически. У вас есть и другие проблемы. Ваша реализация протокола NSURLConnectionDelegate неверна. Вам нужно постепенно добавлять данные, полученные в connection:didReceiveResponse , в объект NSMutableData. Вы можете преобразовать его в JSON в `connectionDidFinishLoading:’.

Что касается утечки памяти, разорвите соединение в connectionDidFinishLoading: или connection:didFailWithError: вы гарантированно получите одно, но не оба из них.

Все, что вам нужно знать, находится здесь

[ПРАВКА: добавлен пример кода NSURLConnection]

 // Attach this to the touchUpInside event of a UIButton
// Note that all objects will be autoreleased
// Note that you can comment out any or all of the NSURLConnection delegate methods
// and the request will execute

- (IBAction)initiateRequest
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL urlWithString:@"http://www.google.com"]];
    NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
}

- (void)connection:(NSURLConnection *)connection didRecieveData:(NSData *)date
{
    NSLog(@"connection:didReceiveData");
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"connection:didReceiveResponse:");
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"connectionDidFinishLoading:");
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"connection:didFailWithError:");
}
  

Комментарии:

1. @XJones , эта строка подключения изначально не была у меня в коде, но была предложена другим пользователем. Я включил это, не сработало, поэтому я убрал это, я отредактирую приведенный выше код. Кроме того, реализации didReceiveData и didReceiveResponse являются неполными, поскольку я ожидал, что любой из них действительно будет вызван, прежде чем я попытался что-либо сделать с данными.

2. Это нормально. didReceiveData Вызывается ли сейчас? Если это так, то данные, которые вы получаете, являются лишь частичным фрагментом и, вероятно, не будут допустимой строкой JSON. Вы также должны получать одно из didFinishLoading или didFailWithError . Вы должны добавить их, чтобы помочь в отладке, возможно, это сбой с ошибкой, и вы еще не знаете об этом.

3. Это не срабатывало до того, как у меня появилась эта строка кода, вот почему было предложено включить ее. В любом случае, я проверил в последний раз, и это все еще не сработало, но затем я включил два предложенных вами метода, и теперь didReceiveData и didReceiveResponse оба работают. Очевидно, требуются все методы?

4. Ни один из методов не требуется NSURLConnection . Я проверил это сам. Кроме того, если / когда ваша проблема будет решена, было бы уместно отдать должное людям, которые помогли вам, мне или другим. Особенно, если вам нужна дальнейшая помощь от людей из SO.

5. Очевидно, ко мне применимы другие законы программирования. В прошлом у меня были программы, которые не компилировались из-за имени переменной (rockPile), код, который был закомментирован, приводил к сбою моей программы, а теперь это. Я очень благодарен всем тем, кто внес предложения, дал совет и т.д., И я более чем готов отдать должное. Проблема в том, что ни один из приведенных ответов технически не заставил мою программу работать, кроме добавления этих двух методов, так что какой из них я должен пометить как правильный «ответ»? Также у меня недостаточно репутации, чтобы проголосовать за любой из ответов. Я открыт для любых предложений о том, как должным образом оценить.

Ответ №4:

Спасибо XJones за предложение. Я не включал два метода didFinishLoading or didFailWithError , как только они были вставлены в мой код, оба didReceiveResponse и didReceiveData оба начали срабатывать. Спасибо вам всем за советы и рекомендации, и, надеюсь, это поможет кому-нибудь еще в будущем.

Комментарии:

1. Ни один из NSURLConnection методов делегирования не требуется. Вам нужно правильно обработать connection:didFinishLoading и connection:didFailWithError: очистить, но NSURLConnection выполнит запрос независимо. Я лично протестировал это, чтобы убедиться. Я также предлагаю вам отдать должное (путем голосования и принятия) людям из SO, мне или другим, которые потратили время, помогая вам.

2. Хм, не уверен, почему тогда мой не работал бы без этих двух методов. Просто еще одно в длинной череде программных препятствий, которые, кажется, применимы только ко мне. Кроме того, я совершенно согласен отдать должное всем, я просто не знал, какой из них пометить как «ответ», поскольку включение этих двух методов было единственным, что сработало. Также у меня недостаточно «репутации», чтобы проголосовать за любой из предоставленных ответов. 🙁