如何从使用NSURLConnection的类及其委托类返回一个对象?

时间:2011-01-05 19:42:48

标签: iphone objective-c nsurlconnection

我正在尝试将代码从UITableViewController类移动到“helper”类。

代码利用NSURLConnection来获取和解析JSON,然后填充NSMutableArray。

我想要做的是在我的助手类中调用一个返回NSMutableArray的方法。我不明白的是如何从NSURLConnection的connectionDidFinishLoading委托类(实际构建数组)返回数组,就像它来自最初调用的启动连接的方法一样。换句话说,调用NSURLConnection的方法如何获得控制权以便它可以从整个操作中返回一个值?

以下是助手类的相关方法。如何让getMovies方法返回在connectionDidFinishLoading委托类中构建的listOfMovies?

-(NSMutableArray)getMovies:(NSURL*)url {

responseData = [[NSMutableData data] retain];       

NSURLRequest *request = [NSURLRequest requestWithURL:url];
//NSURLRequest* request = [NSURLRequest requestWithURL: url cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval: 30.0];

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

}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

[responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
//TODO error handling for connection
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {      

//---initialize the array--- 

listOfMovies = [[NSMutableArray alloc] init];

tmdbMovies = [[NSArray alloc] init];
posters = [[NSArray alloc] init];
thumbs = [[NSDictionary alloc] init];

NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

SBJsonParser *json = [[SBJsonParser new] autorelease];

tmdbMovies = [json objectWithString:responseString];

// loop through all the top level elements in JSON
for (id movie in tmdbMovies) {

    // 0 - Name
    // 1 - Meta
    // 2 - Url


    if ((NSNull *)[movie objectForKey:@"name"] != [NSNull null]) {

        if (![[movie objectForKey:@"name"] isEqualToString:@""]) {
            name = [movie objectForKey:@"name"];
        }

    } 

    if ((NSNull *)[movie objectForKey:@"info"] != [NSNull null]) {

        if (![[movie objectForKey:@"info"] isEqualToString:@""]) {
            meta = [movie objectForKey:@"info"];
        }

    } 

    if ((NSNull *)[movie objectForKey:@"thumb"] != [NSNull null]) {

        if (![[movie objectForKey:@"thumb"] isEqualToString:@""]) {
            thumbUrl = [movie objectForKey:@"thumb"];
        }

    } 

    NSLog(@"Name: %@", name);
    NSLog(@"Info: %@", meta);
    NSLog(@"Thumb: %@", thumbUrl);

    NSMutableArray *movieData = [[NSMutableArray alloc] initWithObjects:name,meta,thumbUrl,nil];

    // add movieData array to listOfJMovies array
    [listOfMovies addObject:movieData];


    [movieData release];
}


//FIXME: Connection warning
if (connection!=nil) { 
    [connection release]; 
}

[responseData release]; 
[responseString release];



}

4 个答案:

答案 0 :(得分:13)

您真正需要做的是创建一个@protocol,为您的助手类创建一个委托。然后将-(NSMutableArray)getMovies:(NSURL*)url更改为-(void)getMovies:(NSURL*)url

调用helper方法的类需要实现helper方法的委托。

然后- (void)connectionDidFinishLoading:(NSURLConnection *)connection调用委托方法。最好有一个成功,一个成功。

=更新Begin =

您还需要在助手文件中定义id delegate,调用类在初始化之后但在调用-(void)getMovies:(NSURL*)url之前设置为self。这样助手文件知道回调的位置。

getMovies *movieListCall = [[getMovies alloc] init];
movieListCall.delegate = self;
[movieListCall getMovies:<your NSURL goes here>];

您将在getMovies.h和getMovies.m文件中看到一些其他行以包含delegate

=更新结束=

在你的getMovies.h文件中添加:

@protocol getMoviesDelegate

@required
- (void)getMoviesSucceeded:(NSMutableArray *)movieArray;
- (void)getMoviesFailed:(NSString *)failedMessage;

@end

@interface getMovies : NSOBject {
id delegate;
}

@property (nonatomic, assign) id delegate;
在你的getMovies.m文件中添加:

@synthesize delegate;

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    //TODO error handling for connection
    if ([delegate respondsToSelector:@selector(getMoviesFailed:)]) {
        [delegate getMoviesFailed:[error localizedDescription]];
    }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {      

//finishes with
    if ([delegate respondsToSelector:@selector(getMoviesSucceeded:)]) {
        [delegate getMoviesSucceeded:listOfMovies];
    }
}

更新您的调用类.h文件以使用getMoviesDelegate

@interface MoviesView : UIViewController <getMoviesDelegate>{
.
.
.
}

getMoviesDelegate方法添加到您的调用类.m文件

- (void)getMoviesSucceeded:(NSMutableArray *)movieArray {
//deal with movieArray here
}

- (void)getMoviesFailed:(NSString *)failedMessage {
//deal with failure here
}

这未经过测试,但希望能为您提供合作路线图。

协议很好,因为你可以同时制作必需和可选的委托方法,它有助于改进你的帮助方法,使其在各个项目中变得非常可重用。如果您已实现协议但未实现协议所需的委托方法,编译器也会发出警告。如果您遵循此路径,请务必使用conformsToProtocol:respondsToSelector:

答案 1 :(得分:7)

从根本上说,正在发生的事情是你正在启动一个异步网络负载(异步是正确的方法,几乎​​可以肯定),然后你需要一些方法来恢复你在加载开始之前做的任何操作。您有几个选择:

  • 创建您自己的委托协议。然后,您的UITableViewController将自己设置为帮助程序的委托,并且帮助程序将调用helperDidLoad或您为该方法命名的任何内容。有关在Cocoa编程指南中编写代理的更多信息。

  • 使用块和继续传递样式。这有点先进,但我喜欢它。在你的UITableViewController中你会写这样的东西:

     [helper doSomething:^ (id loaded) { 
         [modelObject refresh:loaded]; // or whatever you need to do
     }];
    

    然后在你的助手中写下:

     - (void)doSomething:(void ^ (id))continuation {
         _continuation = continuation;
         //kick off network load
     }
    
    
     - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
         _continuation(_data);
     }
    
  • 使用通知。阅读NSNotificationCenter文档。
  • 使用KVO 。 KVO编程指南有很多关于键值观察的好信息。

答案 2 :(得分:0)

  

如何让getMovies方法返回在connectionDidFinishLoading委托类中构建的listOfMovies?

我会争辩说你不应该这样做。

网络请求应异步进行。如果您的getMovies要发出同步请求并且仅在有数据时返回,则在等待网络连接完成时会阻止整个线程。如果主线程正在调用getMovies,这通常是一个坏主意,也是一个糟糕的主意。阻止主线程将阻止您响应触摸或更新UI,您的应用程序将显示为冻结,如果您的用户不首先沮丧地退出,操作系统将终止它。

而是让辅助类通过委托回调,通知,KVO或您喜欢的任何机制在数据可用时(或无法检索数据时)通知调用者。

答案 3 :(得分:0)

以下是步骤,类似样式的伪代码:

  1. [helperInstance setDelegate:self]; // where self is your UITableViewController class

  2. 在您的助手类中,在connectionDidFinishLoading中执行以下操作:

    [delegate finishedLoadingData:JSONData];

  3. 您也可以为您的委托定义协议,并在您的帮助程序类中声明这样的委托:

    @property (nonatomic, assign) id<YourProtocol> delegate;
    

    希望这有帮助, Moszi