11

Ich bin dabei, mein Projekt in ARC umzuwandeln. Ich habe eine Kategorie auf NSColor mit einer Methode, die eine Autoreleased CGColor Darstellung zurück:Convert-Methode, die eine automatisch freigegebene CGColor in ARC zurückgibt

@implementation NSColor (MyCategory) 

- (CGColorRef)CGColor 
{ 
    NSColor *colorRGB = [self colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; 
    CGFloat components[4]; 
    [colorRGB getRed:&components[0] 
       green:&components[1] 
       blue:&components[2] 
       alpha:&components[3]]; 
    CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); 
    CGColorRef theColor = CGColorCreate(space, components); 
    CGColorSpaceRelease(space); 
    return (CGColorRef)[(id)theColor autorelease]; 
} 

@end 

Was ist der richtige Weg, dies mit ARC zu tun? Ich möchte keine beibehaltene CGColor zurückgeben.

Der Konverter ARC in XCode empfehlen

return (CGColorRef)[(__bridge id)theColor autorelease]; 

verwenden, aber, dass die Ergebnisse in der folgenden Fehlermeldung:

[rewriter] es nicht sicher ist, zu 'CGColorRef' das Ergebnis zu werfen 'Autorelease'-Nachricht; ein __bridge Guss in einem Zeiger auf ein Objekt zerstört führen kann und ein __bridge_retained

das Objekt austreten Objekt
+2

Sie haben hier ein ziemlich schönes Speicherleck. CGColorCreate erstellt ein CGColor-Objekt, das bei jedem Aufruf dieser Methode im Speicher gehalten wird. Ich empfehle sehr, etwas zu tun wie: 'CGColorRef colorRef = CGColorCreate (colorSpaceRGB, Komponenten); UIColor * retColor = [UIColor colorWithCGColor: colorRef]; CGColorRelease (colorRef); return retColor; ' – jfeldman

+2

Haben Sie die Frage gelesen? Ich weiß, dass es undicht ist, das ist mein Problem. Ich wollte eine CGColor, keine UIColor (es ist sowieso eine OS X Frage, wie Sie durch meine Erwähnung von NSColor sagen konnten) zurückgeben. Jedenfalls wurde das vor einem halben Jahr beantwortet. – DrummerB

Antwort

7

CGColor ist eine Kern Foundation. Sie sollten nicht versuchen, autorelease damit zu verwenden. Stattdessen sollten Sie Ihre Methode copyCGColor umbenennen und ein beibehaltenes Objekt zurückgeben.

Auto-Release ist ein Objective-C-Konzept. Es existiert nicht auf der Ebene der Core Foundation.

Da CGColor nicht gebührenfrei zu irgendeiner Objective-C-Klasse überbrückt ist, ist es sehr seltsam zu versuchen, es automatisch zu veröffentlichen (auch wenn das funktionieren könnte). später

-Update ein paar Jahre

Es ist jetzt CFAutorelease() auf der Corefoundation Ebene (verfügbar seit Mavericks und iOS 7).

+5

Seltsamerweise kann es sein, sagt NSColor.h von '-CGColor':" Gibt eine automatisch freigegebene CGColor zurück. " –

+0

Verfügbar in OS X 10.9 (und iOS 7), gibt es jetzt eine CFAutorelease() -Funktion. – NSDestr0yer

+0

Können Sie ein Beispiel geben? Ich habe 'CF_RETURNS_RETAINED' zu meinem Eigentum hinzugefügt, aber der statische Analysator klagt immer noch über ein mögliches Leck ... – NSAddict

0

Ich denke, dass Sie __bridge_transfer in diesem Fall verwenden möchten.

Docs

8

Im Wesentlichen ist es, weil es keine gute Möglichkeit ist, den folgenden Code in ARC zu konvertieren:

CGColorRef a = ...; 
id b = [(id)a autorelease]; 
CGColorRef c = (CGColorRef)b; 
// do stuff with c 

Der Konverter entfernt -autorelease und fügt einige Abgüsse überbrückt, aber es stecken bleibt:

CGColorRef a = ...; 
id b = (__bridge_transfer id)a; 
CGColorRef c = (__bridge_SOMETHING CGColorRef)b; 
// do stuff with c. Except the compiler sees that b is no longer being used! 

Aber was sollte der Migrator für __bridge_SOMETHING tun?

  • Wenn es __bridge nimmt, dann wird b nicht mehr verwendet, so kann der Compiler sofort loslassen. Dies stürzt ab.
  • Wenn es __bridge_retained wählt, dann wird das Eigentum zurück auf "CF-Land" übertragen, aber der ursprüngliche Code nahm an, dass das Objekt dem Autorelease-Pool gehören würde. Der Code ist jetzt undicht.

Das Problem ist, dass ARC verbietet -autorelease Aufruf aber nicht über ein dokumentiertes Verfahren muss zu gewährleisten, dass ein Objekt auf den Autofreigabepool hinzugefügt wird - der einzigen Grund, dies zu tun, um eine Autoreleased CF Typen von einem Verfahren zurückzukehren, aber viel von UIKit Klassen haben Eigenschaften-CF eingegeben (und MKOverlayPathView hat eine AtomCGPathRef Eigenschaft, die muss einen Autoreleased Wert zurückgeben).

Dies ist einer der kniffligen Teile von ARC, die ich wirklich wünschte, wurde besser dokumentiert.

