Як я можу передати Block
a Function
/ Method
?
Я намагався - (void)someFunc:(__Block)someBlock
безрезультатно.
тобто. Який тип для Block
?
Як я можу передати Block
a Function
/ Method
?
Я намагався - (void)someFunc:(__Block)someBlock
безрезультатно.
тобто. Який тип для Block
?
Відповіді:
Тип блоку змінюється залежно від його аргументів та типу повернення. У загальному випадку типи блоків оголошуються так само, як і типи функцій вказівників, але замінюючи на *
a ^
. Один із способів передачі блоку методу полягає в наступному:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Але, як бачите, це безладно. Ви можете замість цього використовувати typedef
тип a, щоб зробити типи блоків чистішими:
typedef void (^ IteratorBlock)(id, int);
А потім передайте цей блок такому методу:
- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
NSNumber *
або std::string&
або що-небудь інше, що ви могли б передавати як аргумент функції. Це лише приклад. (Для блоку, еквівалентного, за винятком заміни id
на NSNumber
, typedef
було б typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
.)
NS_NOESCAPE
, але, enumerateObjectsUsingBlock
як мені кажуть, не залишаються, але я не бачу NS_NOESCAPE
ніде на сайті, і не згадуюсь взагалі в документах Apple. Ви можете допомогти?
Найпростішим поясненням цього питання є наступні шаблони:
1. Блок як параметр методу
Шаблон
- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
// your code
}
Приклад
-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
// your code
}
Інші випадки використання:
2. Блок як властивість
Шаблон
@property (nonatomic, copy) returnType (^blockName)(parameters);
Приклад
@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);
3. Блок як аргумент методу
Шаблон
[anObject aMethodWithBlock: ^returnType (parameters) {
// your code
}];
Приклад
[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
// your code
}];
4. Блок як локальна змінна
Шаблон
returnType (^blockName)(parameters) = ^returnType(parameters) {
// your code
};
Приклад
void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
// your code
};
5. Блок як typedef
Шаблон
typedef returnType (^typeName)(parameters);
typeName blockName = ^(parameters) {
// your code
}
Приклад
typedef void(^completionBlock)(NSArray *array, NSError *error);
completionBlock didComplete = ^(NSArray *array, NSError *error){
// your code
};
Ви можете зробити так, передаючи блок як параметр блоку:
//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);
Ще один спосіб передачі блоку за допомогою функцій з прикладу нижче. Я створив функції, щоб виконувати що-небудь у фоновому режимі та на головній черзі.
файл blocks.h
void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));
файл block.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);
}
Тоді імпортуйте block.h при необхідності та викликайте:
- (void)loadInBackground {
performInBackground(^{
NSLog(@"Loading something in background");
//loading code
performOnMainQueue(^{
//completion hadler code on main queue
});
});
}
Ви також можете встановити блок як просту властивість, якщо це застосовно для вас:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
переконайтесь, що властивість блоку "копіювати"!
і звичайно, ви також можете використовувати typedef:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
Також ви викликаєте або викликаєте блок, використовуючи звичайний синтаксис функції c
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Більше інформації про блоки тут
Я завжди схильний забувати про синтаксис блоків. Це мені завжди спадає на думку, коли мені потрібно оголосити блок. Я сподіваюся, що це комусь допоможе :)
Я написав завершення блоку для класу, який поверне значення кісток після того, як вони будуть похитнуті:
Визначте typedef з returnType ( .h
вище @interface
декларації)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Визначте @property
блок для ( .h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Визначте метод за допомогою finishBlock
( .h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Вставте попередній визначений метод у .m
файл та перейдіть finishBlock
до @property
визначеного раніше
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Для запуску completionBlock
передачі до нього заздалегідь визначеної змінноїType (Не забудьте перевірити, чи completionBlock
існує)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
Незважаючи на відповіді, надані на цій темі, я дійсно намагався написати функцію, яка б прийняла блок як функцію - і з параметром. Врешті-решт, ось таке рішення я придумав.
Я хотів написати загальну функцію, loadJSONthread
яка брала б URL-адресу веб-служби JSON, завантажувала деякі дані JSON з цієї URL-адреси на фоновий потік, а потім повертала NSArray * результатів назад у викликову функцію.
В основному, я хотів утримати всю складність фонового потоку в загальній функції багаторазового використання.
Ось як я би назвав цю функцію:
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)
}
} ];
... і це біт, з яким я боровся: як оголосити це і як змусити його викликати функцію Block, коли дані завантажуються, і передавати Block
NSArray * завантажених записів:
+(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);
});
});
}
Це питання StackOverflow стосується того, як викликати функції, передаючи блок як параметр, тому я спростив код вище, і не включив loadJSONDataFromURL
функцію.
Але, якщо вас цікавить, ви можете знайти копію цієї функції завантаження JSON у цьому блозі: http://mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm
Сподіваюся, це допомагає деяким іншим розробникам XCode! (Не забувайте проголосувати це питання і мою відповідь, якщо це так!)
Повний шаблон виглядає так
- (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 {
}