2013-06-03 30 views
7

Tôi đang cố gắng chuyển đối số chặn đến NSInvocation nhưng ứng dụng bị treo. Lệnh gọi thực hiện một yêu cầu mạng và gọi các khối thành công hoặc thất bại. Tôi nghĩ rằng vấn đề là các khối được dealloced trước khi yêu cầu mạng kết thúc. Tôi quản lý để làm cho nó hoạt động với một số hack Block_copy và nó không báo cáo bất kỳ rò rỉ nào bằng cách sử dụng Instruments.NSInvocation với đối số khối

Câu hỏi: - Có thể rò rỉ ở đó mặc dù Máy phân tích hoặc dụng cụ tĩnh không báo cáo nó? - Có cách nào tốt hơn để "giữ lại" khối không?

// Create the NSInvocation 
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector]; 
NSInvocation* invoc = [NSInvocation invocationWithMethodSignature:methodSignature]; 
[invoc setTarget:target]; 
[invoc setSelector:selector]; 

// Create success and error blocks. 
void (^successBlock)(id successResponse) = ^(id successResponse) { 
    // Some success code here ... 
}; 

void (^errorBlock)(NSError *error) = ^(NSError *error) { 
    // Some failure code here ... 
}; 

/* 
Without the two Block_copy lines, the block gets dealloced too soon 
and the app crashes with EXC_BAD_ACCESS 
I tried [successBlock copy] and [failureBlock copy] instead, 
but the app still crashes. 
It seems like Block_copy is the only way to move the block to the heap in this case. 
*/ 
Block_copy((__bridge void *)successBlock); 
Block_copy((__bridge void *)errorBlock); 
// Set the success and failure blocks. 
[invoc setArgument:&successBlock atIndex:2]; 
[invoc setArgument:&errorBlock atIndex:3]; 

[invoc retainArguments]; // does not retain blocks 

// Invoke the method. 
[invoc invoke]; 

Cập nhật: Tôi đã cập nhật mã thành bên dưới. Các khối là NSMallocBlocks, nhưng ứng dụng vẫn bị treo.

// Create success and error blocks. 
int i = 0; 
void (^successBlock)(id successResponse) = ^(id successResponse) { 
    NSLog(@"i = %i", i); 
    // Some success code here ... 
}; 

void (^errorBlock)(NSError *error) = ^(NSError *error) { 
    NSLog(@"i = %i", i); 
    // Some failure code here ... 
}; 

/*** Both blocks are NSMallocBlocks here ***/ 
// Set the success and failure blocks. 
void (^successBlockCopy)(id successResponse) = [successBlock copy]; 
void (^errorBlockCopy)(NSError *error) = [errorBlock copy]; 

/*** Both blocks are still NSMallocBlocks here - I think copy is a NoOp ***/ 

// Set the success and failure blocks. 
[invoc setArgument:&successBlockCopy atIndex:2]; 
[invoc setArgument:&errorBlockCopy atIndex:3]; 

[invoc retainArguments]; // does not retain blocks 

// Invoke the method. 
[invoc invoke]; 

Các khối được truyền xuống trong chuỗi như sau:

NSInvocationNSProxy (NSInvocation sử dụng forwardInvocation:) → method1methodN

methodN cuối cùng gọi là thành công hay thất bại khối tùy thuộc vào HTTP phản ứng.

Tôi có cần sao chép khối ở mọi giai đoạn không? Ví dụ trên đã nói về NSInvocation đầu tiên. Tôi có cần [invocation retainArguments]; ở mọi bước thích hợp không? Tôi đang sử dụng ARC.

Trả lời

8

Block_copy và thực sự [block copy]trả lại bản sao. Họ không kỳ diệu chuyển đổi bản gốc với một bản sao tại cùng một vị trí. Vì vậy, ít nhất tôi nghĩ rằng bạn muốn:

successBlock = Block_copy((__bridge void *)successBlock); 
errorBlock = Block_copy((__bridge void *)errorBlock); 

(hay tương đương, successBlock = [successBlock copy]; ...)

