2013-07-12 100 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:成功:失敗:未實現。因此,將調用methodSignatureForSelector:然後forwardInvocation:。 – pshah

+0

@kball我修復了一些錯別字,很抱歉!它應該是一些方法:成功:失敗: – pshah

回答

3

罪魁禍首是行[invocation retainArguments]。如果你註釋掉那條線,它就可以正常工作。 (該行是從來沒有必要的,因爲無論如何調用從未存儲或使用異步)

說明:

想想-retainArguments一樣。它在對象指針類型的所有參數上調用retain。然後當調用被釋放時,它調用release

但參數是堆棧(非複製)塊。 retainrelease對它沒有影響(因爲它不是堆對象)。所以當它被保留時,什麼都不會發生,然後(從崩潰中)看起來調用是在某個時刻自動釋放的(這是一個完全正常的事情),所以調用的最終釋放和釋放是異步發生的。當調用被釋放時,它會嘗試釋放其保留的參數,但到那時它試圖向不再有效的堆棧塊發送消息,導致崩潰。

P.S.在forwardInvocation:塊的複製也是不必要的