2013-07-12 6 views
3

나중에이 블록을 실행하는 비동기 메서드에 블록을 전달하고 있습니다. someMethod에 전달하기 전에 블록을 복사하지 않으면 내 응용 프로그램이 중단됩니다. 성공 : 실패 :블록을 비동기 메서드에 전달

forwardInvocation의 블록을 복사하는 대신 someMethod에 전달하기 전에 복사하는 대신 success : failure : ?

흐름은 것으로 someMethod : 성공 : 오류 -> forwardInvocation -> httpGet : 성공 : 실패

httpGet : 성공 : 불량 : 성공 또는 HTTP 상태 코드에 따라 불량 블록을 실행한다.

// AppDelegate.h 

@interface AppDelegate : UIResponder <UIApplicationDelegate> 

@property (strong, nonatomic) id response; 
@property (strong, nonatomic) NSError *error; 

@end 

// AppDelegate.m 

@implementation AppDelegate 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    // The app crashes if the blocks are not copied here! 
    [[MyController new] someMethod:[^(NSString *response) { 
     self.response = response; 
     NSLog(@"response = %@", response); 
    } copy] failure:[^(NSError *error) { 
     self.error = error; 
    } copy]]; 

    return YES; 
} 

@end 

// MyController.h 

@protocol MyControllerProtocol <NSObject> 

@optional 


- (void)someMethod:(void (^)(NSString *response))success 
      failure:(void (^)(NSError *error))failure; 

@end 

@interface MyController : NSObject <MyControllerProtocol> 

@end 

// MyController.m 

#import "MyController.h" 
#import "HTTPClient.h" 

@implementation MyController 

- (void)forwardInvocation:(NSInvocation *)invocation { 
    [invocation retainArguments]; 

    NSUInteger numberOfArguments = [[invocation methodSignature] numberOfArguments]; 

    typedef void(^SuccessBlock)(id object); 
    typedef void(^FailureBlock)(NSError *error); 

    __unsafe_unretained SuccessBlock successBlock1; 
    __unsafe_unretained SuccessBlock failureBlock1; 
    [invocation getArgument:&successBlock1 atIndex:(numberOfArguments - 2)]; // success block is always the second to last argument (penultimate) 
    SuccessBlock successBlock = [successBlock1 copy]; 
    [invocation getArgument:&failureBlock1 atIndex:(numberOfArguments - 1)]; // failure block is always the last argument 
    FailureBlock failureBlock = [failureBlock1 copy]; 

    NSLog(@"successBlock copy = %@", successBlock); 
    NSLog(@"failureBlock copy = %@", failureBlock); 

    // Simulates a HTTP request and calls the success block later! 
    [HTTPClient httpGet:@"somerequest" success:successBlock failure:failureBlock]; 
} 

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { 
    NSMethodSignature *methodSignature = [super methodSignatureForSelector:sel]; 
    return methodSignature; 
} 

@end 


// HTTPClient.h 

@interface HTTPClient : NSObject 

+ (void)httpGet:(NSString *)path 
     success:(void (^)(id object))success 
     failure:(void (^)(NSError *error))failure; 

@end 

// HTTPClient.m 

#import "HTTPClient.h" 

@implementation HTTPClient 

+ (void)httpGet:(NSString *)path 
     success:(void (^)(id object))success 
     failure:(void (^)(NSError *error))failure 
{ 
    // Invoke the method. 
    double delayInSeconds = 2.0; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
    dispatch_after(popTime, dispatch_get_main_queue(), 
       ^{ 
        success(@"foo"); 
       }); 
} 

@end 

완전한 소스 코드

는 여기에서 찾을 수 있습니다 : https://github.com/priteshshah1983/BlocksWithNSInvocation

당신이 도와 주실 수 있습니까?

+0

someMethod를 참조해야합니다. 실패 : 코드의 어느 부분에도 나타나지 않습니다. – kball

+0

@kball someMethod : success : failure : 구현되지 않았습니다. 따라서 methodSignatureForSelector :와 forwardInvocation :이 호출됩니다. – pshah

+0

@kball 일부 오타가 수정되었습니다. 죄송합니다. 그것은 어떤 방법으로 생각됩니다 : 성공 : 실패 : – pshah

답변

3

범인은 라인 [invocation retainArguments]입니다. 그 줄을 주석 처리하면 잘 동작합니다. (호출이 비동기 적으로 저장하거나 사용 않습니다 때문 라인 어쨌든 필요 없었어.)

설명 :

-retainArguments가하는 일에 대해 생각해보십시오. 객체 포인터 유형의 모든 인수에 대해 retain을 호출합니다. 호출이 할당 해제되면 release이 호출됩니다.

하지만 인수는 스택 (복사되지 않은) 블록입니다. retainrelease은 효과가 없습니다 (힙 객체가 아니기 때문에). 그래서 그것이 유지되면 아무 일도 일어나지 않고 (충돌로부터) 어떤 시점에서 호출이 자동으로 해제 된 것처럼 보입니다 (완벽하게 정상적인 일이 발생합니다). 따라서 호출의 최종 릴리스와 할당 해제는 비동기 적으로 발생합니다. 호출이 할당 해제되면, 유지 된 인수를 해제하려고 시도하지만, 그때까지는 더 이상 유효하지 않은 스택 블록을 알려주려고하면 충돌이 발생합니다.

P. forwardInvocation:의 블록 복사도 필요하지 않았습니다.