我有一个UITableViewController,称之为TableViewControllerA,这是我创建的另一个对象APICallerB的委托,用于与API通信。通过NSURLSessionDataTask,APICallerB正在设置其中一个属性,然后将其设置为等于TableViewControllerA的属性之一。
这是tableViewControllerA的viewDidLoad
方法:
- (void)viewDidLoad {
[super viewDidLoad];
// init instance of APICallerB
APICallerB *acb = [[APICallerB alloc] init];
// Set TableViewControllerA as delegate
tvcA.delegate = self;
[acb makeAPICallWithArgument:self.argument];
self.property1 = acb.property2;
}
我的问题是:等待[acb makeAPICallWithARgument:self.argument]
完成的最佳方法是什么,以便self.property1 = acb.property2
能够正常运行?我假设可以使用GCD(`dispatch_sync'?)但是,对于iOS / Objective-C的新手,我不知道在哪里使用它。或者最好将其中一个或两个项目移到其他地方?
以下是APICallerB的方法:
- (void)makeAPICallWithArgument:(NSString *)arg
{
NSString *requestString = [NSString stringWithFormat:@"http://%@:%@@apiurl.com/json/Request?arg=%@", API_USERNAME, API_KEY, arg];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
config.HTTPAdditionalHeaders = @{@"Accept" : @"application/json"};
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *ar = [jsonObject[@"Result"] objectForKey:@"results"];
self.property2 = ar;
}];
[dataTask resume];
}
答案 0 :(得分:2)
您正在调用异步方法,因此您应该使用异步模式。例如,完成块实现可能如下所示:
- (void)makeAPICallWithArgument:(NSString *)arg completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler
{
NSString *requestString = [NSString stringWithFormat:@"http://%@:%@@apiurl.com/json/Request?arg=%@", API_USERNAME, API_KEY, arg];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
config.HTTPAdditionalHeaders = @{@"Accept" : @"application/json"};
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (completionHandler) {
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil, error);
});
} else {
NSError *parseError;
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(jsonObject[@"Result"][@"results"], parseError);
});
}
}
}];
[dataTask resume];
}
你会这样称呼:
[acb makeAPICallWithArgument:self.argument completionHandler:^(NSArray *results, NSError *error){
// you'd probably first check to make sure you didn't have an error
self.property1 = results;
}];
一些小的意见:
我可能不会每次都创建一个新会话。如果您希望拨打多个电话,请将此会话保存以备将来使用。
您的makeAPICallWithArgument
正在更新您之后尝试检索的属性。我将退出该属性,并将值作为参数传递回完成块。
我在此添加了一些错误处理。
您的[jsonObject[@"Result"] objectForKey:@"results"]
看起来不正确。你真的有JSON,它返回一个带有“Result”键的字典,而在另一个带有“results”键的字典里面。如果是这样,很好,但这看起来很可疑。即使这是您的JSON格式,我也会将其简化为jsonObject[@"Result"][@"results"]
。
在其他地方,有人建议您考虑信号量。这几乎总是一个坏主意。这用于使异步方法同步运行。但你永远不想阻止主队列。
使用完成块模式消除了对信号量的需要。 Apple出于某种原因提供异步API,因此我们应该在使用它时采用异步模式。