Странные результаты анализа Xcode

Когда я анализирую свой проект в Xcode, я получаю несколько странных ошибок. Весь этот код является частью единого метода, который создает массивы, которые можно использовать для создания MKAnnotations. Извините, если это переполнение кода — я сделал все возможное, чтобы закомментировать несвязанные части. Я включил весь метод в дополнение к фрагментам просто для некоторой перспективы. Спасибо!

- (void)addLines {
    /*

    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

    // Create region and span variables
    MKCoordinateSpan span;
    MKCoordinateRegion region;

    NSArray* arrayOuter = [defaults objectForKey:@"mapSaveDataKey"];

    NSData* data = [arrayOuter objectAtIndex:[[defaults objectForKey:@"currentMap"] intValue]];

    NSArray* arrayOne = [NSKeyedUnarchiver unarchiveObjectWithData:data];

    if (arrayOne.count == 0)
        [self returns:nil];

    */

    NSArray* overlayLat = [arrayOne objectAtIndex:1];
    double lats[overlayLat.count];

    NSArray* overlayLong = [arrayOne objectAtIndex:2];
    double longs[overlayLong.count];

    NSArray* annotationLat = [arrayOne objectAtIndex:8];
    double annotationLats[annotationLat.count];

    NSArray* annotationLong = [arrayOne objectAtIndex:9];
    double annotationsLongs[annotationLong.count];

    /*

    CLLocationCoordinate2D startLocation = CLLocationCoordinate2DMake([[overlayLat objectAtIndex:0] doubleValue], [[overlayLong objectAtIndex:0] doubleValue]);

    CLLocationCoordinate2D finishLocation = CLLocationCoordinate2DMake([[overlayLat objectAtIndex:[overlayLat count] - 1] doubleValue], [[overlayLong objectAtIndex:[overlayLong count] - 1] doubleValue]);

    NSString* string1 = [[[NSString alloc] initWithFormat:@"Lat: %.3f, Long: %.3f", [[overlayLat objectAtIndex:0] doubleValue], [[overlayLong objectAtIndex:0] doubleValue]] autorelease];

    NSString* string2 = [[[NSString alloc] initWithFormat:@"Lat: %.3f, Long: %.3f", [[overlayLat objectAtIndex:([overlayLat count] - 1)] doubleValue], [[overlayLong objectAtIndex:([overlayLong count] - 1)] doubleValue]] autorelease];

    LocationAnnotation* startAnnotation = [[LocationAnnotation alloc] initWithCoordinate:startLocation title:@"Start" subtitle:string1];

    LocationAnnotation* finishAnnotation = [[LocationAnnotation alloc] initWithCoordinate:finishLocation title:@"Finish" subtitle:string2];

    */

    for (int iii = 0; iii < overlayLat.count; iii++) {
        NSNumber* a = (NSNumber*)[overlayLat objectAtIndex:iii];
        lats[iii] = [a doubleValue];
    }

    for (int iii = 0; iii < overlayLong.count; iii++) {
        NSNumber* a = (NSNumber*)[overlayLong objectAtIndex:iii];
        longs[iii] = [a doubleValue];
    }

    for (int iii = 0; iii < annotationLong.count; iii++) {
        NSNumber* a = (NSNumber*)[annotationLong objectAtIndex:iii];
        annotationsLongs[iii] = [a doubleValue];
    }

    for (int iii = 0; iii < annotationLat.count; iii++) {
        NSNumber* a = (NSNumber*)[annotationLat objectAtIndex:iii];
        annotationLats[iii] = [a doubleValue];
    }

    int sizeLats = 0;

    for (int iii = 0; iii < overlayLat.count; iii++)
        if (lats[iii] != 0)
            sizeLats++;

    CLLocationCoordinate2D coords[sizeLats];

    for (int iii = 0; iii < sizeLats; iii++) {
        if (lats[iii] != 0 && longs[iii] != 0) {
            coords[iii].latitude = lats[iii];
            coords[iii].longitude = longs[iii];
        } else {
            coords[iii].latitude = coords[iii - 1].latitude;
            coords[iii].longitude = coords[iii - 1].longitude;
        }
    }

    /*

    // Give variables value
    span = MKCoordinateSpanMake(.05, .05);       
    region = MKCoordinateRegionMake(coords[0], span);

    MKPolyline* line = [MKPolyline polylineWithCoordinates:coords count:sizeLats];

    [mapView addOverlay:line];

    [mapView addAnnotations:[NSArray arrayWithObjects:startAnnotation, finishAnnotation, nil]];
    [mapView selectAnnotation:startAnnotation animated:YES];

    */

    for (int iii = 0; iii < annotationLong.count; iii++) {
        CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(annotationLats[iii], annotationsLongs[iii]);
        /*

        NSString* subtitle = [[[NSString alloc] initWithFormat:@"Lat: %.3f, Long: %.3f", coord.latitude, coord.longitude] autorelease];
        NSString* title = [[[NSString alloc] initWithFormat:@"Stop %i", (iii + 1)] autorelease];
        LocationAnnotation* a = [[LocationAnnotation alloc] initWithCoordinate:coord title:title subtitle:subtitle];
        CLLocation* c = [[CLLocation alloc] initWithCoordinate:coord altitude:0 horizontalAccuracy:0 verticalAccuracy:0 course:0 speed:0 timestamp:0];
        CLLocation* d = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(finishAnnotation.coordinate.latitude, finishAnnotation.coordinate.longitude) altitude:0 horizontalAccuracy:0 verticalAccuracy:0 course:0 speed:0 timestamp:0];
        if ([c distanceFromLocation:d] > 20)
            [mapView addAnnotation:a];
        [c release];
        [d release];
        [a release];

        */
    }

    /*

    [mapView setRegion:region animated:YES];

    [startAnnotation release];
    [finishAnnotation release];

    */
}

