如何返回objective- c (iPhone)中从web服务获得的数据?

时间:2022-09-06 20:51:48

This might be a dumb question. Sorry if it is.

这可能是个愚蠢的问题。对不起,如果是。

But Im working on a project that consumes web services. I can connect to the web service and get the data I need fine.

但是我正在做一个使用web服务的项目。我可以连接到web服务并获得我需要的数据。

I would like to have a method that returns this data obtained from the web service to the caller. The only problem is that the data is only obtained inside the ConnectionDidFinishLoading method, and I can't access this data from my method.

我希望有一个方法可以将从web服务获得的数据返回给调用者。唯一的问题是数据只能在ConnectionDidFinishLoading方法中获得,并且我不能从我的方法访问这些数据。

here is my code, that works fine:

这是我的代码,运行良好:

- (NSData *) dataForMethod:(NSString *)webMethod withPostString:(NSString *)postString
{
    NSURL *url = [NSURL URLWithString:[SigameWebServiceAddress stringByAppendingFormat:@"%@%@", @"/", webMethod]];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
    NSString *msgLength = [NSString stringWithFormat:@"%d", [postString length]];

    [req addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
    [req setHTTPMethod:@"POST"];
    [req setHTTPBody: [postString dataUsingEncoding:NSUTF8StringEncoding]];

    conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
    if (conn) 
    {
        webData = [NSMutableData data];
    }   

    // I WOULD LIKE TO RETURN WEBDATA TO THE CALLER HERE, BUT WEBDATA IS EMPTY NOW, THE  
    //connectionDidFinishLoading ONLY GETS CALLED WITH THE DATA I WANT AFTER THE COMPILER
    //IS DONE EXECUTING MY METHOD.
}

-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response 
{
    [webData setLength: 0];
}

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

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

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSLog(@"DONE. Received Bytes: %d", [webData length]);

    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    //---shows the XML---
    NSLog(@"%@", theXML);  //NOW, THIS IS THE DATA I WANT. BUT HOW CAN I RETURN THIS TO 
                           //THE CALLER. I MEAN, THE CALLER THAT CALLED MY METHOD 
                           //+ (NSData *) dataForMethod: withPostString:
}

Any help here is appreciated! Thanks

感谢您的帮助!谢谢

5 个解决方案

#1


11  

There are really two ways to go about this.

有两种方法可以解决这个问题。

  1. Create a delegate interface
  2. 创建一个委托接口
  3. Use Blocks
  4. 使用块

I would strongly advise against using the synchronous methods - unless you are/have created your own asynchronous framework around them (i.e. you are manually starting another thread and executing your synchronous request on that thread). In the long run you will realize you need the requests to be async, and you'll have to re-work everything such that they are.

我强烈建议不要使用同步方法——除非您已经围绕它们创建了自己的异步框架(例如,您正在手动启动另一个线程并在该线程上执行同步请求)。从长远来看,您将意识到您需要异步的请求,并且您将不得不重新处理所有这些请求。

To give a quick overview of the two options I gave:

为了快速概述我给出的两个选项:

1. Create a delegate interface

The idea here is to create a class which performs the request, and create a protocol the caller must implement. When the request is complete, you will invoke a specified method on the delegate with the data:

这里的想法是创建一个执行请求的类,并创建调用者必须实现的协议。当请求完成后,您将使用数据在委托上调用指定的方法:

The protocol might look something like this:

协议可能是这样的:

@protocol RequestClassDelegate <NSObject>

- (void)requestCompleted:(ResponseClass *)data;
- (void)requestError:(NSError *)error;

@end

The class which makes the request might look something like this:

发出请求的类可能是这样的:

@interface RequestClass : NSObject

- (void)makeRequest:(id<RequestClassDelegate>)delegate;

@end

And the request class implementation might contain some of the following, in addition to your connection logic:

除了连接逻辑之外,请求类实现可能包含以下内容:

@implementation RequestClass
{
    __weak id<RequestClassDelegate> _delegate;
}

// Connection Logic, etc.

- (void)makeRequest:(id<RequestClassDelegate>)delegate
{
    _delegate = delegate;
    // Initiate the request...
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    // Processing, etc.

    // Here we'll call the delegate with the result:
    [_delegate requestCompleted:theResult];
}

@end

2. Use Blocks

This solution is much the same as the first solution - but, a bit more elegant in my opinion. Here, we'll change the RequestClass to use blocks instead of a delegate:

这个解决方案与第一个解决方案非常相似——但是,在我看来,这个解决方案更优雅一些。在这里,我们将把RequestClass改为使用block而不是委托:

