objective-c中block as function argument的执行顺序是什么?
What's the executive order of block as function argument in objective-c?
正如苹果文档所说,
"Blocks are also used for callbacks, defining the code to be executed when a task completes."
所以块应该在传递块的函数体执行后执行。但是我写了下面的测试代码:
void testBlock(void(^test)()){
NSLog(@"1");
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
testBlock(^{
NSLog(@"2");
});
}
}
输出只有“1”。
那么 NSLog(@"2")
在哪里?
@Julian Król
但是看看AFNetworking中的这个函数:
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^{
if (self.completionGroup) {
dispatch_group_enter(self.completionGroup);
}
dispatch_async(http_request_operation_processing_queue(), ^{
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
id responseObject = self.responseObject;
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
success(self, responseObject);
});
}
}
}
if (self.completionGroup) {
dispatch_group_leave(self.completionGroup);
}
});
};
#pragma clang diagnostic pop
}
这个函数没有显式调用块,而且块参数甚至没有名称,所以似乎不应该执行块。但是当我按如下方式使用此功能时:
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFXMLParserResponseSerializer serializer];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/rss+xml"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSXMLParser *XMLParser = (NSXMLParser *)responseObject;
[self.parserDictionary setObject:XMLParser forKey:urlString];
[XMLParser setShouldProcessNamespaces:YES];
XMLParser.delegate = self;
[XMLParser parse];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//When an error occurs while parsing.
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Loading Data"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];
[MBProgressHUD hideHUDForView:self.tableView animated:YES];
}];
并且块被真正执行。这是什么原因?
你没有在内部调用传递的块,这就是为什么输出只有 NSLog(@"2");
你应该有这样的东西:
void testBlock(void(^test)()){
NSLog(@"1");
test();
};
您还应该检查传递的块是否为 nil(因为如果是 nil 则调用它会崩溃)
您可以将块视为可以像变量一样传递的代码块。你像函数一样调用块,所以在你的情况下你会做类似 !test ?: test();
这在语义上等同于:
if (test != nil) {
test();
}
您可能会发现以下示例很有用:
// Define block that returns void and takes void arguments
typedef void (^MyBlock)(void);
// Define block that takes two ints and returns an int
typedef int (^AddBlock)(int a, int b);
@interface MyClass : NSObject
// NOTE: You have to COPY the block variables.
@property (copy) MyBlock blockOne;
@property (copy) AddBlock blockTwo;
@end
@implementation ...
- (void)runBlocks
{
!self.blockOne ?: self.blockOne();
if (self.blockTwo != nil) {
int sum = self.blockTwo(1, 2);
}
}
@end
function in AFNetworking:
几件事:
1) 你不应该有失败和成功块。而是像这样定义一个块:
typdef void (^CompletionHandler)(AFHTTPRequestOperation *op, id responseObj, NSError *error);
然后当您实现该块时,您可以像这样检查错误:
...^(AFHTTPRequestOperation *op, id responseObj, NSError *error) {
if (error != nil) {
// Handle the error
} else {
// YAY everything went well
}
}];
2) 你在这里忽略了循环保留,这不是一件好事。相反,您应该定义一个 self
的弱版本,您可以像这样在块中引用它:
__weak __block MyClass *welf = self;
myBlock = ^{
welf.coolProperty = coolValue;
};
查看 this SO question 了解有关 __weak
和 __block
的信息
正如苹果文档所说,
"Blocks are also used for callbacks, defining the code to be executed when a task completes."
所以块应该在传递块的函数体执行后执行。但是我写了下面的测试代码:
void testBlock(void(^test)()){
NSLog(@"1");
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
testBlock(^{
NSLog(@"2");
});
}
}
输出只有“1”。
那么 NSLog(@"2")
在哪里?
@Julian Król 但是看看AFNetworking中的这个函数:
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^{
if (self.completionGroup) {
dispatch_group_enter(self.completionGroup);
}
dispatch_async(http_request_operation_processing_queue(), ^{
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
id responseObject = self.responseObject;
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
success(self, responseObject);
});
}
}
}
if (self.completionGroup) {
dispatch_group_leave(self.completionGroup);
}
});
};
#pragma clang diagnostic pop
}
这个函数没有显式调用块,而且块参数甚至没有名称,所以似乎不应该执行块。但是当我按如下方式使用此功能时:
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFXMLParserResponseSerializer serializer];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/rss+xml"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSXMLParser *XMLParser = (NSXMLParser *)responseObject;
[self.parserDictionary setObject:XMLParser forKey:urlString];
[XMLParser setShouldProcessNamespaces:YES];
XMLParser.delegate = self;
[XMLParser parse];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//When an error occurs while parsing.
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Loading Data"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];
[MBProgressHUD hideHUDForView:self.tableView animated:YES];
}];
并且块被真正执行。这是什么原因?
你没有在内部调用传递的块,这就是为什么输出只有 NSLog(@"2");
你应该有这样的东西:
void testBlock(void(^test)()){
NSLog(@"1");
test();
};
您还应该检查传递的块是否为 nil(因为如果是 nil 则调用它会崩溃)
您可以将块视为可以像变量一样传递的代码块。你像函数一样调用块,所以在你的情况下你会做类似 !test ?: test();
这在语义上等同于:
if (test != nil) {
test();
}
您可能会发现以下示例很有用:
// Define block that returns void and takes void arguments
typedef void (^MyBlock)(void);
// Define block that takes two ints and returns an int
typedef int (^AddBlock)(int a, int b);
@interface MyClass : NSObject
// NOTE: You have to COPY the block variables.
@property (copy) MyBlock blockOne;
@property (copy) AddBlock blockTwo;
@end
@implementation ...
- (void)runBlocks
{
!self.blockOne ?: self.blockOne();
if (self.blockTwo != nil) {
int sum = self.blockTwo(1, 2);
}
}
@end
function in AFNetworking:
几件事:
1) 你不应该有失败和成功块。而是像这样定义一个块:
typdef void (^CompletionHandler)(AFHTTPRequestOperation *op, id responseObj, NSError *error);
然后当您实现该块时,您可以像这样检查错误:
...^(AFHTTPRequestOperation *op, id responseObj, NSError *error) {
if (error != nil) {
// Handle the error
} else {
// YAY everything went well
}
}];
2) 你在这里忽略了循环保留,这不是一件好事。相反,您应该定义一个 self
的弱版本,您可以像这样在块中引用它:
__weak __block MyClass *welf = self;
myBlock = ^{
welf.coolProperty = coolValue;
};
查看 this SO question 了解有关 __weak
和 __block