Parametre olarak Objective-C geçiş bloğu


Yanıtlar:


257

Bir bloğun türü, bağımsız değişkenlerine ve dönüş türüne bağlı olarak değişir. Genel durumda, blok tipleri, işlev işaretçisi türleriyle aynı şekilde bildirilir, ancak yerine *a kullanılır ^. Bir bloğu bir yönteme geçirmenin bir yolu şöyledir:

- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;

Ama gördüğünüz gibi, bu dağınık. Bunun yerine typedef, blok türlerini daha temiz hale getirmek için a kullanabilirsiniz :

typedef void (^ IteratorBlock)(id, int);

Ve sonra bu bloğu şöyle bir yönteme geçirin:

- (void)iterateWidgets:(IteratorBlock)iteratorBlock;

Neden bir argüman olarak kimliği geçiyorsun? Örneğin bir NSNumber'ı kolayca geçmek mümkün değil mi? Nasıl görünüyor?
bas

7
Kesinlikle gibi özellikle yazılan argüman geçebilir NSNumber *ya std::string&veya başka bir şey, bir fonksiyon argüman olarak geçebileceği. Bu sadece bir örnek. (Değiştirilmesi hariç eşdeğer bir blok için idbirlikte NSNumber, typedefolurdu typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);.)
Jonathan Grynspan

Bu yöntem bildirimini gösterir. Bloklarla ilgili bir sorun, "dağınık" bildirim stilinin, gerçek yöntem çağrısını gerçek bir blok argümanı ile yazmayı net ve kolay yapmamasıdır.
uchuugaka

Typedefs, kodun yazılmasını kolaylaştırmakla kalmaz, aynı zamanda blok / fonksiyon işaretçisi sözdizimi en temiz olmadığı için okunmasını da önemli ölçüde kolaylaştırır.
pyj

@JonathanGrynspan, Swift dünyasından geliyor, ancak bazı eski Objective-C kodlarına dokunmak zorunda kalıyor, bir bloğun kaçtığını veya anlayamayacağını nasıl anlayabilirim? Ben varsayılan olarak, süslenmiş dışında bloklar kaçıyor okudum NS_NOESCAPE, ama enumerateObjectsUsingBlocksöylendi kaçan değil, ama ben NS_NOESCAPEsitede hiçbir yerde görmüyorum , ne de Apple belgelerinde hiç belirtilen kaçan. Yardım edebilir misin?
Mark A. Donohoe

62

Bu soru için en kolay açıklama şu şablonları takip etmektir:

1. Yöntem parametresi olarak engelle

şablon

- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
        // your code
}

Misal

-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
        // your code
}

Vakaların diğer kullanımı:

2. Mülk Olarak Engelle

şablon

@property (nonatomic, copy) returnType (^blockName)(parameters);

Misal

@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);

3. Yöntem bağımsız değişkeni olarak engelleyin

şablon

[anObject aMethodWithBlock: ^returnType (parameters) {
    // your code
}];

Misal

[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
    // your code
}];

4. Yerel değişken olarak engelle

şablon

returnType (^blockName)(parameters) = ^returnType(parameters) {
    // your code
};

Misal

void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
    // your code
};

5. typedef olarak engelleyin

şablon

typedef returnType (^typeName)(parameters);

typeName blockName = ^(parameters) {
    // your code
}

Misal

typedef void(^completionBlock)(NSArray *array, NSError *error);

completionBlock didComplete = ^(NSArray *array, NSError *error){
    // your code
};

1
[self saveWithCompletionBlock: ^ (NSArray * dizisi, NSError * hatası) {// kodunuz}]; Bu örnekte dönüş türü geçersiz olduğu için yok sayılır mı?
Alex

51

Bu yardımcı olabilir:

- (void)someFunc:(void(^)(void))someBlock;

parantez eksik
newacct

Birincisi işe yaramazken bu benim için çalıştı. Btw teşekkürler dostum, bu gerçekten yararlı oldu!
tanou

23

Bunu, bloğu bir blok parametresi olarak geçirerek yapabilirsiniz:

//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);

8

Aşağıdaki örnekte с işlevlerini kullanarak blok geçirmenin başka bir yolu. Arka planda ve ana kuyrukta herhangi bir şey gerçekleştirmek için işlevler oluşturdum.

blocks.h dosyası

void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));

blocks.m dosyası

#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);
}

Gerektiğinde blokları içe aktarın. H ve çağırın:

- (void)loadInBackground {

    performInBackground(^{

        NSLog(@"Loading something in background");
        //loading code

        performOnMainQueue(^{
            //completion hadler code on main queue
        });
    });
}

6

Bloğu sizin için geçerliyse basit bir özellik olarak da ayarlayabilirsiniz:

@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);

block özelliğinin "kopya" olduğundan emin olun!

ve elbette typedef'i de kullanabilirsiniz:

typedef void (^SimpleBlock)(id);

@property (nonatomic, copy) SimpleBlock someActionHandler;



3

Ben çalkalandıktan sonra zar değerlerini döndürecek bir sınıf için bir completionBlock yazdım:

  1. Typedef'i returnType (tanımlamanın .hüstünde @interface) ile tanımlayın

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
  2. A @property( .h) bloğu için tanımla

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
  3. finishBlock( .h) İle bir yöntem tanımlama

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
  4. Önceki tanımlanan yönteme takın .mdosya ve taahhüt finishBlocketmek @propertyönce tanımlanmış

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
  5. Tetiklemek için completionBlockönceden tanımlanmış bir değişkenTipi yazın (Var olup olmadığını kontrol etmeyi unutmayın completionBlock)

    if( self.completeDiceRolling ){
        self.completeDiceRolling(self.dieValue);
    }

2

Bu iş parçacığında verilen cevaplara rağmen, gerçekten bir Blok fonksiyon olarak - ve bir parametre ile alacak bir fonksiyon yazmak için mücadele etti. Sonunda, burada bulduğum çözüm.

loadJSONthreadBir JSON Web Hizmeti URL'si alır, bir arka plan iş parçacığında bu URL'den bazı JSON verileri yüklemek, sonra arama işlevine sonuçların bir NSArray * döndürmek genel bir işlev yazmak istedim .

Temel olarak, tüm arka plan iş parçacığı karmaşıklığını yeniden kullanılabilir genel bir işlevde gizli tutmak istedim.

İşte ben bu işlevi şöyle diyorum:

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)
    }

} ];

... ve bununla uğraştığım bit: nasıl bildirilir ve veri yüklendikten sonra Blok işlevini çağırmak ve yüklü Blockbir NSArray * iletmek için nasıl alınır :

+(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);

        });
    });
}

Bu StackOverflow soru, bir blok parametre olarak geçen fonksiyonları aramak için, bu yüzden yukarıdaki kodu basitleştirilmiş ve loadJSONDataFromURLişlevi dahil değil .

Ancak, ilgileniyorsanız, bu JSON yükleme işlevinin bir kopyasını bu blogda bulabilirsiniz: http://mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm

Umarım bu diğer bazı XCode geliştiricilerine yardımcı olur! (Bu soruyu ve cevabımı oylamayı unutmayın, eğer varsa!)


1
Bu gerçekten ios ve bloklar için gördüğüm en iyi hilelerden biri. Bayıldım !!!!
portforwardpodcast

1

Tam şablon şöyle görünür

- (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 {

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.