Nếu không, bạn đang tạo bản sao, không làm gì với họ và vẫn đi qua bản off vào thỉnh nguyện.

EDIT: vì vậy, tôi đặt đoạn mã sau vào một dự án:

@interface DummyClass: NSObject 
@end 

typedef void (^ successBlock)(id successResponse); 
typedef void (^ failureBlock)(NSError *error); 

@implementation DummyClass 

- (id)init 
{ 
    self = [super init]; 

    if(self) 
    { 
     SEL selector = @selector(someMethodWithSuccess:failure:); 
     id target = self; 

     // Create the NSInvocation 
     NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector]; 
     NSInvocation* invoc = [NSInvocation invocationWithMethodSignature:methodSignature]; 
     [invoc setTarget:target]; 
     [invoc setSelector:selector]; 

     // Create success and error blocks. 
     void (^successBlock)(id successResponse) = ^(id successResponse) { 
      // Some success code here ... 
      NSLog(@"Off, off, off with %@", successResponse); 
     }; 

     void (^errorBlock)(NSError *error) = ^(NSError *error) { 
      // Some failure code here ... 
      NSLog(@"Dance, dance, dance till %@", error); 
     }; 

     successBlock = [successBlock copy]; 
     errorBlock = [errorBlock copy]; 

     // Set the success and failure blocks. 
     [invoc setArgument:&successBlock atIndex:2]; 
     [invoc setArgument:&errorBlock atIndex:3]; 

     [invoc retainArguments]; // does not retain blocks 

     // 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(), 
     ^{ 
      [invoc invoke]; 

     }); 
    } 

    return self; 
} 

- (void)someMethodWithSuccess:(successBlock)successBlock failure:(failureBlock)failureBlock 
{ 
    NSLog(@"Words:"); 
    successBlock(@[@"your", @"head"]); 
    failureBlock([NSError errorWithDomain:@"you're dead" code:0 userInfo:nil]); 
} 

@end 

Và thêm dòng sau vào cuối application:didFinishLaunchingWithOptions::

DummyClass *unusedInstance = [[DummyClass alloc] init]; 

Kết quả là hai giây sau khi tung ra của tôi chương trình sau đây xuất hiện trên bảng điều khiển:

2013-06-02 20:11:56.057 TestProject[3330:c07] Words: 
2013-06-02 20:11:56.059 TestProject[3330:c07] Off, off, off with (
    your, 
    head 
) 
2013-06-02 20:11:56.060 TestProject[3330:c07] Dance, dance, dance till Error Domain=you're dead Code=0 "The operation couldn’t be completed. (you're dead error 0.)" 
+0

Tôi đã thử successBlock = [successBlock cop y]; và errorBlock = [errorBlock copy]; nhưng tôi nhận được cùng một vụ tai nạn với lỗi này: địa chỉ không chứa một phần trỏ đến một phần trong một tập tin đối tượng . Như tôi đã đề cập, việc thêm các dòng Block_copy như đã đề cập ngăn ngừa sự cố, nhưng tôi không chắc liệu chúng có bị rò rỉ bộ nhớ hay không. – pshah

+0

'Block_copy' mà bạn hiện đang sử dụng không có hiệu lực tài liệu. Tất cả những gì bạn thấy là các kết quả không xác định được gây ra bởi 'invoke' có vấn đề có hiệu ứng không xác định khác nhau. Nó không phải là một giải pháp thực sự. Và ngay cả những thây ma cũng sẽ không giúp bạn gỡ lỗi ở đây, vì chúng không thể giữ cho các đối tượng ngăn xếp còn sống giả tạo - một khi ngăn xếp phát triển trở lại, nó sẽ ghi đè lên chúng. – Tommy

+0

Tôi đã được ấn tượng rằng gọi Block_copy buộc khối được lưu trên heap thay vì ngăn xếp. Và, tôi vẫn không thể tìm ra lý do tại sao truyền vào [successBlock copy] thay vì successBlock thành lời gọi sẽ không hoạt động. – pshah