iOS - 删除 NSMutableArray 中的对象抛出 NSRangeException
iOS - Removing An Object In NSMutableArray Throws NSRangeException
我正在使用 TCP 短连接向主机发送数据。当我想向主机发送数据时,我将实例化一个套接字,将套接字实例存储到一个数组中,然后在数据写入成功后将其删除。
我是这样做的:
- (void)connectAndSendData:(NSData *)data
{
GCDAsyncSocket *asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
[_connectionDataToBeSentMap setObject:data forKey:[NSValue valueWithNonretainedObject:asyncSocket]];
NSError *error;
if(![asyncSocket connectToHost:_host onPort:TCP_COMMUNICATION_PORT withTimeout:TCP_CONNECTION_TIMEOUT error:&error])
{
NSLog(@"TCPShortConnection - failed when attempting to connect to host with IP %@", _host);
}
[_connectionList addObject:asyncSocket];
}
我会在写完数据后立即断开socket。因此连接将在之后被删除:
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
[_connectionList removeObject:sock];
if(err.code == GCDAsyncSocketConnectTimeoutError)
{
// NSLog(@"TCPShortConnection - on connection timed out");
[_delegate onTCPShortConnectionTimedOut];
}
else
{
// NSLog(@"TCPShortConnection - on disconnection of socket with host %@", _host);
}
}
这一行 [_connectionList removeObject:sock];
抛出 NSRangeException。我不知道为什么。因为我通过删除空数组中不存在的对象进行了测试,所以没有抛出这样的异常。
这是日志:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(0x258d9b0b 0x25096dff 0x257ea6d3 0x257ff033 0xfae43 0x1ca94d 0x4ccba7 0x4d8eff 0x4d87f1 0x2560ae0d 0x2560a9fc)
libc++abi.dylib: terminating with uncaught exception of type NSException
截图:
编辑:
请注意这不是 100% 发生的。
这里有两个:
案例 1:
在_connectionList数组中成功添加对象时。
[_connectionList addObject:asyncSocket];
在这种情况下不会发生此类异常。
[_connectionList removeObject:sock];
成功从 _connectionList 数组中删除对象。
案例 2:
当_connectionList数组中对象没有添加成功时。
这里抛出一个异常,
[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]
因为对象没有添加到_connectionList数组中。
所以这个问题叫做reader-writer problem.
因为 NSMutableArray 不是线程安全的。
猜测:您正在从不同的线程
访问您的NSMutableArray
解释:
NSMutableArray
不是线程安全的,并发读写也不安全 - 如果您从 GCD 并发队列访问数组,可能会发生这种情况。
GCDAsyncSocket
顾名思义使用 GCD。特别是委托调用在用户设置的 GCD 队列上调度,并且该队列可能是并发的。
您正在将全局并发队列之一设置为委托队列。
解决方案:您应该首先确定您是否应该使用数组,我们无法为您确定。如果您需要数组,那么它需要是线程安全的。一个简单的方法是:
当您创建数组时,还要为该数组创建一个顺序 GCD 队列。
要执行从数组读取的数组方法,请在数组的顺序队列上执行 同步 分派。您可以使用 __block
变量 return 分派块的结果。
要对数组执行写操作,请使用 异步 调度到数组的队列。
这个简单的模型确保不会同时发生对数组的两个操作,并且所有读取和写入都按照它们被分派的顺序进行。
正如我在开头所说的,我猜并发访问数组是你的问题,你需要自己确定。如果是这样应该修复它。
HTH
我正在使用 TCP 短连接向主机发送数据。当我想向主机发送数据时,我将实例化一个套接字,将套接字实例存储到一个数组中,然后在数据写入成功后将其删除。
我是这样做的:
- (void)connectAndSendData:(NSData *)data
{
GCDAsyncSocket *asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
[_connectionDataToBeSentMap setObject:data forKey:[NSValue valueWithNonretainedObject:asyncSocket]];
NSError *error;
if(![asyncSocket connectToHost:_host onPort:TCP_COMMUNICATION_PORT withTimeout:TCP_CONNECTION_TIMEOUT error:&error])
{
NSLog(@"TCPShortConnection - failed when attempting to connect to host with IP %@", _host);
}
[_connectionList addObject:asyncSocket];
}
我会在写完数据后立即断开socket。因此连接将在之后被删除:
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
[_connectionList removeObject:sock];
if(err.code == GCDAsyncSocketConnectTimeoutError)
{
// NSLog(@"TCPShortConnection - on connection timed out");
[_delegate onTCPShortConnectionTimedOut];
}
else
{
// NSLog(@"TCPShortConnection - on disconnection of socket with host %@", _host);
}
}
这一行 [_connectionList removeObject:sock];
抛出 NSRangeException。我不知道为什么。因为我通过删除空数组中不存在的对象进行了测试,所以没有抛出这样的异常。
这是日志:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(0x258d9b0b 0x25096dff 0x257ea6d3 0x257ff033 0xfae43 0x1ca94d 0x4ccba7 0x4d8eff 0x4d87f1 0x2560ae0d 0x2560a9fc)
libc++abi.dylib: terminating with uncaught exception of type NSException
截图:
编辑:
请注意这不是 100% 发生的。
这里有两个:
案例 1: 在_connectionList数组中成功添加对象时。
[_connectionList addObject:asyncSocket];
在这种情况下不会发生此类异常。
[_connectionList removeObject:sock];
成功从 _connectionList 数组中删除对象。
案例 2: 当_connectionList数组中对象没有添加成功时。
这里抛出一个异常,
[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]
因为对象没有添加到_connectionList数组中。
所以这个问题叫做reader-writer problem.
因为 NSMutableArray 不是线程安全的。
猜测:您正在从不同的线程
访问您的NSMutableArray
解释:
NSMutableArray
不是线程安全的,并发读写也不安全 - 如果您从 GCD 并发队列访问数组,可能会发生这种情况。GCDAsyncSocket
顾名思义使用 GCD。特别是委托调用在用户设置的 GCD 队列上调度,并且该队列可能是并发的。您正在将全局并发队列之一设置为委托队列。
解决方案:您应该首先确定您是否应该使用数组,我们无法为您确定。如果您需要数组,那么它需要是线程安全的。一个简单的方法是:
当您创建数组时,还要为该数组创建一个顺序 GCD 队列。
要执行从数组读取的数组方法,请在数组的顺序队列上执行 同步 分派。您可以使用
__block
变量 return 分派块的结果。要对数组执行写操作,请使用 异步 调度到数组的队列。
这个简单的模型确保不会同时发生对数组的两个操作,并且所有读取和写入都按照它们被分派的顺序进行。
正如我在开头所说的,我猜并发访问数组是你的问题,你需要自己确定。如果是这样应该修复它。
HTH