Передача CFErrorRef в NSError (или наоборот) с помощью ARC

Я использовал для передачи NSError в CFErrorRef, как это, и используя его в SMJobBless

NSError *error
BOOL removed = SMJobRemove(kSMDomainSystemLaunchd,
                               (CFStringRef) daemonBundleID,
                               auth,
                               true,
                               (CFErrorRef*) &error);
if (!removed) {
        NSLog(@"Failed to remove existing PacketTool");
        [NSApp presentError: error];
    }

Поскольку у меня были ошибки с ARC, "Приведение косвенного указателя на указатель Obj-C на" CFErrorRef "не разрешено с помощью ARC", я изменил и решил сделать противоположное

CFErrorRef *cfError = nil;
BOOL blessed = SMJobBless(kSMDomainSystemLaunchd, (__bridge CFStringRef)daemonBundleID,
                          auth,
                          cfError);
if (!blessed) {
    NSError *error = (__bridge NSError *)cfError;
    NSLog(@"Failed to bless PacketTool: %@", error);
    [NSApp presentError: error];
    return FALSE;
}

Теперь у меня есть "Несовместимые типы, накладывающие" CFErrorRef "на NSError *" с помощью __bridge cast

Что я могу сделать?

Обновление: благодаря Грегу, теперь правильный код:

CFErrorRef cfError = nil;
BOOL blessed = SMJobBless(kSMDomainSystemLaunchd,
                          (__bridge CFStringRef) daemonBundleID,
                          auth,
                          &cfError);
if (!blessed) {
    NSError *error = (__bridge NSError *)cfError;
    NSLog(@"Failed to bless PacketTool: %@", error);
    [NSApp presentError: error];
    return FALSE;
}

Ответ 1

Когда вы объявляете cfError, вы не должны использовать указатель *, вы должны использовать:

CFErrorRef cfError = nil;
NSError *error = (__bridge NSError *)cfError;

Другим способом это работает следующим образом:

NSError *error = nil;
CFErrorRef ref = (__bridge CFErrorRef) error;

Надеюсь на эту помощь.

Ответ 2

С 7 по 13 декабря в 16:05, Том добавил:

Обновление: благодаря Грегу, теперь правильный код:

CFErrorRef cfError = nil;
BOOL blessed = SMJobBless(kSMDomainSystemLaunchd,
                          (__bridge CFStringRef) daemonBundleID,
                          auth,
                          &cfError);
if (!blessed) {
    NSError *error = (__bridge NSError *)cfError;
    NSLog(@"Failed to bless PacketTool: %@", error);
    [NSApp presentError: error];
    return FALSE;
}

Я знаю, что этому сообщению 2 года, но это неправильно, и я не хочу, чтобы другие программисты копировали этот неправильный код. Этот код утечки памяти, поскольку CFError никогда не выпускается!

CoreFoundation не имеет автоматического управления памятью, также не при использовании ARC. ARC применяется только к объектам Obj-C. И CoreFoundation не знает autorelease или autorelease пулы, поэтому объекты CoreFoundation (CFStringRef, CFNumberRef, CFErrorRef и т.д.), Которые вы получаете из функций CoreFoundation, никогда не выполняются автореализацией. Им либо не нужно вообще выпускать, либо вы можете освободить их. И в случае ошибок (CFErrorRef *), вы можете их освободить.

См. также fooobar.com/info/447912/...

Первая часть кода верна:

CFErrorRef cfError = nil;
BOOL blessed = SMJobBless(
    kSMDomainSystemLaunchd,
    (__bridge CFStringRef)daemonBundleID,
    auth, &cfError
);

Но тогда вам нужно понять мостовое литье. Простейшая форма или литье моста - это просто __bridge, и этот актер говорит, что ARC "ничего не делает". Если вы сделаете это

NSError * error = (__bridge NSError *)cfError;

Вы сообщаете ARC: "Отбрасывайте cfError до error , но не управляйте памятью ошибки после трансляции, это не ваша бизнес."

Если вы это сделаете, вы по-прежнему несете ответственность за освобождение CFErrorRef! Это означает, что после того, как вы закончите с cfError и с помощью error ( "и", поскольку оба указывают на один и тот же объект, и если они уничтожены, оба указателя становятся недействительными), вы должны сделать это:

CFRelease(cfError);

В противном случае вы теряете память!

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

NSError * error = (__bridge_transfer NSError *)cfError;

вы сообщаете ARC: "Вставьте cfError в error , а затем вы можете управлять памятью error."

Теперь вам не нужно ничего отпускать, так как как только error выходит за рамки, ARC выпустит его для вас. И поскольку error и cfError на самом деле являются одним и тем же объектом, релиз error также выпускает cfError, поэтому теперь вам не нужно ничего отпускать. Как следует из названия, этот приведение "переносит" объект в ARC. Как только это будет сделано, вы больше не должны использовать cfError, так как вы не можете точно сказать, когда именно ARC выпустит error, и как только это произойдет, cfError является недопустимым указателем, его можно легко свернуть все ваше приложение.

То же самое верно, если вы бросаете в другое направление. Если вы делаете

NSError * error = ...;
CFErrorRef cfError = (__bridge CFErrorRef)error;

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

NSError * error = ...;
CFErrorRef cfError = (__bridge_retained CFErrorRef)error;

Это говорит ARC: " Сохранить error один раз, затем отбрасывает error в cfError и никогда не балансирует это сохранение."

Таким образом, даже когда error выходит за пределы области действия, он не будет выпущен ARC, так как счетчик сохранения объекта не станет 0, он все равно будет по крайней мере 1 из-за этого приведения. Теперь вам нужно позаботиться об управлении памятью, а это значит, что после завершения работы с cfError вы должны его освободить:

CFRelease(cfError);