Первый:

int sizeLats = 0;

for(int iii = 0; iii < overlayLat.count; iii++)
    if(lats[iii] != 0) //"The left operand of '!=' is a garbage value"
        sizeLats++;

Как смеет Xcode обвинять меня в таких махинациях?

Второй:

CLLocationCoordinate2D coords[sizeLats]; //"Declared variable-length array (VLA) has zero size"

Третий:

for (int iii = 0; iii < sizeLats; iii++) {
    if (lats[iii] != 0 && longs[iii] != 0) { // "The left operand of '!=' is a garbage value"
        coords[iii].latitude = lats[iii];
        coords[iii].longitude = longs[iii];
    } else {
        coords[iii].latitude = coords[iii - 1].latitude; // "Assigned value is garbage or undefined"
        coords[iii].longitude = coords[iii - 1].longitude;
    }
}

Четвертое:

CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(annotationLats[iii], annotationsLongs[iii]); // "Function call argument is an uninitialized value"

Кроме того, я только что инициализировал этот массив? Я понимаю, что он может быть неинициализирован, если annotationLats и annotationLongs были инициализированы нулевой длиной.


Эти предупреждения жизненно важны? Если моя программа работает, как и ожидалось, без этих предупреждений, могу ли я их игнорировать? Я просто чувствую, что не стоит игнорировать Xcode — он довольно умный.


person eric.mitchell    schedule 03.11.2011    source источник


Ответы (1)


неправильное понимание заключается в том, что clang/checker не может гарантировать, что count массивов не изменится, и не может быть уверен, что программа будет работать так, как если бы ваши массивы были неизменяемыми.

Мой реальный вопрос заключается в том, являются ли эти предупреждения жизненно важными, и если моя программа работает, как и ожидалось, без ошибок (ну, без ошибок, связанных с этими предупреждениями: D), могу ли я безопасно игнорировать их? Я просто чувствую, что не стоит игнорировать Xcode; это довольно разумно.

Лично я не игнорирую их и действительно стараюсь свести эти проблемы к минимуму.

Вы можете немного изменить свою программу, чтобы она соответствовала потоку программы, ожидаемому компилятором/проверщиком. Самый простой способ добиться этого и удалить большинство предупреждений — ввести переменную для того, что, как вы ожидаете, не может измениться:

NSArray* overlayLat = [arrayOne objectAtIndex:1];
const NSUInteger overlayLatCount = [overlayLat count];
// now use the variable instead of calling the method where needed:
double lats[overlayLatCount];
...

Затем используйте эти переменные вместо методов. Естественно, вы должны следить за тем, чтобы создаваемые вами переменные всегда были равны значению, которое они представляют.

Есть еще несколько моментов: программа проверки не всегда знает семантику типа. Например: вы можете подумать, что счетчик NSArray может кэшироваться, но контролер должен много знать о многих классах, чтобы понять эти проблемы. Более того, программа проверки не может гарантировать, что программа будет работать так, как вы ожидаете, по многим другим причинам. Допустим, счетчик массива был кэширован в качестве оптимизации; это привело бы к множеству ошибок в реальных программах. Таким образом, вы должны сделать некоторые вещи немного более очевидными, когда у вас есть контекст, на который программа проверки может не полагаться — в некоторых случаях вам нужно представить этот контекст.

Более подробно о первом наборе проблем

NSArray * overlayLat = [arrayOne objectAtIndex:1];
const NSUInteger overlayLatCount = [overlayLat count];
double lats[overlayLatCount];

NSArray * overlayLong = [arrayOne objectAtIndex:2];
const NSUInteger overlayLongCount = [overlayLong count];
double longs[overlayLongCount];

