Как я могу передать Block
a Function
/ Method
?
Я безуспешно пытался - (void)someFunc:(__Block)someBlock
.
т.е. Что такое тип для Block
?
Как я могу передать Block
a Function
/ Method
?
Я безуспешно пытался - (void)someFunc:(__Block)someBlock
.
т.е. Что такое тип для Block
?
Ответы:
Тип блока зависит от его аргументов и типа возвращаемого значения. В общем случае, типы блоков объявляются так же , как типы указателей функции являются, но заменяя *
с ^
. Один из способов передать блок в метод следующий:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Но, как видите, это грязно. Вместо этого вы можете использовать a, typedef
чтобы сделать типы блоков более чистыми:
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));
файл 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 при необходимости и вызовите его:
- (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);
Определите a @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
передайте ему предопределенный тип переменной (не забудьте проверить, 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 {
}