Es gibt ein paar Reifen, durch die man springen kann, die mit unterschiedlichem Erfolg funktionieren könnten. Um die zunehmende ickiness:

  1. definieren CFAutorelease() Funktion in einer Datei ohne ARC zusammengestellt (add -fno-objc-arc zu den Compiler-Flags in Zielsystemeinstellungen → Build-Phasen → Compile Quellen). Ich überlasse das dem Leser als Übung. Dies funktioniert, weil ARC-Code mit MRC-Code zusammenarbeiten muss. Dies ist wahrscheinlich die sauberste Lösung. (Dies zieht zwangsläufig einen Kommentar nach sich, der besagt, dass das CF-Präfix nicht verwendet werden sollte, aber solange Sie keinen Link-Fehler sehen, sind C-Symbol-Namenskonflikte im Allgemeinen sicher, weil der "zweistufige Namespace" eingeführt wurde in 10,3 oder so.)

  2. Verschiedene Reifen, um es eine Nachricht oder gleichwertiges zu senden. Alle diese sind ein bisschen unordentlich, weil sie ARC "täuschen", außer das letzte, das id voraussetzt, ist ABI-kompatibel mit void*. Sie sind wahrscheinlich auch langsamer als die oben genannten, weil sie eine Klasse/Selektor nachschlagen müssen (objc_lookUpClass() und könnte schneller sein oder sogar optimiert weg, aber ich würde nicht darauf wetten).

    return (__bridge CGColorRef)[(__bridge id)theColor performSelector:NSSelectorFromString(@"autorelease")] 
    
    [NSClassFromString(@"NSAutoreleasePool") addObject:(__bridge id)theColor] 
    return theColor; 
    
    return (__bridge CGColorRef)((id(*)(id,SEL))objc_msgSend)((__bridge id)theColor,NSSelectorFromString(@"autorelease")); 
    
    return ((void*(*)(void*,SEL))objc_msgSend)(theColor,NSSelectorFromString(@"autorelease")); 
    
  3. Kraft es durch die Zuordnung zu einer __autoreleasing Variable auf den Autofreigabepool hinzugefügt werden, dass der Compiler entfernt nicht optimieren können. Ich bin mir nicht sicher, ob dies garantiert ist (insbesondere ist etwas ähnlich wie objc_autoreleaseReturnValue() und objc_retainAutoreleasedReturnValue() möglich, aber ich denke, das ist unwahrscheinlich, da es den häufigen Fall von (NSError * __autoreleasing *)error verlangsamen würde).

    -(id)forceAutorelease:(id)o into:(id __autoreleasing*)p 
    { 
        *p = o; 
        return p; 
    } 
    
    -(CGColorRef)CGColor 
    { 
        ... 
        CGColorRef theColor = CGColorCreate(...); 
        CGColorSpaceRelease(space); 
        id __autoreleasing temp; 
        return (__bridge CGColorRef)[self forceAutorelease:(__bridge_transfer id)theColor into:&temp]; 
    } 
    

    (Es könnte auch möglich sein, dass der Compiler/runtime statische Dispatch/inlining zu kooperieren und verwenden, bis die relevanten Methoden außer Kraft gesetzt werden, aber das scheint kompliziert und nicht ohne erheblich Gemeinkosten ihre eigenen.)

  4. Verwenden Sie eine typedef mit __attribute__((NSObject)). Dies ist die verwechslungsfähig dokumentiert Teile des ARC spec, aber so etwas wie dieses scheint Arbeit:

    typedef CGColorRef MyCGColorRef __attribute__((NSObject)); 
    -(MyCGColorRef)CGColor 
    { 
        ... 
        return (__bridge MyCGColorRef)(__bridge_transfer id)theColor; 
    } 
    

    ich glaube, Sie müssen zwei Brücken für diese arbeiten (ein Eigentum an ARC und ein anderes zu übertragen); Wenn Sie einfach return theColor; Ich vermute, dass es durchgesickert ist. Von meinem Lesen der Dokumente, Sie sollte brauchen nur (__bridge_transfer MyCGColorRef), weil es von einem Nicht-ARC-Zeiger (CGColorRef) in einen ARC-Zeiger (MyCGColorRef) konvertiert, aber das macht den Compiler beschweren. Leider geben die Dokumente keine Beispiele, wie man __attribute__((NSObject)) typedefs verwendet.

    Beachten Sie, dass Sie den Rückgabetyp in der Kopfzeile nicht ändern müssen. Dies kann die automatische Rückgabe von Rückgabewerten ermöglichen, aber ich bin nicht sicher, wie der Compiler die Konvertierung von MyCGColorRef zu CGColorRef handhabt. Le Seufzer.

1

Tat im manuellen Speicherverwaltung Sie können retain, release und autorelease jede Corefoundation Objekt, weil sie alle gebührenfrei überbrückt mindestens NSObject sind.

Da ARC die Verwendung der manuellen Speicherverwaltung verbietet, sollten wir irgendwie den Compiler sagen, was zu tun ist. Eine Möglichkeit besteht darin, Ihre Methode - (CGColorRef)copyCGColor; zu benennen, damit der Compiler weiß, dass die Methode das Objekt mit +1 retain count zurückgibt.

Allerdings, wenn Sie wie ich sind und es vorziehen, plain „CGColor“ für solche Methoden, können Sie einfach __attribute__((cf_returns_retained)) an die Methodendefinition anhängen:

@interface NSColor (MyCategory) 

- (CGColorRef)CGColor __attribute__((cf_returns_retained)); 

@end 
4

seit OS X 10.9 oder iOS 7 einfach CFAutorelease() verwenden können (erklärt in CFBase.h).

Verwandte Themen