NSArray * annotationLat = [arrayOne objectAtIndex:8];
const NSUInteger annotationLatCount = [annotationLat count];
double annotationLats[annotationLatCount];

NSArray * annotationLong = [arrayOne objectAtIndex:9];
const NSUInteger annotationLongCount = [annotationLong count];
double annotationsLongs[annotationLongCount];

for (int iii = 0; iii < overlayLatCount; iii++) {
    NSNumber * a = (NSNumber*)[overlayLat objectAtIndex:iii];
    lats[iii] = [a doubleValue];
}

for (int iii = 0; iii < overlayLongCount; iii++) {
    NSNumber * a = (NSNumber*)[overlayLong objectAtIndex:iii];
    longs[iii] = [a doubleValue];
}

for (int iii = 0; iii < annotationLongCount; iii++) {
    NSNumber * a = (NSNumber*)[annotationLong objectAtIndex:iii];
    annotationsLongs[iii] = [a doubleValue];
}

for (int iii = 0; iii < annotationLatCount; iii++) {
    NSNumber * a = (NSNumber*)[annotationLat objectAtIndex:iii];
    annotationLats[iii] = [a doubleValue];
}

Подробная информация об оставшихся проблемах

1) Заявленный VLA имеет нулевой размер:

Значение: чекер видит, что sizeLats инициализируется нулем, и увеличивается только условно. Он предупреждает вас, что длина массива может быть равна нулю.

Решение: проверьте на ноль, прежде чем продолжить:

if (0 == sizeLats) return;

CLLocationCoordinate2D coords[sizeLats];

Это один из способов гарантировать компилятору, что вы не будете создавать или использовать VLA нулевой длины.

2) Левый операнд != является мусорным значением:

if ((lats[iii] != 0) && (longs[iii] != 0)) {

Значение: программа проверки не может гарантировать, что массив когда-либо был полностью инициализирован и что sizeLats меньше, чем [overlayLat count] и [overlayLong count]. Что еще более важно, он должен предупреждать о потенциальном доступе вне диапазона. Это сообщение не очень понятно.

Решение: На самом деле в программе есть несколько непроверенных границ и допущений (и, возможно, некоторая проверка ошибок была удалена). Это нужно сделать более понятным для проверки и компилятора. Вы должны сделать это более понятным для компилятора и добавить обнаружение ошибок на всякий случай (предоставит более полный пример).

Дополнительные примечания - сравнения с плавающей запятой не очень безопасны - вы не должны ожидать, что массивы C будут инициализированы

3) Аргумент вызова функции является неинициализированным значением.

Значение: происхождение этой проблемы такое же, как и в № 2 "Левый операнд != является мусорным значением". Массив по указанному индексу, возможно, не был инициализирован. Худшая проблема снова заключается в том, что нет гарантии, что индекс массива будет в диапазоне. Тот факт, что он не помечен как таковой, заставляет задуматься, не отключила ли программа проверки это предупреждение из-за слишком большого количества ложных срабатываний.

Разрешение: то же, что и № 2

Таким образом, более тщательный пример с обширной проверкой ошибок может походить наподобие следующей программы. Программа не очень красивая и может потребовать некоторой очистки/рефакторинга, но вся проверка ошибок существует, она проходит без проблем с проверкой, делает сомнительный код недоступным и обнаруживает большое количество ошибок. Я по-прежнему не считаю сравнения с плавающей запятой или VLA хорошими, но этого должно быть достаточно, чтобы решить ваш вопрос.

/**
  @brief fills a double buffer with values from an NSArray
  @param destinationBuffer the buffer to fill
  @param countOfDestinationBuffer the number of elements in @a destinationBuffer
  @param the source values. an NSArray filled with NSNumber objects. @a countOfDestinationBuffer must be equal to @a [source count]
  @return false if an error occurred, else true
*/
static bool FillDoubleArrayFromNSArray(double* const destinationBuffer, const NSUInteger countOfDestinationBuffer, NSArray* source) {
    const NSUInteger sourceCount = [source count];
    if ((0 == destinationBuffer) || (0 == countOfDestinationBuffer) || (0 == [source count])) {
        assert(0 && "invalid argument");
        return false;
    }
    else if (sourceCount != countOfDestinationBuffer) {
        assert(0 && "buffer size mismatch");
        return false;
    }

    for (NSUInteger idx = 0; idx < sourceCount; ++idx) {
        NSNumber* a = (NSNumber*)[source objectAtIndex:idx];
        destinationBuffer[idx] = [a doubleValue];
    }

    return true;
}

