奇怪的Xcode分析结果

时间:2011-11-03 01:43:06

标签: ios xcode static-analysis analyzer clang-static-analyzer

当我在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"

除此之外,我刚刚初始化了那个数组?我了解,如果annotationLatsannotationLongs初始化为零长度,则可能未初始化。


这些警告是否至关重要?如果我的程序按预期运行而没有这些警告,我可以安全地忽略它们吗?我觉得忽略Xcode并不是一个好主意 - 它非常聪明。

1 个答案:

答案 0 :(得分:6)

误解是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相同

因此,一个更广泛的错误检查示例可能会像以下程序一样 flow 。该程序不漂亮,可以使用一些清理/重构,但所有错误检查都存在,它没有检查器问题,并使可疑代码无法访问,并检测到大量的错误。我仍然不认为浮点比较或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]);
/* ... */
    }
}