2015-05-09 60 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