Как я могу исправить это предупреждение clang: "Объект с +0 счетчиками, возвращаемыми вызывающему абоненту, где ожидается +1 (владеющий) счетчиком"?

У меня есть кусок кода Objective-C, который выглядит следующим образом:

- (NSString *)copyData:(NSData *)data
{
    NSString *path = [[[self outputDirectory] stringByAppendingPathComponent:@"archive"] stringByAppendingPathExtension:@"zip"];
    NSLog(@"Copying data to %@", path);
    [data writeToFile:path atomically:NO];
    return path;
}

Код вызывается из инициализатора, который выглядит так:

- (id)initWithData:(NSData *)data
{
    if ((self = [super init]) != nil) {
        NSString *path = [self copyData:data];        // Line 41 (referenced in warning, shown below)
        return [self initWithContentsOfFile:path];
    }
    return self;
}

При запуске статического анализатора clang я получаю следующие предупреждения для переменной path:

Потенциальная утечка объекта, выделенного в строке 41 и сохраненная в "путь"

Объект с +0 счетчиками, возвращаемыми вызывающему абоненту, где ожидается +1 (владеющий) счетчиком

Я в замешательстве. Я понимаю, что stringByAppendingPathComponent должен возвращать строку с автореализацией, поэтому у нее должно быть значение net keep count of 0. (Очевидно, я не хочу его сохранять.)

Я попытался изменить copyData:, чтобы вернуть следующее, но не избавился от предупреждения:

return [[path retain] autorelease];

Итак, что сделка с этим предупреждением?

Ответ 1

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

В вашем случае, конечно, вы имеете в виду файлы и еще много чего, поэтому это предупреждение невежественное. Если вы измените имя своего метода на что-то вроде saveData: вместо этого, я уверен, предупреждение исчезнет.

Ответ 2

Кроме того, в тех случаях, когда вы действительно хотите назвать метод с "копией" или чем-то, потому что, независимо от Cocoa правил управления памятью, копия является лучшим именем для метода, вы можете аннотировать декларацию метода с помощью NS_RETURNS_NOT_RETAINED, а затем Кланг не даст вам предупреждения. Итак:

// Copies data from data to string; does not follow the copy rule
- (NSString*)copyData:(NSData*)data NS_RETURNS_NOT_RETAINED;

Ответ 3

Так как метод имеет в нем имя copy, анализатор ожидает, что возвращаемый объект будет иметь счет сохранения +1, в соответствии с Memory Руководство по управлению.

Ответ 4

Нет, это неверно; если метод не содержит "alloc", "copy", "new" или одно из других ключевых слов, подразумевающих, что объект будет принадлежать invoker, метод возвращает объект с автореализацией или иным образом управляемым, поэтому stringByAppendingPathComponent возвращает строку с автореализацией.

Кроме того, ваш метод "copyData" содержит слово "копия", подразумевая, что результат должен быть владельцем (и выпущен) вызывающим. Однако результат, который вы вернули, был автореализован, следовательно, сообщение об ошибке, которое оно вам дает. Если вы хотите исправить ошибку, не авторекламу. То есть:

 return [path retain]

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

Название "copyData", ИМХО, в любом случае неинтуитивно. Я бы предложил вам переименовать вашу функцию в "pathToSavedDataWithData" или тому подобное. Что-то, что говорит, что он на самом деле делает.

Ответ 5

Я собираюсь взять удар в это и предположить, что вы получите то же точное сообщение об ошибке, независимо от того, начиналось ли название вашей программы с "copy..." или нет. Я просто оказался в аналогичном сценарии, и "копия" не была частью имени той подпрограммы, которую я вызывал. Клэнг выдавал сообщение об ошибке просто потому, что возвращал автореализованный объект, опасную ситуацию. Выполнение

  return [path retain]  

трюк в конце, как рекомендовал Майкл, позаботился о проблеме.