typedef void (^requestCompletedBlock)(id);
typedef void (^requestErrorBlock)(NSError *);
@interface RequestClass : NSObject

@property (nonatomic, copy) requestCompletedBlock completed;
@property (nonatomic, copy) requestErrorBlock errored;

- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error;

@end

And the implementation of that might look something like this:

它的实现大概是这样的:

@implementation RequestClass

@synthesize completed = _completed;
@synthesize errored = _errored;

// Connection Logic, etc.

- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error
{
    self.completed = completed;
    self.errored = error;
    // Initiate the request...
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    // Processing, etc.

    // Here we'll call the delegate with the result:
    self.completed(theResult);
}

@end

#2


2  

It sounds like you are trying to use return the data synchronously from your method, but you are using an asynchronous method (using an NSURLConnection and presumably calling its start method) to begin retrieving data. If you really want your method to return its result synchronously, read on. As @Steve says in another answer, however, you may also reconsider your interface design and instead implement it using an asynchronous approach and use his recommendations for either a delegate or block-based interface.

听起来好像您试图使用从方法中同步返回数据,但是您正在使用异步方法(使用NSURLConnection并可能调用它的start方法)开始检索数据。如果您真的希望您的方法以同步方式返回结果,请继续阅读。但是,正如@Steve在另一个回答中所说,您也可以重新考虑您的接口设计,而是使用异步方法实现它,并将他的建议用于委托或基于块的接口。

If you want to return the data synchronously from your method, use a synchronous request. So change this part of your code:

如果希望从方法中同步返回数据,请使用同步请求。所以改变这部分代码:

conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
[conn start]; // I presume you have this somewhere
if (conn) 
{
    webData = [NSMutableData data];
} 

with something more like this:

更像这样:

 NSURLResponse *response = nil;
 NSError *error = nil;
 webdata = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error];
 if (webdata) {
     return webdata;
 }
 else {
     // Handle error by looking at response and/or error values
     return nil;
 }

You will no longer need any of your delegate code if you use this approach. You will be limited in some ways though. For example, if your web service requires authentication via something other than URL parameters you can't use this approach.

如果使用这种方法,您将不再需要任何委托代码。但在某些方面你会受到限制。例如,如果web服务需要通过URL参数以外的其他方式进行身份验证,则不能使用这种方法。

#3


2  

Steve's answer is great and I can only suggest the way using blocks. Actually, as I am new into Objective-C I implemented the approach steve outlined. It works perfectly.

史蒂夫的回答很好,我只能建议如何使用积木。实际上,由于我是Objective-C的新手,我实现了steve所描述的方法。它的工作原理。

The Post for more details and my own point of view you can find here: http://kerkermeister.net/how-to-build-an-cocos2d-ios-app-communicating-with-a-restful-api-the-sequence/

关于更多细节和我的观点,你可以在这里找到:http://kerkermeister.net/how-to-build-an-cocos2d-ios-app- communication -with-a-restful-api- sequence/

The Post contains all the tiny steps you need to follow to get Steve's solution approach with blocks working. That includes: - an updateable view that will render information as soon as retrieved from Web API asynchronously - a controller invoking the HTTP request to the Web API - the actual HttpRequest class that uses iOS standard NSURLConnections - a model class that uses blocks as callbacks to update its data

这个帖子包含了你需要遵循的所有小步骤,以获得史蒂夫的解决方案。包括:可更新视图呈现信息尽快从异步Web API检索——控制器调用HTTP请求到Web API -使用iOS的实际HttpRequest类标准nsurlconnection——一个模型类,使用回调函数来更新其数据块

#4


0  

Your going to have to either implement a separate method in which you use the data once the data has been returned by the connectionDidFinishLoading method or make the request synchronously. The reason I believe the above does not work is because the request is happening on a separate thread, so the main thread continues, but does not actually have the data.

您将不得不实现一个单独的方法,在该方法中,当connectionDidFinishLoading方法返回数据时,您将使用该数据,或者同步地发出请求。我认为上面的方法不起作用的原因是因为请求是在一个单独的线程上发生的,所以主线程继续,但实际上没有数据。

This is a good way to do that if synchronous is what you want:

如果同步是你想要的,这是一个很好的方法:

Does all NSURLConnections connect asynchronously? iOs

所有NSURLConnections都是异步连接的吗?iOs

#5


-1  

Your going to need to parse the XML that comes back. There are some good Objective C XML parsers out there. One in particular is made for ease of use....

您需要解析返回的XML。有一些很好的Objective - C XML解析器。一个特别为易用性....

