Я работаю над приложением Mac, которое использует сборку мусора. Приложение аварийно завершает работу для нескольких пользователей, и журналы сбоев указывают, что оно как-то связано с повреждением памяти или ее перебором.
Я публикую важный бит в журнале аварий ниже.
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000000
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Application Specific Information:
objc[81831]: garbage collection is ON
Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libSystem.B.dylib 0x00007fffffe00847 __memcpy + 167
1 libauto.dylib 0x00007fff82718170 auto_zone_write_barrier_memmove + 96
2 libauto.dylib 0x00007fff8271916e auto_realloc(_malloc_zone_t*, void*, unsigned long) + 878
3 libSystem.B.dylib 0x00007fff8346e0db malloc_zone_realloc + 92
4 com.apple.Foundation 0x00007fff83169836 _NSMutableDataGrowBytes + 652
5 com.apple.Foundation 0x00007fff83169513 -[NSConcreteMutableData appendBytes:length:] + 101
6 MY.Application 0x000000010000b9cd -[Connection stream:handleEvent:] + 376
7 com.apple.CoreFoundation 0x00007fff85742373 _signalEventSync + 115
8 com.apple.CoreFoundation 0x00007fff857422e4 _cfstream_solo_signalEventSync + 116
Что происходит, мое приложение получает данные из сети и записывает эти данные в объект NSMutableData. Я говорил об этом с некоторыми другими разработчиками, и мы полагаем, что память перегружается, что приводит к сбою.
Вопрос в том, как вы предотвращаете перегрузку памяти и как вы отлаживаете подобные ошибки в XCode?
Для полноты я также публикую код метода, который приводит к сбою.
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasSpaceAvailable: {
if (stream == outputStream) {
[self writeBufferToStream];
}
break;
}
case NSStreamEventOpenCompleted:
if (stream == inputStream) {
readReady = YES;
} else {
writeReady = YES;
}
if ([self isReadyForUse] && [delegate respondsToSelector:@selector(connectionReadyForUse:)])
[delegate connectionReadyForUse:self];
break;
case NSStreamEventHasBytesAvailable: {
if (stream == inputStream) {
int bytesRead = 0;
static uint8_t buffer[kBufferSize];
bytesRead = [inputStream read:buffer maxLength:sizeof(buffer)];
[inBuffer appendBytes:buffer length:bytesRead];
//** Process buffer contents **//
BOOL safe = YES;
while (safe) {
if (inSize <= 0) {
if ([inBuffer length] >= sizeof(uint64_t)) {
memcpy(&inSize, [inBuffer bytes], sizeof(uint64_t));
NSRange rangeToDelete = {0, sizeof(uint64_t)};
[inBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
} else {
break;
}
}
if (inSize > 0) {
if ([inBuffer length] >= inSize) {
NSMutableData *packetData = [NSMutableData dataWithBytes:[inBuffer bytes] length:inSize];
[delegate connection:self receivedData:packetData];
safe = NO;
NSRange rangeToDelete = {0, inSize};
[inBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
inSize = 0;
} else {
break;
}
} else {
break;
}
}
}
break;
}
case NSStreamEventErrorOccurred: {
NSError *theError = [stream streamError];
if (stream == inputStream)
if (delegate && [delegate respondsToSelector:@selector(connection:encounteredReadError:)])
[delegate connection:self encounteredReadError:theError];
else{
if (delegate && [delegate respondsToSelector:@selector(connection:encounteredWriteError:)])
[delegate connection:self encounteredWriteError:theError];
}
break;
}
case NSStreamEventEndEncountered: {
if (delegate && [delegate respondsToSelector:@selector(connectionDisconnected:)])
[delegate connectionDisconnected:self];
readReady = NO;
writeReady = NO;
break;
}
default:
break;
}
}