简体   繁体   English

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

[英]How to return an object from a class that uses NSURLConnection and it's delegate classes?

I'm in the process of trying to move code from a UITableViewController class to a "helper" class. 我正在尝试将代码从UITableViewController类移动到“helper”类。

The code utilizes NSURLConnection to grab and parse JSON and then populate an NSMutableArray. 该代码利用NSURLConnection来获取和解析JSON,然后填充NSMutableArray。

What I'd like to do is call a method in my helper class that returns a NSMutableArray. 我想要做的是在我的助手类中调用一个返回NSMutableArray的方法。 What I don't understand is how to return the array from the connectionDidFinishLoading delegate class of NSURLConnection (where the array is actually built) as though it was from the originally called method that started the connection. 我不明白的是如何从NSURLConnection的connectionDidFinishLoading委托类(实际构建数组)返回数组,就像它来自最初调用的启动连接的方法一样。 In other words, how does the method that calls NSURLConnection get control back so it can return a value from the whole operation? 换句话说,调用NSURLConnection的方法如何获得控制权,以便它可以从整个操作中返回一个值?

Here are the relevant methods from the helper class. 以下是帮助程序类的相关方法。 How do I get the getMovies method to return the listOfMovies that is built in the connectionDidFinishLoading delegate class? 如何让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];



}

What you really need to do here is create a @protocol that creates a delegate for your helper class. 你真正需要做的是创建一个@protocol ,为你的助手类创建一个委托。 Then change -(NSMutableArray)getMovies:(NSURL*)url to -(void)getMovies:(NSURL*)url 然后改变-(NSMutableArray)getMovies:(NSURL*)url to -(void)getMovies:(NSURL*)url

The class that is calling your helper method needs to implement your helper method's delegate. 调用helper方法的类需要实现helper方法的委托。

Then - (void)connectionDidFinishLoading:(NSURLConnection *)connection calls the delegate method(s). 然后- (void)connectionDidFinishLoading:(NSURLConnection *)connection调用委托方法。 It's best to have a one for success and one for failure. 最好有一个成功,一个成功。

=Update Begin= =更新Begin =

You will need to also define an id delegate in your helper file which the calling class sets to self after init but before calling -(void)getMovies:(NSURL*)url . 您还需要在助手文件中定义一个id delegate ,调用类在init之后但在调用-(void)getMovies:(NSURL*)url之前设置为self。 That way the helper file knows where to call back to. 这样,帮助文件知道回调的位置。

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

You will see some additional lines for the inclusion of a delegate in both the getMovies.h and getMovies.m files. 您将在getMovies.h和getMovies.m文件中看到一些其他行以包含delegate

=Update End= =更新结束=

in your getMovies.h file add: 在你的getMovies.h文件中添加:

@protocol getMoviesDelegate

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

@end

@interface getMovies : NSOBject {
id delegate;
}

@property (nonatomic, assign) id delegate;

in your getMovies.m file add: 在你的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];
    }
}

update your calling class .h file to use getMoviesDelegate : 更新您的调用类.h文件以使用getMoviesDelegate

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

add the getMoviesDelegate methods to your calling class .m file getMoviesDelegate方法添加到您的调用类.m文件中

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

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

This is not tested but hopefully gives you a road map to work with. 这未经过测试,但希望能为您提供合作路线图。

Protocols are nice because you can make both required and optional delegate methods and it helps in refining your helper methods to become very reusable across projects. 协议很好,因为您可以制作必需和可选的委托方法,它有助于改进您的帮助程序方法,使其在各个项目中变得非常可重用。 The compiler will also warn you if you have implemented a protocol but not implemented the protocol's required delegate methods. 如果您已实现协议但未实现协议所需的委托方法,编译器也会发出警告。 If you follow this path be sure to use conformsToProtocol: and respondsToSelector: 如果您遵循此路径,请确保使用conformsToProtocol:respondsToSelector:

Fundamentally, what's happening is that you're starting an asynchronous network load (asynchronous is the right way to do this, almost assuredly), and then you need some way to resume whatever operation you were doing before the load began. 从根本上说,正在发生的是你正在启动异步网络负载(异步是正确的方法,几乎​​可以肯定),然后你需要一些方法来恢复在加载开始之前你正在做的任何操作。 You have a few options: 你有几个选择:

  • Create your own delegate protocol . 创建自己的委托协议 Your UITableViewController would then set itself as the helper's delegate, and the helper would call helperDidLoad or whatever you named that method. 然后,您的UITableViewController将自己设置为帮助程序的委托,并且帮助程序将调用helperDidLoad或您为该方法命名的任何内容。 There's more information on writing delegates in the Cocoa Programming Guide. 有关在Cocoa编程指南中编写代理的更多信息。

  • Use blocks and continuation passing style . 使用块和继续传递样式 This is a bit more advanced but I like it. 这有点先进,但我喜欢它。 In your UITableViewController you'd write something like this: 在你的UITableViewController中你会写这样的东西:

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

    And then in your helper you'd write: 然后在你的助手中写下:

      - (void)doSomething:(void ^ (id))continuation { _continuation = continuation; //kick off network load } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { _continuation(_data); } 
  • Use notifications . 使用通知 Read the NSNotificationCenter docs. 阅读NSNotificationCenter文档。
  • Use KVO . 使用KVO The KVO programming guide has a lot of good info on Key-Value Observing. KVO编程指南有很多关于键值观察的好信息。

How to i get the getMovies method to return the listOfMovies that is built in the connectionDidFinishLoading delegate class? 如何让getMovies方法返回在connectionDidFinishLoading委托类中构建的listOfMovies?

I'm going to argue that you should not do that. 我打算说你不应该这样做。

Network requests should be made asynchronously. 网络请求应异步进行。 If your getMovies were to make a synchronous request and return only when it had data you would block that entire thread while you waiting for a network connection to finish. 如果你的getMovies要发出同步请求并且仅在它有数据时返回,那么当你等待网络连接完成时,你会阻塞整个线程。 This is a bad idea in general and a terrible idea if your main thread is calling getMovies . 如果您的主线程正在调用getMovies这通常是一个坏主意并且是一个糟糕的主意。 Blocking the main thread will prevent you from responding to touches or updating the UI, your app will appear frozen, and the OS will terminate it if your users don't quit in frustration first. 阻止主线程将阻止您响应触摸或更新UI,您的应用程序将显示为冻结,如果您的用户不首先沮丧地退出,操作系统将终止它。

Instead have the helper class notify the caller when data is available (or when it failed to retrieve data) through a delegate call back, notification, KVO, or whatever mechanism you prefer. 而是让辅助类通过委托回调,通知,KVO或您喜欢的任何机制在数据可用时(或无法检索数据时)通知调用者。

Here are the steps, pseudocode like style: 以下是步骤,类似样式的伪代码:

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

  2. in your helper class, in the connectionDidFinishLoading do something like this: 在你的helper类中,在connectionDidFinishLoading中执行以下操作:

    [delegate finishedLoadingData:JSONData];

Also you can define a protocol for your delegate, and the declare the delegate like this in your helper class: 您还可以为您的委托定义协议,并在您的帮助程序类中声明这样的委托:

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

Hope this helps, Moszi 希望这有帮助,Moszi

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM