Objective C: 替换一个同步请求
Objective C: replacing a synchronous request
我在替换此代码时遇到问题。
-(NSMutableArray *) GetPrices: {
NSError *error;
NSURLResponse *response;
NSData *tw_result = [NSURLConnection
sendSynchronousRequest:urlRequest
returningResponse:&response error:&error];
我遇到的问题是调用此代码的函数处理 url 然后 returns 数据到调用它的方法。
以前我是这样用的
ViewController 通过创建操作队列调用一个函数来收集数据(以便 UI 和主线程可用)
NSOperationQueue *myQueue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(loadDataWithOperation) object:nil];
[myQueue addOperation:operation];
[operation release];
[myQueue release];
操作队列中的函数调用方法来获取对象的数据,然后该方法运行同步 URL 请求。
-(void)loadDataWithOperation {
self.sectionPriceArray = [self.myObject GetPrices];
所以 myObject 会 return 一个价格数组。
我尝试过使用 NSSession,但我不知道如何将结果传回,因为该方法在从完成处理程序获取 tw_result 之前终止。
任何想法我都必须在 Objective C 中执行此操作,因为我没有得到客户的许可以转换为 swift。
编辑问题并提供更多详细信息:
在我尝试过的 GetPrices 方法中
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:urlRequest
completionHandler:^(NSData *tw_result,
NSURLResponse *response,
NSError *error) {
result = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];
NSArray *resultArray = [result componentsSeparatedByString:@"\n"];
}) resume];
但我不知道如何在调用级别上使这项工作更上一层楼。
正如@maddy 提到的,您将要为 getPrices 方法使用完成块而不是 return——returns + async 不要混用。
这是将您的 getPrices 方法转换为的一般形式:
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPriceArray))priceCompletion;
此站点:http://goshdarnblocksyntax.com 有一些常见的块语法声明用法。
通常您会调用此异步方法,然后在完成块中设置 iVar 并在收到新数据后重新加载关联的 UI 元素。沿着这些线的东西:
[self _getPricesWithCompletion:^(NSMutableArray *sectionPriceArray) {
self.sectionPriceArray = sectionPriceArray;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// reload any UI elements dependent on your sectionPriceArray here
}];
}];
现在在您显示的示例代码中,您似乎正在使用 NSOperationQueue 来排队不同的操作。这里的事情可能会变得有点复杂。随后的队列操作不会等待您的异步操作完成后再执行。因此,例如,如果您在 getPrices
操作之后有一个操作利用了获取价格的结果,那么此时 iVar 几乎肯定不会包含正确的数据。在这种情况下,您需要使用某种信号量来等待异步操作完成,然后再继续执行依赖于它的操作。
这是我的意思的一个例子:
NotProperlyWaiting.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NotProperlyWaiting : NSObject
@property (strong, nullable) NSMutableArray *sectionPriceArray;
- (void)callOperations;
- (void)fakeServerCallWithCompletion:(void(^)(NSData *tw_result, NSURLResponse *response, NSError *error))completion;
@end
NotProperlyWaiting.m
#import "NotProperlyWaiting.h"
@interface NotProperlyWaiting()
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPriceArray))priceCompletion;
- (void)_printPricesArray;
@end
@implementation NotProperlyWaiting
- (instancetype)init {
self = [super init];
if (self) {
_sectionPriceArray = [NSMutableArray array];
}
return self;
}
- (void)callOperations {
// setup our completion block to be passed in (this is what will eventually set the self.sectionPricesArray
void (^pricesCompletion)(NSMutableArray *) = ^ void (NSMutableArray *sectionPricesArrayFromCompletion){
self.sectionPriceArray = sectionPricesArrayFromCompletion;
};
NSOperationQueue *myQueue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(_getPricesWithCompletion:) object:pricesCompletion];
NSInvocationOperation *printOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(_printPricesArray) object:nil];
[myQueue addOperation:operation];
[myQueue addOperation:printOperation];
}
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion {
[self fakeServerCallWithCompletion:^(NSData *tw_result, NSURLResponse *response, NSError *error) {
// check the error or response or whatever else to verify that the data is legit from your server endpoint here
// then convert the data to your mutable array and pass it through to our completion block
NSString *stringData = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];
NSMutableArray *tempPricesArray = [NSMutableArray arrayWithArray:[stringData componentsSeparatedByString:@"\n"]];
// now our completion block passing in the result prices array
priceCompletion(tempPricesArray);
}];
}
- (void)_printPricesArray {
NSLog(@"NotWaiting -- Prices array : %@", self.sectionPriceArray);
}
// this is a fake version of NSURLSession
- (void)fakeServerCallWithCompletion:(void(^)(NSData *tw_result, NSURLResponse *response, NSError *error))completion {
NSString *fakeServerResponse = @"FirstThing\nSecondThing\nThirdThing";
NSData *fakeData = [fakeServerResponse dataUsingEncoding:NSUTF8StringEncoding];
NSURLResponse *fakeResponse = [[NSURLResponse alloc] init];
NSError *fakeError = [NSError errorWithDomain:@"FakeErrorDomain" code:33 userInfo:nil];
// never call sleep in your own code, this is just to simulate the wait time for the server to return data
sleep(3);
completion(fakeData,fakeResponse,fakeError);
}
NS_ASSUME_NONNULL_END
ProperlyWaiting.h(NotProperlyWaiting.h 的子class 重新使用 callOperation 和 fakeServerCallWithCompletion:)
#import "NotProperlyWaiting.h"
NS_ASSUME_NONNULL_BEGIN
@interface ProperlyWaiting : NotProperlyWaiting
@end
NS_ASSUME_NONNULL_END
ProperlyWaiting.m
#import "ProperlyWaiting.h"
@interface ProperlyWaiting()
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion;
- (void)_printPricesArray;
@property dispatch_semaphore_t semaphore;
@end
@implementation ProperlyWaiting
- (void)callOperations {
self.semaphore = dispatch_semaphore_create(0);
[super callOperations];
}
// identical implementations to NotProperlyWaiting, but this time we'll use a semaphore to ensure the _printPricesArray waits for the async operation to complete before continuing
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion {
[self fakeServerCallWithCompletion:^(NSData *tw_result, NSURLResponse *response, NSError *error) {
// check the error or response or whatever else to verify that the data is legit from your server endpoint here
// then convert the data to your mutable array and pass it through to our completion block
NSString *stringData = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];
NSMutableArray *tempPricesArray = [NSMutableArray arrayWithArray:[stringData componentsSeparatedByString:@"\n"]];
// now our completion block passing in the result prices array
priceCompletion(tempPricesArray);
// signal our semaphore to let it know we're done
dispatch_semaphore_signal(self.semaphore);
}];
}
- (void)_printPricesArray {
// wait for the semaphore signal before continuing (so we know the async operation we're waiting on has completed)
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"Waiting -- Prices array : %@", self.sectionPriceArray);
}
@end
class 的示例调用如下:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NotProperlyWaiting *notWaiting = [[NotProperlyWaiting alloc] init];
[notWaiting callOperations];
ProperlyWaiting *waiting = [[ProperlyWaiting alloc] init];
[waiting callOperations];
}
日志中的输出将是:
NotWaiting -- Prices array : (
)
然后 3 秒后:
Waiting -- Prices array : (
FirstThing,
SecondThing,
ThirdThing
)
指向与此主题相关的有用文档的一些其他链接:
https://developer.apple.com/documentation/dispatch/1452955-dispatch_semaphore_create
我在替换此代码时遇到问题。
-(NSMutableArray *) GetPrices: {
NSError *error;
NSURLResponse *response;
NSData *tw_result = [NSURLConnection
sendSynchronousRequest:urlRequest
returningResponse:&response error:&error];
我遇到的问题是调用此代码的函数处理 url 然后 returns 数据到调用它的方法。
以前我是这样用的
ViewController 通过创建操作队列调用一个函数来收集数据(以便 UI 和主线程可用)
NSOperationQueue *myQueue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(loadDataWithOperation) object:nil];
[myQueue addOperation:operation];
[operation release];
[myQueue release];
操作队列中的函数调用方法来获取对象的数据,然后该方法运行同步 URL 请求。
-(void)loadDataWithOperation {
self.sectionPriceArray = [self.myObject GetPrices];
所以 myObject 会 return 一个价格数组。
我尝试过使用 NSSession,但我不知道如何将结果传回,因为该方法在从完成处理程序获取 tw_result 之前终止。
任何想法我都必须在 Objective C 中执行此操作,因为我没有得到客户的许可以转换为 swift。
编辑问题并提供更多详细信息:
在我尝试过的 GetPrices 方法中
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:urlRequest
completionHandler:^(NSData *tw_result,
NSURLResponse *response,
NSError *error) {
result = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];
NSArray *resultArray = [result componentsSeparatedByString:@"\n"];
}) resume];
但我不知道如何在调用级别上使这项工作更上一层楼。
正如@maddy 提到的,您将要为 getPrices 方法使用完成块而不是 return——returns + async 不要混用。
这是将您的 getPrices 方法转换为的一般形式:
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPriceArray))priceCompletion;
此站点:http://goshdarnblocksyntax.com 有一些常见的块语法声明用法。
通常您会调用此异步方法,然后在完成块中设置 iVar 并在收到新数据后重新加载关联的 UI 元素。沿着这些线的东西:
[self _getPricesWithCompletion:^(NSMutableArray *sectionPriceArray) {
self.sectionPriceArray = sectionPriceArray;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// reload any UI elements dependent on your sectionPriceArray here
}];
}];
现在在您显示的示例代码中,您似乎正在使用 NSOperationQueue 来排队不同的操作。这里的事情可能会变得有点复杂。随后的队列操作不会等待您的异步操作完成后再执行。因此,例如,如果您在 getPrices
操作之后有一个操作利用了获取价格的结果,那么此时 iVar 几乎肯定不会包含正确的数据。在这种情况下,您需要使用某种信号量来等待异步操作完成,然后再继续执行依赖于它的操作。
这是我的意思的一个例子:
NotProperlyWaiting.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NotProperlyWaiting : NSObject
@property (strong, nullable) NSMutableArray *sectionPriceArray;
- (void)callOperations;
- (void)fakeServerCallWithCompletion:(void(^)(NSData *tw_result, NSURLResponse *response, NSError *error))completion;
@end
NotProperlyWaiting.m
#import "NotProperlyWaiting.h"
@interface NotProperlyWaiting()
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPriceArray))priceCompletion;
- (void)_printPricesArray;
@end
@implementation NotProperlyWaiting
- (instancetype)init {
self = [super init];
if (self) {
_sectionPriceArray = [NSMutableArray array];
}
return self;
}
- (void)callOperations {
// setup our completion block to be passed in (this is what will eventually set the self.sectionPricesArray
void (^pricesCompletion)(NSMutableArray *) = ^ void (NSMutableArray *sectionPricesArrayFromCompletion){
self.sectionPriceArray = sectionPricesArrayFromCompletion;
};
NSOperationQueue *myQueue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(_getPricesWithCompletion:) object:pricesCompletion];
NSInvocationOperation *printOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(_printPricesArray) object:nil];
[myQueue addOperation:operation];
[myQueue addOperation:printOperation];
}
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion {
[self fakeServerCallWithCompletion:^(NSData *tw_result, NSURLResponse *response, NSError *error) {
// check the error or response or whatever else to verify that the data is legit from your server endpoint here
// then convert the data to your mutable array and pass it through to our completion block
NSString *stringData = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];
NSMutableArray *tempPricesArray = [NSMutableArray arrayWithArray:[stringData componentsSeparatedByString:@"\n"]];
// now our completion block passing in the result prices array
priceCompletion(tempPricesArray);
}];
}
- (void)_printPricesArray {
NSLog(@"NotWaiting -- Prices array : %@", self.sectionPriceArray);
}
// this is a fake version of NSURLSession
- (void)fakeServerCallWithCompletion:(void(^)(NSData *tw_result, NSURLResponse *response, NSError *error))completion {
NSString *fakeServerResponse = @"FirstThing\nSecondThing\nThirdThing";
NSData *fakeData = [fakeServerResponse dataUsingEncoding:NSUTF8StringEncoding];
NSURLResponse *fakeResponse = [[NSURLResponse alloc] init];
NSError *fakeError = [NSError errorWithDomain:@"FakeErrorDomain" code:33 userInfo:nil];
// never call sleep in your own code, this is just to simulate the wait time for the server to return data
sleep(3);
completion(fakeData,fakeResponse,fakeError);
}
NS_ASSUME_NONNULL_END
ProperlyWaiting.h(NotProperlyWaiting.h 的子class 重新使用 callOperation 和 fakeServerCallWithCompletion:)
#import "NotProperlyWaiting.h"
NS_ASSUME_NONNULL_BEGIN
@interface ProperlyWaiting : NotProperlyWaiting
@end
NS_ASSUME_NONNULL_END
ProperlyWaiting.m
#import "ProperlyWaiting.h"
@interface ProperlyWaiting()
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion;
- (void)_printPricesArray;
@property dispatch_semaphore_t semaphore;
@end
@implementation ProperlyWaiting
- (void)callOperations {
self.semaphore = dispatch_semaphore_create(0);
[super callOperations];
}
// identical implementations to NotProperlyWaiting, but this time we'll use a semaphore to ensure the _printPricesArray waits for the async operation to complete before continuing
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion {
[self fakeServerCallWithCompletion:^(NSData *tw_result, NSURLResponse *response, NSError *error) {
// check the error or response or whatever else to verify that the data is legit from your server endpoint here
// then convert the data to your mutable array and pass it through to our completion block
NSString *stringData = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];
NSMutableArray *tempPricesArray = [NSMutableArray arrayWithArray:[stringData componentsSeparatedByString:@"\n"]];
// now our completion block passing in the result prices array
priceCompletion(tempPricesArray);
// signal our semaphore to let it know we're done
dispatch_semaphore_signal(self.semaphore);
}];
}
- (void)_printPricesArray {
// wait for the semaphore signal before continuing (so we know the async operation we're waiting on has completed)
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"Waiting -- Prices array : %@", self.sectionPriceArray);
}
@end
class 的示例调用如下:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NotProperlyWaiting *notWaiting = [[NotProperlyWaiting alloc] init];
[notWaiting callOperations];
ProperlyWaiting *waiting = [[ProperlyWaiting alloc] init];
[waiting callOperations];
}
日志中的输出将是:
NotWaiting -- Prices array : (
)
然后 3 秒后:
Waiting -- Prices array : (
FirstThing,
SecondThing,
ThirdThing
)
指向与此主题相关的有用文档的一些其他链接:
https://developer.apple.com/documentation/dispatch/1452955-dispatch_semaphore_create