2015-05-09 57 views
1

我創建了一個實現了幾個方法的類。這些方法由另一個類調用,並通過NSBlockOperation進行管理。NSBlockOperation EXC_BAD_ACCESS

我NSBlockOperation工作正常,我有問題,當我試圖評估的變量:

EXC_BAD_ACCESS

我已經做了很多研究在互聯網上和this是最接近我的問題之一。我試圖做sugerito,但你遇到同樣的問題。

你有什麼建議嗎?

編輯:

這是堆棧跟蹤:

2015-05-09 15:24:45.976 OutParameters[12326:743087] Stack trace : (
    0 OutParameters      0x000000010e5d6602 -[ListOperation _method1:] + 194 
    1 OutParameters      0x000000010e5d646f __25-[ListOperation method1:]_block_invoke + 95 
    2 Foundation       0x000000010e74257f __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7 
    3 Foundation       0x000000010e6830b2 -[NSBlockOperation main] + 98 
    4 Foundation       0x000000010e665774 -[__NSOperationInternal _start:] + 645 
    5 Foundation       0x000000010e665383 __NSOQSchedule_f + 184 
    6 libdispatch.dylib     0x00000001113f4614 _dispatch_client_callout + 8 
    7 libdispatch.dylib     0x00000001113db6a7 _dispatch_queue_drain + 2176 
    8 libdispatch.dylib     0x00000001113dacc0 _dispatch_queue_invoke + 235 
    9 libdispatch.dylib     0x00000001113de3b9 _dispatch_root_queue_drain + 1359 
    10 libdispatch.dylib     0x00000001113dfb17 _dispatch_worker_thread3 + 111 
    11 libsystem_pthread.dylib    0x0000000111761637 _pthread_wqthread + 729 
    12 libsystem_pthread.dylib    0x000000011175f40d start_wqthread + 13 
) 

修改後的代碼:

- (IBAction)testCallMethod:(id)sender { 
    NSString * output; 
    [self.listOperationObj method1:&output]; 
    NSLog(@"Output: %@", output); 
} 

而且

@interface ListOperation : NSObject 

-(void)method1:(NSString**)output; 

@end 

#define MAX_OPERATIONS 10 
//define a log-level 
static int logLevel = CSLOG_LEVEL_INFO; 
@interface ListOperation() 
// Tail used to synchronize the methods 
@property NSOperationQueue *queue; 
@end 

#pragma mark - Public methods 

@implementation ListOperation 

- (id)init { 
    self = [super init]; 
    if(self) { 
     _queue = [NSOperationQueue new]; 
     if(_queue) { 
      [_queue setMaxConcurrentOperationCount:1]; 
     }else { 
      NSLog(@"TokenMgr creation failed: error creating operation queue"); 
      self = nil; 
     } 
    } 
    return self; 
} 

-(void)method1:(NSString *__autoreleasing *)output{ 
    LOGFSTART 
    if([self _isQueueFull] == FALSE) { 
     WEAK 
     NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 
      STRONG 
      [strongSelf _method1:output]; 
     }]; 
     [self.queue addOperation:operation]; 
     [operation waitUntilFinished]; 
    } 
    else { 
     LOGE(@"TokenMgr's queue is full, aborting operation"); 
    } 
    LOGFEND 
} 

#pragma mark - private methods 

-(void)_method1:(NSString *__autoreleasing *)output{ 
    std::string testString = "try put string"; 
    *output = [NSString stringWithUTF8String:testString.c_str()]; 
} 

- (BOOL) _isQueueFull { 
    return self.queue.operationCount > MAX_OPERATIONS; 
} 

@end 

如果我反覆按下按鈕,此更改會導致同樣的錯誤。

+0

從發生崩潰時發佈堆棧跟蹤。 –

+0

我已編輯,謝謝。 – Stephany

回答

1

直接的問題與塊無關。你的代碼片段說:

- (IBAction)testCallMethod:(id)sender { 
    NSString *__autoreleasing * output; 
    [self.listOperationObj method1:output]; 
    NSLog(@"Output: %@", *output); 
} 

這是行不通的,因爲output不會指向有效的內存地址,而當您嘗試取消引用與*output = ...未初始化的指針,它會崩潰。

相反, 應該是:

- (IBAction)testCallMethod:(id)sender { 
    NSString *output; 
    [self.listOperationObj method1:&output]; 
    NSLog(@"Output: %@", output); 
} 

現在output引用一個真正NSString *指針,你可以用一個對象的引用填充。


還有第二個,更深層次的問題,即利用* __autoreleasing *引用了裏面的操作實例化一個對象。操作擁有自己的自動釋放池,因此您在與testCallMethod中使用該對象時發生競爭。

取而代之,通常使用完成塊將數據傳回給調用者。因此:

- (IBAction)testCallMethod:(id)sender { 
    [self.listOperationObj method2:^(NSString *output) { 
     NSLog(@"Output: %@", output); 
    }]; 
} 

- (void)method2:(void (^)(NSString *))completionHandler { 
    LOGFSTART 
    if([self _isQueueFull] == FALSE) { 
     WEAK 
     NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 
      STRONG 
      [strongSelf _method2:completionHandler]; 
     }]; 
     [self.queue addOperation:operation]; 
     // [operation waitUntilFinished]; // not needed any more 
    } 
    else { 
     LOGE(@"TokenMgr's queue is full, aborting operation"); 
    } 
    LOGFEND 
} 

-(void)_method2:(void (^)(NSString *))completionHandler { 
    std::string testString = "try put string"; 
    NSString *output = [NSString stringWithUTF8String:testString.c_str()]; 
    completionHandler(output); 
} 

注意,順便說一下,這也解決了你的榜樣另一個問題,那你有事實把一個waitUntilFinished電話在那裏。你永遠不應該從主線程中調用waitUntilFinished。如果你使用完成塊,就像上面那樣,那就不再需要了。

+0

感謝您的回答。我試圖按照我的建議去做。它似乎只有時纔有效。如果我按下按鈕快速給我出現相同的錯誤,有時甚至是我們第一次按下。 – Stephany

+0

是的,這是第二個完全不相關的問題。注意,當你嘗試在視圖控制器中使用'output'時,它會崩潰,而不是在'_method1'中使用'output'。請參閱修訂後的答案以討論這個其他問題以及如何安全地傳回數據的常見解決方案。 – Rob

+0

感謝您的幫助(; – Stephany