매개 변수로서 Objective-C 패스 블록


답변:


256

블록의 유형은 인수 및 반환 유형에 따라 다릅니다. 일반적인 경우, 블록 유형의 함수 포인터 유형은 동일한 방식으로 선언되어 있지만 교체 *로모그래퍼 ^. 메소드에 블록을 전달하는 한 가지 방법은 다음과 같습니다.

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

그러나 보시다시피 지저분합니다. 대신 a typedef를 사용하여 블록 유형을 더 깨끗하게 만들 수 있습니다 .

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

그런 다음 해당 블록을 다음과 같은 방법으로 전달하십시오.

- (void)iterateWidgets:(IteratorBlock)iteratorBlock;

왜 id를 인수로 전달합니까? 예를 들어 NSNumber를 쉽게 전달할 수 없습니까? 어떻게 보일까요?
bas

7
당신은 확실히 같은 강력한 형식의 인수를 전달할 수 있습니다 NSNumber *하거나 std::string&또는 다른 어떤 당신이 함수 인수로 전달할 수 있습니다. 이것은 단지 예일뿐입니다. (대체 이외의 상응하는 블록 용 id으로 NSNumber(가), typedef이 될 것이다 typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);.)
조나단 Grynspan

메소드 선언을 보여줍니다. 블록의 문제점 중 하나는 "지저분한"선언 스타일이 실제 블록 인수를 사용하여 실제 메소드 호출을 명확하고 쉽게 작성할 수 없다는 것입니다.
uchuugaka

Typedef는 코드 작성이 쉬울뿐만 아니라 블록 / 함수 포인터 구문이 가장 깨끗하지 않기 때문에 읽기가 훨씬 쉽습니다.
pyj

@JonathanGrynspan, Swift 세계에서 왔지만 오래된 Objective-C 코드를 터치 해야하는 경우 블록이 탈출하는지 여부를 어떻게 알 수 있습니까? 나는 기본적으로 블록으로 장식되어있는 경우를 제외하고는 탈출 NS_NOESCAPE하지만 enumerateObjectsUsingBlock, 탈출하지 않는다고 들었지만 NS_NOESCAPE사이트의 어느 곳도 보지 못하고 Apple 문서에서 탈출이 언급되지 않았습니다. 도울 수 있니?
Mark A. Donohoe

62

이 질문에 대한 가장 쉬운 설명은 다음 템플릿을 따르는 것입니다.

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

1
[self saveWithCompletionBlock : ^ (NSArray * array, NSError * error) {// 코드}]; 이 예제에서 리턴 유형은 void입니까?
Alex

51

도움이 될 수 있습니다.

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

당신은 괄호를 놓쳤다
newacct

이것은 이전 버전은 그렇지 않은 동안 나를 위해 일했습니다. Btw 고마워 친구, 정말 도움이되었습니다!
tanou

23

다음과 같이 블록을 블록 매개 변수로 전달하면됩니다 :

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

아래 예에서 с 함수를 사용하여 블록을 전달하는 또 다른 방법입니다. 백그라운드와 메인 큐에서 무엇이든 수행하는 기능을 만들었습니다.

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

필요한 경우 import blocks.h보다 다음을 호출하십시오.

- (void)loadInBackground {

    performInBackground(^{

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

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

6

해당되는 경우 블록을 간단한 속성으로 설정할 수도 있습니다.

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

블록 속성이 "복사"인지 확인하십시오!

물론 typedef를 사용할 수도 있습니다.

typedef void (^SimpleBlock)(id);

@property (nonatomic, copy) SimpleBlock someActionHandler;



3

나는 주사위가 흔들린 후에 주사위의 가치를 반환하는 클래스에 대해 completeBlock을 썼습니다.

  1. returnType을 사용하여 typedef 정의 ( .h위의 @interface선언)

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
  2. @property블록에 대한 정의 ( .h)

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
  3. finishBlock( .h) 로 메소드 정의

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
  4. 에서 이전에 정의 된 방법 삽입 .m파일을 커밋 finishBlock@property전에 정의

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
  5. completionBlock미리 정의 된 variableType 전달 을 트리거하려면 ( completionBlock존재 여부를 확인하는 것을 잊지 마십시오 )

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

2

이 스레드에 대한 답변에도 불구하고 실제로 블록을 함수로 사용하는 함수를 작성하는 데 어려움을 겪었습니다. 결국 여기에 내가 찾은 해결책이 있습니다.

loadJSONthreadJSON 웹 서비스의 URL을 가져 와서이 URL의 JSON 데이터를 백그라운드 스레드에로드 한 다음 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 개발자에게 도움이되기를 바랍니다! (그렇다면이 질문과 내 대답에 투표하는 것을 잊지 마십시오!)


1
이것은 내가 ios 및 블록에 대해 본 최고의 트릭 중 하나입니다. 사랑해 !!!!
portforwardpodcast 2018 년

1

전체 템플릿은 다음과 같습니다

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

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.