Jak mogę przejść Block
do Function
/ Method
?
Próbowałem - (void)someFunc:(__Block)someBlock
bez skutku.
to znaczy. Jaki jest typ dla Block
?
Jak mogę przejść Block
do Function
/ Method
?
Próbowałem - (void)someFunc:(__Block)someBlock
bez skutku.
to znaczy. Jaki jest typ dla Block
?
Odpowiedzi:
Typ bloku różni się w zależności od jego argumentów i typu zwracanego. W ogólnym przypadku typy bloków są deklarowane w taki sam sposób, jak typy wskaźników do funkcji, ale zastępując *
znak ^
. Jeden ze sposobów przekazania bloku do metody jest następujący:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Ale jak widać, jest to niechlujne. Zamiast tego możesz użyć a, typedef
aby uczynić typy bloków czystszymi:
typedef void (^ IteratorBlock)(id, int);
Następnie przekaż ten blok do takiej metody:
- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
NSNumber *
lub std::string&
czy cokolwiek innego można przekazać jako argument funkcji. To tylko przykład. (Dla bloku, który jest równoważny, z wyjątkiem zastąpienia id
przez NSNumber
, typedef
byłoby typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
.)
NS_NOESCAPE
, ale enumerateObjectsUsingBlock
powiedziano mi, że nie są uciekające, ale nie widzę NS_NOESCAPE
nigdzie w witrynie ani w ogóle nie wspomniano o ucieczce w dokumentach Apple. Możesz pomóc?
Najłatwiejszym wyjaśnieniem tego pytania jest skorzystanie z tych szablonów:
1. Blok jako parametr metody
Szablon
- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
// your code
}
Przykład
-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
// your code
}
Inne zastosowania przypadków:
2. Blok jako właściwość
Szablon
@property (nonatomic, copy) returnType (^blockName)(parameters);
Przykład
@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);
3. Blok jako argument metody
Szablon
[anObject aMethodWithBlock: ^returnType (parameters) {
// your code
}];
Przykład
[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
// your code
}];
4. Blokuj jako zmienną lokalną
Szablon
returnType (^blockName)(parameters) = ^returnType(parameters) {
// your code
};
Przykład
void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
// your code
};
5. Block as a typedef
Szablon
typedef returnType (^typeName)(parameters);
typeName blockName = ^(parameters) {
// your code
}
Przykład
typedef void(^completionBlock)(NSArray *array, NSError *error);
completionBlock didComplete = ^(NSArray *array, NSError *error){
// your code
};
Możesz to zrobić, przekazując blok jako parametr bloku:
//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);
Jeszcze jeden sposób na przekazanie bloku przy użyciu funkcji с w przykładzie poniżej. Stworzyłem funkcje, które wykonują wszystko w tle i w głównej kolejce.
plik blocks.h
void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));
plik 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);
}
Następnie zaimportuj bloki.h w razie potrzeby i wywołaj go:
- (void)loadInBackground {
performInBackground(^{
NSLog(@"Loading something in background");
//loading code
performOnMainQueue(^{
//completion hadler code on main queue
});
});
}
Możesz również ustawić blok jako prostą właściwość, jeśli ma to zastosowanie w Twoim przypadku:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
upewnij się, że właściwość bloku to „kopia”!
i oczywiście możesz też użyć typedef:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
Również wywołujesz lub wywołujesz blok używając zwykłej składni funkcji c
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Więcej informacji o blokach tutaj
Zawsze zapominam o składni bloków. To zawsze przychodzi mi na myśl, gdy muszę zadeklarować blok. Mam nadzieję, że komuś to pomoże :)
Napisałem completeBlock dla klasy, która zwróci wartości kostek po ich wstrząśnięciu:
Zdefiniuj typedef z returnType ( .h
powyżej @interface
deklaracji)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Zdefiniuj a @property
dla bloku ( .h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Zdefiniuj metodę za pomocą finishBlock
( .h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Wkładka poprzedniej metody zdefiniowanej w .m
pliku i zobowiązać się finishBlock
do @property
zdefiniowanych wcześniej
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Aby wyzwolić, completionBlock
podaj do niego predefiniowany typ zmiennej (nie zapomnij sprawdzić, czy completionBlock
istnieje)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
Pomimo odpowiedzi udzielonych w tym wątku, naprawdę starałem się napisać funkcję, która przyjmowałaby blok jako funkcję - i to z parametrem. Ostatecznie oto rozwiązanie, które wymyśliłem.
Chciałem napisać funkcję ogólną loadJSONthread
, która pobierałaby adres URL usługi internetowej JSON, ładowała niektóre dane JSON z tego adresu URL w wątku w tle, a następnie zwracała wyniki NSArray * z powrotem do funkcji wywołującej.
Zasadniczo chciałem ukryć całą złożoność wątku tła w ogólnej funkcji wielokrotnego użytku.
Oto jak nazwałbym tę funkcję:
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)
}
} ];
... i to jest bit, z którym się zmagałem: jak to zadeklarować i jak zmusić go do wywołania funkcji Block po załadowaniu danych i przekazania Block
NSArray * załadowanych rekordów:
+(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);
});
});
}
To pytanie StackOverflow dotyczy tego, jak wywoływać funkcje, przekazując Block jako parametr, więc uprościłem powyższy kod i nie uwzględniłem loadJSONDataFromURL
funkcji.
Ale jeśli jesteś zainteresowany, możesz znaleźć kopię tej funkcji ładowania JSON na tym blogu: http://mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm
Mam nadzieję, że pomoże to innym programistom XCode! (Nie zapomnij zagłosować na to pytanie i moją odpowiedź, jeśli tak!)
Wygląda jak pełny szablon
- (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 {
}