http://nfarina.com/post/2843708636/a-lightweight-xml-parser-for-ios

http://nfarina.com/post/2843708636/a-lightweight-xml-parser-for-ios

It's a very light weight parser for extracting the values you want from XML. I've used many times with great success and little hassle. Here is how I query a web address and turn it into data.

它是一个非常轻的解析器,用于从XML中提取所需的值。我已经用过很多次了,都很成功,也很少有麻烦。以下是我查询web地址并将其转换为数据的方法。

 NSString *query = [NSString stringWithFormat:@"http://WEB_ADDRESS_FOR_XML];
    NSURL *URL = [NSURL URLWithString:query];
    NSData *data = [NSData dataWithContentsOfURL:URL];

Or with NSURLConnection, in the did receive data:

或与NSURLConnection,在did接收数据:

-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data 
{
    //USE THE DATA RETURNED HERE....
}

Then use the Parser from my link to get the contents:

然后使用我链接中的解析器获取内容:

 SMXMLDocument *document = [SMXMLDocument documentWithData:data error:NULL];

    NSLog("\nXML Returned:%@",document);

#1


11  

There are really two ways to go about this.

有两种方法可以解决这个问题。

  1. Create a delegate interface
  2. 创建一个委托接口
  3. Use Blocks
  4. 使用块

I would strongly advise against using the synchronous methods - unless you are/have created your own asynchronous framework around them (i.e. you are manually starting another thread and executing your synchronous request on that thread). In the long run you will realize you need the requests to be async, and you'll have to re-work everything such that they are.

我强烈建议不要使用同步方法——除非您已经围绕它们创建了自己的异步框架(例如,您正在手动启动另一个线程并在该线程上执行同步请求)。从长远来看,您将意识到您需要异步的请求,并且您将不得不重新处理所有这些请求。

To give a quick overview of the two options I gave:

为了快速概述我给出的两个选项:

1. Create a delegate interface

The idea here is to create a class which performs the request, and create a protocol the caller must implement. When the request is complete, you will invoke a specified method on the delegate with the data:

这里的想法是创建一个执行请求的类,并创建调用者必须实现的协议。当请求完成后,您将使用数据在委托上调用指定的方法:

The protocol might look something like this:

协议可能是这样的:

@protocol RequestClassDelegate <NSObject>

- (void)requestCompleted:(ResponseClass *)data;
- (void)requestError:(NSError *)error;

@end

The class which makes the request might look something like this:

发出请求的类可能是这样的:

@interface RequestClass : NSObject

- (void)makeRequest:(id<RequestClassDelegate>)delegate;

@end

And the request class implementation might contain some of the following, in addition to your connection logic:

除了连接逻辑之外,请求类实现可能包含以下内容:

@implementation RequestClass
{
    __weak id<RequestClassDelegate> _delegate;
}

// Connection Logic, etc.

- (void)makeRequest:(id<RequestClassDelegate>)delegate
{
    _delegate = delegate;
    // Initiate the request...
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    // Processing, etc.

    // Here we'll call the delegate with the result:
    [_delegate requestCompleted:theResult];
}

@end

2. Use Blocks

This solution is much the same as the first solution - but, a bit more elegant in my opinion. Here, we'll change the RequestClass to use blocks instead of a delegate:

这个解决方案与第一个解决方案非常相似——但是,在我看来,这个解决方案更优雅一些。在这里,我们将把RequestClass改为使用block而不是委托:

typedef void (^requestCompletedBlock)(id);
typedef void (^requestErrorBlock)(NSError *);
@interface RequestClass : NSObject

@property (nonatomic, copy) requestCompletedBlock completed;
@property (nonatomic, copy) requestErrorBlock errored;

- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error;

@end

And the implementation of that might look something like this:

它的实现大概是这样的:

@implementation RequestClass

@synthesize completed = _completed;
@synthesize errored = _errored;

// Connection Logic, etc.

- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error
{
    self.completed = completed;
    self.errored = error;
    // Initiate the request...
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    // Processing, etc.

    // Here we'll call the delegate with the result:
    self.completed(theResult);
}

@end

#2


2  

It sounds like you are trying to use return the data synchronously from your method, but you are using an asynchronous method (using an NSURLConnection and presumably calling its start method) to begin retrieving data. If you really want your method to return its result synchronously, read on. As @Steve says in another answer, however, you may also reconsider your interface design and instead implement it using an asynchronous approach and use his recommendations for either a delegate or block-based interface.

听起来好像您试图使用从方法中同步返回数据,但是您正在使用异步方法(使用NSURLConnection并可能调用它的start方法)开始检索数据。如果您真的希望您的方法以同步方式返回结果,请继续阅读。但是,正如@Steve在另一个回答中所说,您也可以重新考虑您的接口设计,而是使用异步方法实现它,并将他的建议用于委托或基于块的接口。

If you want to return the data synchronously from your method, use a synchronous request. So change this part of your code:

如果希望从方法中同步返回数据,请使用同步请求。所以改变这部分代码:

conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
[conn start]; // I presume you have this somewhere
if (conn) 
{
    webData = [NSMutableData data];
} 

with something more like this:

更像这样:

 NSURLResponse *response = nil;
 NSError *error = nil;
 webdata = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error];
 if (webdata) {
     return webdata;
 }
 else {
     // Handle error by looking at response and/or error values
     return nil;
 }

You will no longer need any of your delegate code if you use this approach. You will be limited in some ways though. For example, if your web service requires authentication via something other than URL parameters you can't use this approach.

如果使用这种方法,您将不再需要任何委托代码。但在某些方面你会受到限制。例如,如果web服务需要通过URL参数以外的其他方式进行身份验证,则不能使用这种方法。

#3


2  

Steve's answer is great and I can only suggest the way using blocks. Actually, as I am new into Objective-C I implemented the approach steve outlined. It works perfectly.

史蒂夫的回答很好,我只能建议如何使用积木。实际上,由于我是Objective-C的新手,我实现了steve所描述的方法。它的工作原理。

The Post for more details and my own point of view you can find here: http://kerkermeister.net/how-to-build-an-cocos2d-ios-app-communicating-with-a-restful-api-the-sequence/

关于更多细节和我的观点,你可以在这里找到:http://kerkermeister.net/how-to-build-an-cocos2d-ios-app- communication -with-a-restful-api- sequence/

The Post contains all the tiny steps you need to follow to get Steve's solution approach with blocks working. That includes: - an updateable view that will render information as soon as retrieved from Web API asynchronously - a controller invoking the HTTP request to the Web API - the actual HttpRequest class that uses iOS standard NSURLConnections - a model class that uses blocks as callbacks to update its data

这个帖子包含了你需要遵循的所有小步骤,以获得史蒂夫的解决方案。包括:可更新视图呈现信息尽快从异步Web API检索——控制器调用HTTP请求到Web API -使用iOS的实际HttpRequest类标准nsurlconnection——一个模型类,使用回调函数来更新其数据块

#4


0  

Your going to have to either implement a separate method in which you use the data once the data has been returned by the connectionDidFinishLoading method or make the request synchronously. The reason I believe the above does not work is because the request is happening on a separate thread, so the main thread continues, but does not actually have the data.

您将不得不实现一个单独的方法,在该方法中,当connectionDidFinishLoading方法返回数据时,您将使用该数据,或者同步地发出请求。我认为上面的方法不起作用的原因是因为请求是在一个单独的线程上发生的,所以主线程继续,但实际上没有数据。

This is a good way to do that if synchronous is what you want:

如果同步是你想要的,这是一个很好的方法:

Does all NSURLConnections connect asynchronously? iOs

所有NSURLConnections都是异步连接的吗?iOs

#5


-1  

Your going to need to parse the XML that comes back. There are some good Objective C XML parsers out there. One in particular is made for ease of use....

您需要解析返回的XML。有一些很好的Objective - C XML解析器。一个特别为易用性....

http://nfarina.com/post/2843708636/a-lightweight-xml-parser-for-ios

http://nfarina.com/post/2843708636/a-lightweight-xml-parser-for-ios

It's a very light weight parser for extracting the values you want from XML. I've used many times with great success and little hassle. Here is how I query a web address and turn it into data.

它是一个非常轻的解析器,用于从XML中提取所需的值。我已经用过很多次了,都很成功,也很少有麻烦。以下是我查询web地址并将其转换为数据的方法。

 NSString *query = [NSString stringWithFormat:@"http://WEB_ADDRESS_FOR_XML];
    NSURL *URL = [NSURL URLWithString:query];
    NSData *data = [NSData dataWithContentsOfURL:URL];

Or with NSURLConnection, in the did receive data:

或与NSURLConnection,在did接收数据:

-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data 
{
    //USE THE DATA RETURNED HERE....
}

Then use the Parser from my link to get the contents:

然后使用我链接中的解析器获取内容:

 SMXMLDocument *document = [SMXMLDocument documentWithData:data error:NULL];

    NSLog("\nXML Returned:%@",document);