iOS - 在NSMutableArray中删除对象引发NSRangeException

时间:2016-09-03 03:46:33

标签: ios objective-c nsmutablearray 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];
}

写完数据后我会立即断开套接字。因此,之后将删除连接:

- (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

截图:

enter image description here

编辑:

请注意,这不是百分之百。

2 个答案:

答案 0 :(得分:3)

猜猜:您正在从不同的线程访问NSMutableArray

说明:

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

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

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

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

    1. 创建阵列时,还要为此阵列创建顺序 GCD队列。

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

    3. 要对阵列执行写操作,请使用异步分派到阵列的队列。

    4. 这个简单的模型确保数组上不能同时进行两个操作,并且所有读写都按照它们的调度顺序进行。

      正如我在开始时所说,我猜测并发访问阵列是你的问题,你需要自己确定。如果是这样就应该解决它。

      HTH

答案 1 :(得分:0)

这里有两个:

案例1: 在_connectionList数组中成功添加对象时。

   [_connectionList addObject:asyncSocket];

在这种情况下,不会发生此类异常。

[_connectionList removeObject:sock];

成功从_connectionList数组中删除对象。

<强> CASE2: 当对象未在_connectionList数组中成功添加时。

在这里抛出一个例外,

[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]

因为该对象未在_connectionList数组中成功添加。

所以这个问题叫做读写器问题

因为NSMutableArray不是线程安全的。