首页 > 解决方案 > 如何在 Objective C / async 问题中解决这种竞争条件?'索引 1 超出空数组的范围'

问题描述

嗨,我有这个库的问题:LTSupportAutomotive

我是一个快速的程序员,所以在目标 c 方面不是很好。

如何解决这种竞争状况?

我希望有一个人可以帮助我。

'*** -[__NSArrayM insertObject:atIndex:]: 空数组的索引 1 超出范围'

-(void)asyncEnqueueInternalCommand:(LTOBD2AdapterInternalCommand*)internalCommand
{
    @synchronized(self) {
        [_commandQueue addObject:internalCommand];
    }
}

源代码:

https://github.com/mickeyl/LTSupportAutomotive/blob/f51b962421f211ee6af5c733f79190117d7cac5e/LTSupportAutomotive/LTOBD2Adapter.m

更新 1:

使用第一个修复创建了一个分支: https ://github.com/Skyb0rg/LTSupportAutomotive/tree/BugfixMemoryManagement

添加额外的 commandQueue 后,我得到了新的错误:

在 LTBTLEWriteCharacteristicStream.m

-(void)characteristicDidWriteValue
{
    [self.delegate stream:self handleEvent:NSStreamEventHasSpaceAvailable];
}

此函数崩溃:在当前参数寄存器中找到选择器名称:委托

线程 6 崩溃:0 libobjc.A.dylib 0x00000001bcc19430 objc_retain + 16

1 LTSupportAutomotive 0x00000001093c2a34 -[LTBTLEWriteCharacteristicStream 特性DidWriteValue] (LTBTLEWriteCharacteristicStream.m:39)

2 LTSupportAutomotive 0x00000001093c2714 -[LTBTLESerialTransporter 外围设备:didWriteValueForCharacteristic:error:] (LTBTLESerialTransporter.m:311)

3 核心蓝牙 0x00000001c35e6ce0 -[CBPeripheral handleAttributeEvent:args:attributeSelector:delegateSelector:delegateFlag:] + 236

4 核心蓝牙 0x00000001c35e6e40 -[CBPeripheral 句柄CharacteristicEvent:characteristicSelector:delegateSelector:delegateFlag:] + 128

5 核心蓝牙 0x00000001c35e24f0 -[CBPeripheral handleMsg:args:] + 352

6 核心蓝牙 0x00000001c35dcbfc -[CBCentralManager handleMsg:args:] + 200

7 核心蓝牙 0x00000001c35eb770 __30-[CBXpcConnection _handleMsg:]_block_invoke + 56

8 libdispatch.dylib 0x00000001bd4696c8 _dispatch_call_block_and_release + 20

9 libdispatch.dylib 0x00000001bd46a484 _dispatch_client_callout + 12

10 libdispatch.dylib 0x00000001bd411bd0 _dispatch_lane_serial_drain$VARIANT$mp + 588

11 libdispatch.dylib 0x00000001bd41274c _dispatch_lane_invoke$VARIANT$mp + 480

12 libdispatch.dylib 0x00000001bd411a9c _dispatch_lane_serial_drain$VARIANT$mp + 280

13 libdispatch.dylib 0x00000001bd412718 _dispatch_lane_invoke$VARIANT$mp + 428

14 libdispatch.dylib 0x00000001bd41aeb8 _dispatch_workloop_worker_thread + 596

15 libsystem_pthread.dylib 0x00000001bd64d0dc _pthread_wqthread + 308

16 libsystem_pthread.dylib 0x00000001bd64fcec start_wqthread + 0

和另一个崩溃:LTBTLEReadCharacteristicStream.m

-(void)characteristicDidUpdateValue
{
        NSData* value = _characteristic.value;
        [_buffer appendData:value];
        [self.delegate stream:self handleEvent:NSStreamEventHasBytesAvailable];
}

*** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[OS_dispatch_data 流:handleEvent:]:无法识别的选择器发送到实例 0x281be1b90”

线程 9 崩溃:0 libsystem_kernel.dylib 0x00000001bd5c7104 __pthread_kill + 8

1 libsystem_pthread.dylib 0x00000001bd643020 pthread_kill$VARIANT$mp + 376

2 libsystem_c.dylib 0x00000001bd51ed78 中止 + 136

3 大众-R-CLUB会员APP 0x00000001045603ac uncaught_exception_handler + 68

4 核心基础 0x00000001bda321e0 __handleUncaughtException + 688

5 libobjc.A.dylib 0x00000001bcc01e4c _objc_terminate() + 108

6 VW-R-CLUB会员APP 0x0000000104555c4c BITCrashUncaughtCXXerminateHandler() (BITCrashCXXExceptionHandler.mm:183)

7 libc++abi.dylib 0x00000001bcbf50fc std::__terminate(void (*)()) + 12

8 libc++abi.dylib 0x00000001bcbf5188 std::terminate() + 80

9 libdispatch.dylib 0x00000001bd46a498 _dispatch_client_callout + 32

10 libdispatch.dylib 0x00000001bd411bd0 _dispatch_lane_serial_drain$VARIANT$mp + 588

11 libdispatch.dylib 0x00000001bd41274c _dispatch_lane_invoke$VARIANT$mp + 480

12 libdispatch.dylib 0x00000001bd411a9c _dispatch_lane_serial_drain$VARIANT$mp + 280

13 libdispatch.dylib 0x00000001bd412718 _dispatch_lane_invoke$VARIANT$mp + 428

14 libdispatch.dylib 0x00000001bd41aeb8 _dispatch_workloop_worker_thread + 596

15 libsystem_pthread.dylib 0x00000001bd64d0dc _pthread_wqthread + 308

16 libsystem_pthread.dylib 0x00000001bd64fcec start_wqthread + 0

标签: objective-cobd-ii

解决方案


首先,您显示的代码不是在 github 中找到的代码。您是同步 的所有用法_commandQueue,还是仅同步这个?如果只有这一个,为什么其他的不同步?

然后,在 github 中,我发现了 的多种用法_commandQueue,其中一些在名为async... 的方法中,但也有一些不是async,比如cancelPendingCommandsor responseCompleted。您需要找出async方法名称中的意图是什么,以及为什么方法 likecancelPendingCommands不是不知何故async


更新

所以主要思想似乎是保护_commandQueue串行内部的所有访问_dispatchQueue。... 方法中已经是这种情况async:它们是从该队列中调用的:

dispatch_async( _dispatchQueue, ^{
    [self asyncEnqueueInternalCommand:internalCommand];
});

因此,您需要确保每次访问_commandQueue都在此队列中排队,例如更改cancelPendingCommands为类似

-(void)cancelPendingCommands
{
    dispatch_async( _dispatchQueue, ^{
        // This cancels all but the first command in order to prevent sending a new command while
        // the response to an active command is still pending. OBD2 adapters usually can't cope with
        // that and emit a 'STOPPED' response in that case.
        if ( _hasPendingAnswer )
        {
            NSRange allButTheFirst = NSMakeRange( 1, _commandQueue.count - 1 );
            [_commandQueue removeObjectsInRange:allButTheFirst];
        }
        else
        {
            [_commandQueue removeAllObjects];
        }
    });
}

(或创建一个专用asyncCancelPendingCommands函数,该函数将从cancelPendingCommands调度块内部调用。依此类推。

PS 如果你需要同步执行,你也可以使用dispatch_sync而不是dispatch_async,但是你必须确保你不会造成死锁。


推荐阅读