块执行时出现EXC_BAD_ACCESS错误

4

我有一个只有一个方法的类,它使用URLConnection将序列化的NSDictionary发送到某个URL上的脚本,然后调用完成块。以下是该方法的代码:

- (void)sendDictionary:(NSDictionary *)dictionary toScript:(NSString *)scriptName completion:(void (^) (id response))completionBlock
{

    ...Serialize data and add it to an NSURLRequest request...

    H2URLConnection *connection = [[H2URLConnection alloc]initWithRequest:request];

    //Define a semaphore to block execution of later statements until the signal is received.
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);

    [connection setCompletionBlock:[^(id obj, NSError *err)
     {
         if (!err) {
            //Catch the server response
             NSString *receivedString = [[NSString alloc] initWithData:obj encoding:NSUTF8StringEncoding];
             NSLog( @"ChecklistAppNetworkManager received string: %@", receivedString);

             //Convert the JSON response into an NSDictionary
             NSError *otherError;
             id deserializedJSON = [NSJSONSerialization JSONObjectWithData:obj options:kNilOptions error:&otherError];

             if (otherError) {
                 NSLog(@"ChecklistAppNetworkManager JSON Error: %@", otherError.description);
             }

             [completionBlock invoke];

             NSLog(@"ChecklistAppNetworkManager JSON Response: %@", deserializedJSON);

             //Dispatch the semaphore signal so that the main thread continues.
             dispatch_semaphore_signal(sem);

         } else {
             NSLog(@"ChecklistAppNetworkManager encountered an error connecting to the server: %@", [err description]);
         }

     }copy]];
    //Finalize and initate the connection.
    [connection start];

    //Since block is dispatched to main queue, stall with a loop until the semaphore signal arrives.
    while (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
    }
}

我试图在另一个类中调用这个类实例的方法,其中完成块被定义。这是我遇到“EXC_BAD_ACCESS”错误的代码:

- (void)doSomeServerTask
{
    H2ChecklistAppNetworkManager *currentNetworkManager = ((H2AppDelegate *)[[UIApplication sharedApplication]delegate]).networkManager; //Instantiate class where that method is defined

    NSMutableDictionary *dictonary = [NSMutableDictionary dictionary];

    ...populate dictionary...

    [currentNetworkManager sendDictionary:dictionary toScript:@"script.php" completion:[^(id response)
     { //THIS iS THE LINE WHERE THE BAD ACCESS OCCURS

         NSLog(@"LoginViewController received response: %@", response);

     } copy]];
}

任何帮助都将不胜感激!


你应该分享堆栈跟踪。这里的信息不足以诊断问题。话虽如此,那些“copy”引用是不必要的。另外,我是否正确推断出H2URLConnection是一个NSURLConnection子类?标准的initWithRequest实际上会启动连接,所以除非你已经覆盖它,使其调用带有startImmediatelyNONSURLConnection方法initWithRequest,否则你会启动两次连接。确保使用startImmediately: NO版本。我建议打印request,以便我们可以确保URL不是nil之类的东西。 - Rob
此外,您正在阻塞主事件循环并运行子运行循环。这绝对不被推荐;这会破坏事情。您真的需要重构此代码以正确地异步化,并让主事件循环自由运行。 - bbum
1
顺便提一下,调用 completionBlock 的方式不是 [completionBlock invoke]; 而是 completionBlock(deserializedJSON)。或者,除非你指定 completionBlocknonnull 的,为谨慎起见,应该这样写 if (completionBlock) { completionBlock(deserializedJSON) } - Rob
@bbum 感谢您的建议。考虑到我需要发送一些数据,然后在服务器响应后显示响应数据,我该如何使其“完全异步”? - Hersh Bhargava
我之前使用的是ASIHTTPRequest,但该库已不再受支持。 - Hersh Bhargava
显示剩余3条评论
1个回答

5

completionBlock该方法的参数只有一个,但是你使用invoke方法去调用这个block。很可能导致崩溃的原因是运行时试图保留本应该作为参数传递的内存中的任何垃圾。


但是,您真的需要完全重构此代码。阻塞主事件循环是不好的。在MEL上运行子runloop甚至更糟,它改变了调度队列处理语义的方式,并可能导致病态的性能或行为。

您应该转移到真正的异步模型。如果应用程序无法继续直到完成这些查询,则显示一个阻止进度的模态指示器。

要做到这一点,您可以将代码松散结构如下:

• 将用户界面放入“加载...”或其他模态状态

• 使用完成处理程序执行异步数据请求

• 在完成处理程序中,将“更新UI”请求分派到主队列

• 在“更新UI”后,撤销模态“加载...”UI并更新用户的显示

没有必要阻塞主事件循环来完成所有这些操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接