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

解释:

  1. NSMutableArray 不是线程安全的,并发读写也不安全 - 如果您从 GCD 并发队列访问数组,可能会发生这种情况。

  2. GCDAsyncSocket 顾名思义使用 GCD。特别是委托调用在用户设置的 GCD 队列上调度,并且该队列可能是并发的。

  3. 您正在将全局并发队列之一设置为委托队列。

解决方案:您应该首先确定您是否应该使用数组,我们无法为您确定。如果您需要数组,那么它需要是线程安全的。一个简单的方法是:

  1. 当您创建数组时,还要为该数组创建一个顺序 GCD 队列。

  2. 要执行从数组读取的数组方法,请在数组的顺序队列上执行 同步 分派。您可以使用 __block 变量 return 分派块的结果。

  3. 要对数组执行写操作,请使用 异步 调度到数组的队列。

这个简单的模型确保不会同时发生对数组的两个操作,并且所有读取和写入都按照它们被分派的顺序进行。

正如我在开头所说的,我猜并发访问数组是你的问题,你需要自己确定。如果是这样应该修复它。

HTH