- (void)addLines {
    NSArray* arrayOne = [NSArray array];

    NSArray* overlayLatArray = [arrayOne objectAtIndex:1];
    const NSUInteger overlayLatCount = [overlayLatArray count];
    if (0 == overlayLatCount) {
        assert(0 && "empty array or invalid object. bailing.");
        return;
    }

    double lats[overlayLatCount];
    if (!FillDoubleArrayFromNSArray(lats, overlayLatCount, overlayLatArray)) {
    /* do something */
        return;
    }

    NSArray* overlayLongArray = [arrayOne objectAtIndex:2];
    const NSUInteger overlayLongCount = [overlayLongArray count];
    if (0 == overlayLongCount) {
        assert(0 && "empty array or invalid object. bailing.");
        return;
    }

    double longs[overlayLongCount];
    if (!FillDoubleArrayFromNSArray(longs, overlayLongCount, overlayLongArray)) {
    /* do something */
        return;
    }

    NSUInteger sizeLat = 0;
    for (NSUInteger idx = 0; idx < overlayLatCount; ++idx) {
        if (lats[idx] != 0) {
            ++sizeLat;
        }
    }

    if (0 == sizeLat) {
        assert(0 && "what to do when no lines can be drawn?");
        return;
    }

    if ((overlayLatCount < sizeLat) || (overlayLongCount < sizeLat)) {
        assert(0 && "input range error (overlayLongCount is really what we are testing here)");
        return;
    }

    CLLocationCoordinate2D coords[sizeLat];
    for (NSUInteger idx = 0; idx < sizeLat; ++idx) {
        if ((lats[idx] != 0) && (longs[idx] != 0)) {
            coords[idx] = CLLocationCoordinate2DMake(lats[idx], longs[idx]);
        }
        else if (0 == idx) {
            assert(0 && "range error. access of invalid index would have occurred");
            return;
        }
        else {
            coords[idx] = coords[idx - 1];
        }
    }

    NSArray* annotationLatArray = [arrayOne objectAtIndex:8];
    const NSUInteger annotationLatCount = [annotationLatArray count];
    if (0 == annotationLatCount) {
        assert(0 && "empty array or invalid object. bailing.");
        return;
    }

    double annotationLat[annotationLatCount];
    if (!FillDoubleArrayFromNSArray(annotationLat, annotationLatCount, annotationLatArray)) {
    /* do something */
        return;
    }

    NSArray* annotationLongArray = [arrayOne objectAtIndex:9];
    const NSUInteger annotationLongCount = [annotationLongArray count];
    if (0 == annotationLongCount) {
        assert(0 && "empty array or invalid object. bailing.");
        return;
    }

    double annotationLong[annotationLongCount];
    if (!FillDoubleArrayFromNSArray(annotationLong, annotationLongCount, annotationLongArray)) {
    /* do something */
        return;
    }

    if (annotationLatCount < annotationLongCount) {
        assert(0 && "input range error in next loop. bailing");
        return;
    }

    for (NSUInteger idx = 0; idx < annotationLongCount; ++idx) {
        CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(annotationLat[idx], annotationLong[idx]);
/* ... */
    }
}
person justin    schedule 03.11.2011
comment
@Bavarious yup - мы снова ввели эту деталь одновременно =) - person justin; 03.11.2011
comment
Я сделал так: NSArray* overlayLat = [arrayOne objectAtIndex:1]; const NSUInteger sizeOverlayLat = [счетчик overlayLat]; двойные латы[sizeOverlayLat]; для каждого массива и заменил overLat.count на sizeOverlayLat, но все равно получаю те же ошибки - person eric.mitchell; 03.11.2011
comment
@Rickay Какую версию анализатора вы используете? Я не получаю предупреждений с помощью checker-258. - person ; 03.11.2011
comment
@Rickay хммм... посмотрю, смогу ли я скомпилировать твой пример - brb - person justin; 03.11.2011
comment
@Rickay только что проверил предложение overlayLatCount - он удалил предупреждения, связанные с этим значением, с помощью средства проверки Xc4.2. Это не исправило несвязанные предупреждения... - person justin; 03.11.2011
comment
@Bavarious Я не совсем уверен - где я могу это увидеть? - person eric.mitchell; 03.11.2011
comment
И что именно означают «другие предупреждения»? Я искоренил все свои проблемы с управлением памятью, но мое ОКР заставляет меня ДЕЙСТВИТЕЛЬНО избавиться от этих других. К тому же, наверное, это правильно. - person eric.mitchell; 03.11.2011
comment
Это означает, что есть ряд проблем с анализатором, и я не все их подробно описал. То, что я подробно описал, исчезло с шашкой Xc4.2. - person justin; 03.11.2011
comment
Я имел в виду, что означают другие предупреждения (например, «левый операнд != является мусорным значением»)? - person eric.mitchell; 03.11.2011