qiniu / happy-dns-objc

dns library for objective c
MIT License
501 stars 83 forks source link

was mutated while being enumerated. 崩溃 #47

Closed dimsky closed 7 years ago

dimsky commented 7 years ago

Bugly 统计,HappytDNS 导致 崩溃很频繁。

NSGenericException *** Collection <NSArrayM: 0x1513962d0> was mutated while being enumerated. 解析原始 0 CoreFoundation _exceptionPreprocess + 124 1 libobjc.A.dylib objc_exception_throw + 56 2 CoreFoundation -[NSException name] 3 HappyDNS records2Ips + 208 4 HappyDNS -[QNDnsManager queryInternalWithDomain:] + 1472 5 HappyDNS -[QNDnsManager queryWithDomain:] + 228 6 Qiniu -[QNSessionManager sendRequest:withCompleteBlock:withProgressBlock:withCancelBlock:] + 944 7 libdispatch.dylib dispatch_call_block_and_release + 24 8 libdispatch.dylib dispatch_client_callout + 16 9 libdispatch.dylib dispatch_root_queue_drain + 2140 10 libdispatch.dylib dispatch_worker_thread3 + 112 11 libsystem_pthread.dylib _pthread_wqthread + 1092 12 libsystem_pthread.dylib start_wqthread + 4

具体问题在 QNDnsManager.m 文件中, 对 queryInternalWithDomain 方法进行了异步调用,并且在@synchronized 块 中对 result 数组进行了更改,所以会导致崩溃, 建议在 records2Ips 方法中 的 for (QNRecord *r in records) 改为 for (QNRecord *r in [records copy]) 或者不要使用 for in 来进行循环。

static NSArray *records2Ips(NSArray *records) {
    NSMutableArray *array = [[NSMutableArray alloc] init];
    for (QNRecord *r in records) {    // 此处使用 for in  遍历会有问题
        [array addObject:r.value];
    }
    return array;
}
- (NSArray *)queryInternalWithDomain:(QNDomain *)domain {
    if (domain.hostsFirst) {
        NSArray *ret = [_hosts query:domain networkInfo:_curNetwork];
        if (ret != nil && ret.count != 0) {
            return ret;
        }
    }
    NSMutableArray *result;
    @synchronized(_cache) {
        if ([_curNetwork isEqualToInfo:[QNNetworkInfo normal]] && [QNNetworkInfo isNetworkChanged]) {
            [_cache removeAllObjects];
            _resolverStatus = 0;
        } else {
            result = [_cache objectForKey:domain.domain];
            if (result != nil && result.count > 1) {
                QNRecord *first = [result firstObject];
                [result removeObjectAtIndex:0];    // 此处对 result 进行了修改
                [result addObject:first];
            }
        }
    }

    if (result != nil && result.count > 0) {
        QNRecord *record = [result objectAtIndex:0];
        if (![record expired:[[NSDate date] timeIntervalSince1970]]) {
            return records2Ips(result);
        }
    }
...
alpha-pj commented 7 years ago

我也遇到了同样的崩溃问题

wangliangliang2 commented 7 years ago

感谢@dimsky 说的挺好的。我们来改下。

longbai commented 7 years ago

fixed in 0.3.11

FlyingRadish commented 6 years ago

0.3.12上出现了同样的问题,这个问题似乎并没有被fix 看了一下commit log,似乎在0.3.12,@longbai 去掉了 @dimsky 建议的copy步骤,并标注为"无用的copy",请问去掉的原因是什么?

wangjunhua1 commented 5 years ago
image

遇到同样的问题 在0.3.14 崩溃还挺多的 作者能看下吗@longbai