Làm thế nào tôi có thể vượt qua một Block
đến một Function
/ Method
?
Tôi đã cố gắng - (void)someFunc:(__Block)someBlock
mà không có kết quả.
I E. Là gì loại cho một Block
?
Làm thế nào tôi có thể vượt qua một Block
đến một Function
/ Method
?
Tôi đã cố gắng - (void)someFunc:(__Block)someBlock
mà không có kết quả.
I E. Là gì loại cho một Block
?
Câu trả lời:
Loại của một khối khác nhau tùy thuộc vào đối số và loại trả về của nó. Trong trường hợp chung, các loại khối được khai báo giống như các kiểu con trỏ hàm, nhưng thay thế *
bằng a ^
. Một cách để truyền một khối cho một phương thức như sau:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Nhưng như bạn có thể thấy, đó là mớ hỗn độn. Thay vào đó, bạn có thể sử dụng một typedef
để làm cho các loại khối sạch hơn:
typedef void (^ IteratorBlock)(id, int);
Và sau đó chuyển khối đó sang một phương thức như vậy:
- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
NSNumber *
hoặc std::string&
hoặc bất cứ điều gì khác mà bạn có thể vượt qua như một đối số chức năng. Đây chỉ là một ví dụ. (Đối với một khối tương đương ngoại trừ thay thế id
bằng NSNumber
, typedef
sẽ là typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
.)
NS_NOESCAPE
, nhưng enumerateObjectsUsingBlock
tôi được bảo là không thoát, nhưng tôi không thấy NS_NOESCAPE
bất cứ nơi nào trên trang web, cũng không thoát được đề cập đến trong các tài liệu của Apple. Bạn có thể giúp?
Giải thích đơn giản nhất cho câu hỏi này là theo các mẫu sau:
1. Chặn làm tham số phương thức
Bản mẫu
- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
// your code
}
Thí dụ
-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
// your code
}
Các trường hợp sử dụng khác:
2. Chặn như một tài sản
Bản mẫu
@property (nonatomic, copy) returnType (^blockName)(parameters);
Thí dụ
@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);
3. Chặn làm đối số phương thức
Bản mẫu
[anObject aMethodWithBlock: ^returnType (parameters) {
// your code
}];
Thí dụ
[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
// your code
}];
4. Chặn như một biến cục bộ
Bản mẫu
returnType (^blockName)(parameters) = ^returnType(parameters) {
// your code
};
Thí dụ
void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
// your code
};
5. Chặn như một typedef
Bản mẫu
typedef returnType (^typeName)(parameters);
typeName blockName = ^(parameters) {
// your code
}
Thí dụ
typedef void(^completionBlock)(NSArray *array, NSError *error);
completionBlock didComplete = ^(NSArray *array, NSError *error){
// your code
};
Bạn có thể làm như thế này, chuyển khối dưới dạng tham số khối:
//creating a block named "completion" that will take no arguments and will return void
void(^completion)() = ^() {
NSLog(@"bbb");
};
//creating a block namd "block" that will take a block as argument and will return void
void(^block)(void(^completion)()) = ^(void(^completion)()) {
NSLog(@"aaa");
completion();
};
//invoking block "block" with block "completion" as argument
block(completion);
Một cách nữa để vượt qua khối bằng cách sử dụng các chức năng trong ví dụ dưới đây. Tôi đã tạo các hàm để thực hiện bất cứ điều gì trong nền và trên hàng đợi chính.
tập tin blocks.h
void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));
tập tin blocks.m
#import "blocks.h"
void performInBackground(void(^block)(void)) {
if (nil == block) {
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), block);
}
void performOnMainQueue(void(^block)(void)) {
if (nil == block) {
return;
}
dispatch_async(dispatch_get_main_queue(), block);
}
Hơn nhập khối.h khi cần thiết và gọi nó:
- (void)loadInBackground {
performInBackground(^{
NSLog(@"Loading something in background");
//loading code
performOnMainQueue(^{
//completion hadler code on main queue
});
});
}
Bạn cũng có thể đặt khối làm tài sản đơn giản nếu nó phù hợp với bạn:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
đảm bảo rằng tài sản khối là "bản sao"!
và tất nhiên bạn cũng có thể sử dụng typedef:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
Ngoài ra, bạn gọi hoặc gọi một khối bằng cách sử dụng cú pháp hàm c thông thường
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Thêm thông tin về các khối ở đây
Tôi luôn có xu hướng quên về cú pháp khối. Điều này luôn xuất hiện trong đầu tôi khi tôi cần khai báo một khối. Tôi hi vọng nó giúp ích cho ai đó :)
Tôi đã viết một xongBlock cho một lớp sẽ trả về các giá trị của súc sắc sau khi chúng bị lắc:
Xác định typedef với returnType ( khai báo .h
ở trên @interface
)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Xác định a @property
cho khối ( .h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Xác định một phương thức với finishBlock
( .h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Chèn phương pháp được xác định trước đó trong .m
tập tin và cam kết finishBlock
để @property
được xác định trước
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Để kích hoạt completionBlock
vượt qua biếnType được xác định trước cho nó (Đừng quên kiểm tra xem có completionBlock
tồn tại không)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
Bất chấp các câu trả lời được đưa ra trên chuỗi này, tôi thực sự vật lộn để viết một hàm sẽ lấy Khối làm hàm - và với một tham số. Cuối cùng, đây là giải pháp tôi nghĩ ra.
Tôi muốn viết một hàm chung, loadJSONthread
sẽ lấy URL của Dịch vụ web JSON, tải một số dữ liệu JSON từ URL này trên một luồng nền, sau đó trả lại kết quả NSArray * cho hàm gọi.
Về cơ bản, tôi muốn giữ tất cả độ phức tạp của luồng nền trong một hàm có thể sử dụng lại chung chung.
Đây là cách tôi sẽ gọi chức năng này:
NSString* WebServiceURL = @"http://www.inorthwind.com/Service1.svc/getAllCustomers";
[JSONHelper loadJSONthread:WebServiceURL onLoadedData:^(NSArray *results) {
// Finished loading the JSON data
NSLog(@"Loaded %lu rows.", (unsigned long)results.count);
// Iterate through our array of Company records, and create/update the records in our SQLite database
for (NSDictionary *oneCompany in results)
{
// Do something with this Company record (eg store it in our SQLite database)
}
} ];
... và đây là bit mà tôi đã vật lộn với: cách khai báo và cách gọi hàm Block sau khi dữ liệu được tải và truyền Block
NSArray * của các bản ghi được tải:
+(void)loadJSONthread:(NSString*)urlString onLoadedData:(void (^)(NSArray*))onLoadedData
{
__block NSArray* results = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// Call an external function to load the JSON data
NSDictionary * dictionary = [JSONHelper loadJSONDataFromURL:urlString];
results = [dictionary objectForKey:@"Results"];
dispatch_async(dispatch_get_main_queue(), ^{
// This code gets run on the main thread when the JSON has loaded
onLoadedData(results);
});
});
}
Câu hỏi StackOverflow này liên quan đến cách gọi các hàm, truyền Khối như một tham số, vì vậy tôi đã đơn giản hóa mã ở trên và không bao gồm loadJSONDataFromURL
hàm.
Nhưng, nếu bạn quan tâm, bạn có thể tìm thấy một bản sao của chức năng tải JSON này trên blog này: http://mikeledgeledledase.azurewebsites.net/pages/Service/WebService-Page6.htm
Hy vọng điều này sẽ giúp một số nhà phát triển XCode khác! (Đừng quên bỏ phiếu cho câu hỏi này và câu trả lời của tôi, nếu có!)
Mẫu đầy đủ trông như
- (void) main {
//Call
[self someMethodWithSuccessBlock:^{[self successMethod];}
withFailureBlock:^(NSError * error) {[self failureMethod:error];}];
}
//Definition
- (void) someMethodWithSuccessBlock:(void (^) (void))successBlock
withFailureBlock:(void (^) (NSError*))failureBlock {
//Execute a block
successBlock();
// failureBlock([[NSError alloc]init]);
}
- (void) successMethod {
}
- (void) failureMethod:(NSError*) error {
}