Khối thông qua Objective-C làm tham số


Câu trả lời:


256

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;

Tại sao bạn chuyển id làm đối số? Chẳng hạn, có thể dễ dàng vượt qua NSNumber không? Làm thế nào mà nhìn?
bas

7
Bạn chắc chắn có thể vượt qua một cuộc tranh luận mạnh mẽ-gõ như 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ế idbằng NSNumber, typedefsẽ là typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);.)
Jonathan Grynspan

Điều này cho thấy khai báo phương pháp. Một rắc rối với các khối là kiểu khai báo "lộn xộn" không làm cho nó rõ ràng và dễ dàng viết lời gọi phương thức thực tế với một đối số khối thực.
uchuugaka

Typedefs không chỉ làm cho mã dễ viết hơn mà còn dễ đọc hơn vì cú pháp con trỏ khối / hàm không phải là sạch nhất.
pyj

@JonathanGrynspan, đến từ thế giới Swift nhưng phải chạm vào một số mã Objective-C cũ, làm thế nào tôi có thể biết liệu một khối có thoát ra hay không? Tôi đọc theo mặc định, các khối đang thoát trừ khi được trang trí NS_NOESCAPE, nhưng enumerateObjectsUsingBlocktôi được bảo là không thoát, nhưng tôi không thấy NS_NOESCAPEbấ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?
Mark A. Donohoe

62

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

1
[tự lưuWithCompletionBlock: ^ (NSArray * mảng, lỗi NSError *) {// mã của bạn}]; Trong ví dụ này, kiểu trả về bị bỏ qua vì nó là void?
Alex

51

Điều này có thể hữu ích:

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

bạn đang thiếu dấu ngoặc đơn
newacct

Cái này làm việc cho tôi trong khi cái trước thì không. Btw cảm ơn bạn đời, đó là, thực sự, hữu ích!
tanou

23

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

8

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

6

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;


4

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 đó :)

http://fuckingblocksyntax.com


Điều này đã tiết kiệm thời gian của tôi
Le Đinh

3

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:

  1. Xác định typedef với returnType ( khai báo .hở trên @interface)

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
  2. Xác định a @propertycho khối ( .h)

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
  3. Xác định một phương thức với finishBlock( .h)

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
  4. Chèn phương pháp được xác định trước đó trong .mtập tin và cam kết finishBlockđể @propertyđược xác định trước

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
  5. Để kích hoạt completionBlockvượt qua biếnType được xác định trước cho nó (Đừng quên kiểm tra xem có completionBlocktồn tại không)

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

2

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, loadJSONthreadsẽ 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 BlockNSArray * 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 loadJSONDataFromURLhà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ó!)


1
Đây thực sự là một trong những thủ thuật hay nhất tôi từng thấy đối với ios và các khối. Yêu nó người đàn ông !!!!
portforwardpodcast

1

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 {

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.