Глубокая копия словарей дает ошибку анализа в Xcode 4.2 - PullRequest
3 голосов
/ 14 октября 2011

У меня есть следующий метод в категории NSDictionary, чтобы сделать глубокую копию, которая прекрасно работает.

Я только что обновил Xcode 4.1 до 4.2, и функция Analyze выдает два предупреждения анализатора для этого кода, как указано:

- (id)deepCopy;
{
    id dict = [[NSMutableDictionary alloc] init];
    id copy;

    for (id key in self)
    {
        id object = [self objectForKey:key];

        if ([object respondsToSelector:@selector(deepCopy)])
            copy = [object deepCopy];
        else
            copy = [object copy];

        [dict setObject:copy forKey:key];

        // Both -deepCopy and -copy retain the object, and so does -setObject:forKey:, so need to -release:
        [copy release];  // Xcode 4.2's Analyze says this is an incorrect decrement of the reference count?!
    }

    return dict;  // Xcode 4.2's Analyze says this is a potential leak
}

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

Я пока не использую ARC, хотя мне интересно, есть ли дополнительные изменения, необходимые для поддержки ARC для этого метода.

Ответы [ 2 ]

11 голосов
/ 14 октября 2011

Предположительно, это потому, что deepCopy не начинается с префикса copy.

Так что вы можете захотеть изменить что-то вроде copyWithDeepCopiedValues (или что-то подобное), а затем посмотрите, помечает ли анализатор это.

Обновление

Как отметил Александр, вы можете использовать атрибуты для обозначения цели подсчета ссылок.Это (ИМО) должно быть исключением из правила и использоваться редко, если вообще когда-либо.Лично я не буду использовать атрибуты для методов objc, потому что они хрупкие.

Единственный атрибут, который я использовал до сих пор, был consume, и каждый раз, когда я использую эти атрибуты, был в статически типизированных контекстах (например,Функции C и функции и методы C ++.

Причины, по которым следует избегать атрибутов, когда это возможно:

1) Придерживайтесь соглашений для программистов.Код более понятен, и вам не нужно обращаться к документации.

2) Подход хрупок.Вы по-прежнему можете вводить дисбалансы в счетчиках ссылок, а атрибуты можно использовать для введения ошибок сборки из-за конфликтов в атрибутах.

Все следующие случаи построены с включенной ARC:

Case #1

#import <Foundation/Foundation.h>

@interface MONType : NSObject

- (NSString *)string __attribute__((objc_method_family(copy)));

@end

@implementation MONType

- (NSString *)string
{
    NSMutableString * ret = [NSMutableString new];
    [ret appendString:@"MONType"];
    return ret;
}

@end

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        id obj = nil;
        if (random() % 2U) {
            obj = [[NSAttributedString alloc] initWithString:@"NSAttributedString"];
        }
        else {
            obj = [MONType new];
        }
        NSLog(@"Result: %@, %@", obj, [obj string]);
    }
    /* this tool's name is ARC, dump the leaks: */
    system("leaks ARC");
    return 0;
}

Эта программа выдает следующую ошибку: error: multiple methods named 'string' found with mismatched result, parameter type or attributes.

Отлично, компилятор делает все возможное, чтобы предотвратить эти проблемы.Это означает, что конфликты в атрибутах могут приводить к ошибкам на основе перевода.Это плохо , потому что, когда нетривиальные кодовые базы объединяются и атрибуты конфликтуют, у вас будут ошибки для исправления и программы для обновления.Это также означает, что простое включение других библиотек в единицы перевода может привести к поломке существующих программ при использовании атрибутов.

Case # 2

Header.h

extern id NewObject(void);

Header.m

#import <Foundation/Foundation.h>
#import "Header.h"

@interface MONType : NSObject

- (NSString *)string __attribute__((objc_method_family(copy)));

@end

@implementation MONType

- (NSString *)string
{
    NSMutableString * ret = [NSMutableString new];
    [ret appendString:@"-[MONType string]"];
    return ret;
}

@end


id NewObject(void) {
    id obj = nil;
    if (random() % 2U) {
        obj = [[NSAttributedString alloc] initWithString:@"NSAttributedString"];
    }
    else {
        obj = [MONType new];
    }
    return obj;
}

main.m

#import <Foundation/Foundation.h>
#import "Header.h"

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        for (size_t idx = 0; idx < 8; ++idx) {
            id obj = NewObject();
            NSLog(@"Result: %@, %@", obj, [obj string]);
        }
    }
    /* this tool's name is ARC, dump the leaks: */
    system("leaks ARC");
    return 0;
}

Ok.Это просто плохо .Мы ввели утечки, потому что в блоке перевода не было необходимой информации.Вот отчет об утечках:

leaks Report Version:  2.0
Process 7778: 1230 nodes malloced for 210 KB
Process 7778: 4 leaks for 192 total leaked bytes.
Leak: 0x1005001f0  size=64  zone: DefaultMallocZone_0x100003000   __NSCFString  ObjC  CoreFoundation  mutable non-inline:  "-[MONType string]"
Leak: 0x100500320  size=64  zone: DefaultMallocZone_0x100003000   __NSCFString  ObjC  CoreFoundation  mutable non-inline:  "-[MONType string]"
Leak: 0x100500230  size=32  zone: DefaultMallocZone_0x100003000  has-length-byte:  "-[MONType string]"
Leak: 0x100500390  size=32  zone: DefaultMallocZone_0x100003000  has-length-byte:  "-[MONType string]"

примечание: количество может отличаться, потому что мы использовали random()

Это означает, что, поскольку MONType не виден для main(), привязка компиляторасвойства ARC для методов, которые были видны текущему TU (то есть string из объявлений в Foundation, все из которых следуют соглашению).В результате компилятор ошибся, и мы смогли внести утечки в нашу программу.

Случай 3

Используя аналогичный подход, я также смогввести дисбаланс отрицательного количества ссылок (преждевременные выпуски или сообщение зомби).

примечание: код не предоставлен, поскольку в случае № 2 уже показано, как можно достичь дисбаланса в счетчике ссылок.

Заключение

Вы можете избежать всех этих проблем и улучшить удобочитаемость и удобство обслуживания, придерживаясь соглашения, а не используя атрибуты.

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

6 голосов
/ 14 октября 2011

Добавляя к ответу @ Джастина, вы можете сказать компилятору, что -deepCopy возвращает сохраненный объект , добавив атрибут NS_RETURNS_RETAINED к объявлению метода следующим образом:

- (id) deepCopy NS_RETURNED_RETAINED;

В качестве альтернативы, вы можете использовать для явного управления «семейством» методов , используя атрибут objc_method_family, например:

- (id) deepCopy __attribute__((objc_method_family(copy)));

Если вы сделаете это, компилятор узнает, что этот метод относится к семейству copy, и вернет скопированное значение.

...