如何解决 Objective C / async 问题中的竞争条件? 'index 1 beyond bounds for empty array'
How to fix this race condition in Objective C / async problem? 'index 1 beyond bounds for empty array'
您好,我对这个库有疑问:
LTSupportAutomotive
我是一名 swift 程序员,所以在 objective c 方面并不是很擅长。
如何解决这个竞争条件?
希望有人能帮帮我。
'*** -[__NSArrayM insertObject:atIndex:]: 索引 1 超出空数组的范围'
-(void)asyncEnqueueInternalCommand:(LTOBD2AdapterInternalCommand*)internalCommand
{
@synchronized(self) {
[_commandQueue addObject:internalCommand];
}
}
源代码:
更新 1:
创建了一个包含第一个修复的分支:
https://github.com/Skyb0rg/LTSupportAutomotive/tree/BugfixMemoryManagement
添加额外的 commandQueue 后出现新错误:
在LTBTLEWriteCharacteristicStream.m
-(void)characteristicDidWriteValue
{
[self.delegate stream:self handleEvent:NSStreamEventHasSpaceAvailable];
}
此功能崩溃:
在当前参数寄存器中找到的选择器名称:delegate
线程 6 崩溃:
0 libobjc.A.dylib 0x00000001bcc19430 objc_retain + 16
1 LTSupportAutomotive 0x00000001093c2a34 -[LTBTLEWriteCharacteristicStream characteristicDidWriteValue] (LTBTLEWriteCharacteristicStream.m:39)
2 LTSupportAutomotive 0x00000001093c2714 -[LTBTLESerialTransporter peripheral:didWriteValueForCharacteristic:error:] (LTBTLESerialTransporter.m:311)
3 CoreBluetooth 0x00000001c35e6ce0 -[CBPeripheral handleAttributeEvent:args:attributeSelector:delegateSelector:delegateFlag:] + 236
4 CoreBluetooth 0x00000001c35e6e40 -[CBPeripheral handleCharacteristicEvent:characteristicSelector:delegateSelector:delegateFlag:] + 128
5 CoreBluetooth 0x00000001c35e24f0 -[CBPeripheral handleMsg:args:] + 352
6 核心蓝牙 0x00000001c35dcbfc -[CBCentralManager handleMsg:args:] + 200
7 CoreBluetooth 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 stream: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 VW-R-CLUB会员APP 0x00000001045603ac uncaught_exception_handler + 68
4 CoreFoundation 0x00000001bda321e0 __handleUncaughtException + 688
5 libobjc.A.dylib 0x00000001bcc01e4c _objc_terminate() + 108
6 VW-R-CLUB会员APP 0x0000000104555c4c BITCrashUncaughtCXXTerminateHandler() (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
首先,您显示的代码不是 github 中的代码。您是同步 _commandQueue
的所有用法还是仅同步这一个?如果只有这一个,为什么其他的不同步?
然后,在 github 中我发现了 _commandQueue
的多种用法,其中一些在名为 async
... 的方法中,但也有一些不是 async
,例如 cancelPendingCommands
或 responseCompleted
。您需要找出方法名称中 async
的意图是什么,以及为什么像 cancelPendingCommands
这样的方法不是 async
.
更新
所以主要思想似乎是保护串行 _dispatchQueue
内的所有 _commandQueue
访问。在 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
调用。依此类推。
P.S。如果你需要同步执行,你也可以使用 dispatch_sync
而不是 dispatch_async
,但是你必须确保你不会创建死锁。
您好,我对这个库有疑问: LTSupportAutomotive
我是一名 swift 程序员,所以在 objective c 方面并不是很擅长。
如何解决这个竞争条件?
希望有人能帮帮我。
'*** -[__NSArrayM insertObject:atIndex:]: 索引 1 超出空数组的范围'
-(void)asyncEnqueueInternalCommand:(LTOBD2AdapterInternalCommand*)internalCommand
{
@synchronized(self) {
[_commandQueue addObject:internalCommand];
}
}
源代码:
更新 1:
创建了一个包含第一个修复的分支: https://github.com/Skyb0rg/LTSupportAutomotive/tree/BugfixMemoryManagement
添加额外的 commandQueue 后出现新错误:
在LTBTLEWriteCharacteristicStream.m
-(void)characteristicDidWriteValue
{
[self.delegate stream:self handleEvent:NSStreamEventHasSpaceAvailable];
}
此功能崩溃: 在当前参数寄存器中找到的选择器名称:delegate
线程 6 崩溃: 0 libobjc.A.dylib 0x00000001bcc19430 objc_retain + 16
1 LTSupportAutomotive 0x00000001093c2a34 -[LTBTLEWriteCharacteristicStream characteristicDidWriteValue] (LTBTLEWriteCharacteristicStream.m:39)
2 LTSupportAutomotive 0x00000001093c2714 -[LTBTLESerialTransporter peripheral:didWriteValueForCharacteristic:error:] (LTBTLESerialTransporter.m:311)
3 CoreBluetooth 0x00000001c35e6ce0 -[CBPeripheral handleAttributeEvent:args:attributeSelector:delegateSelector:delegateFlag:] + 236
4 CoreBluetooth 0x00000001c35e6e40 -[CBPeripheral handleCharacteristicEvent:characteristicSelector:delegateSelector:delegateFlag:] + 128
5 CoreBluetooth 0x00000001c35e24f0 -[CBPeripheral handleMsg:args:] + 352
6 核心蓝牙 0x00000001c35dcbfc -[CBCentralManager handleMsg:args:] + 200
7 CoreBluetooth 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 stream: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 VW-R-CLUB会员APP 0x00000001045603ac uncaught_exception_handler + 68
4 CoreFoundation 0x00000001bda321e0 __handleUncaughtException + 688
5 libobjc.A.dylib 0x00000001bcc01e4c _objc_terminate() + 108
6 VW-R-CLUB会员APP 0x0000000104555c4c BITCrashUncaughtCXXTerminateHandler() (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
首先,您显示的代码不是 github 中的代码。您是同步 _commandQueue
的所有用法还是仅同步这一个?如果只有这一个,为什么其他的不同步?
然后,在 github 中我发现了 _commandQueue
的多种用法,其中一些在名为 async
... 的方法中,但也有一些不是 async
,例如 cancelPendingCommands
或 responseCompleted
。您需要找出方法名称中 async
的意图是什么,以及为什么像 cancelPendingCommands
这样的方法不是 async
.
更新
所以主要思想似乎是保护串行 _dispatchQueue
内的所有 _commandQueue
访问。在 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
调用。依此类推。
P.S。如果你需要同步执行,你也可以使用 dispatch_sync
而不是 dispatch_async
,但是你必须确保你不会创建死锁。