11 Stimmen

Objective-C mit ARC: Benutzerdefinierter Setter wird nicht beibehalten

HINWEIS: Dies war auf einen Fehler in einigen XCode-Betaversionen zurückzuführen, der längst behoben ist. Diese Frage und Antwort wird Ihnen wahrscheinlich nicht helfen, wenn Sie Probleme mit ARC haben.


Ich bin dabei, mein Projekt von der manuellen Referenzzählung auf ARC umzustellen, und bin dabei auf ein Problem gestoßen: Wie kann ich sicherstellen, dass ein benutzerdefinierter Setter für eine Retain-Eigenschaft tatsächlich beibehalten wird?

Unter myClass.h habe ich eine Eigenschaft deklariert: @property (retain) NSDate *date . Es spielt keine Rolle, ob ich manuell eine __strong ivar oder lassen Sie es automatisch generieren.

Bei der Umsetzung habe ich natürlich @synthesize date und implementierte einen benutzerdefinierten Setter (oder einfach das Xcode-Demoprojekt herunterladen ) :

- (void)setDate:(NSDate *)newDate
{
  if (allowedToSetNewDate)
  {
    date = newDate;
  }
}

Dies scheint das Datum nicht beizubehalten und führt dazu, dass ich message sent to deallocated instance wenn newName (auto-) freigegeben wird, woher er kam, wenn versucht wird, auf myClass.date später (vorausgesetzt, Zombie ist aktiviert; andernfalls stürzt es einfach ab).

Ändern des Setters zur Verwendung von date = [newDate copy] umgeht den Fehler, ist aber nicht wirklich das, was ich will. Das Löschen des benutzerdefinierten Setters funktioniert auch, ist aber offensichtlich nicht erwünscht.

Was übersehe ich hier? Wie kann ich sicherstellen, dass ein benutzerdefinierter Setter für eine Retain-Eigenschaft tatsächlich in einer ARC-Umgebung beibehält? Dies scheint so eine grundlegende und gemeinsame Aufgabe, die ich denke, ich bin etwas sehr offensichtlich übersehen.

(HINWEIS: Dies fällt nicht unter die Bedingungen eines Apple NDA, da ARC als Teil von LLVM öffentlich freigegeben ist)

EDIT: Ich habe ein kleines Xcode-Projekt erstellt, um das Problem zu demonstrieren und es auf github hochgeladen. Fühlen Sie sich frei zu Herunterladen und herumspielen. Ich bin mit meinem Verstand am Ende (obwohl mein Verstand heute zugegebenermaßen nicht der beste ist).

EDIT: Für dieses Beispielprojekt ist dieses Problem gelöst (siehe akzeptierte Antwort). Leider besteht das Problem in dem größeren Projekt, das ich nicht freigeben darf, weiterhin. Als Workaround habe ich ein Duplikat hinzugefügt strong Eigenschaften mit synthetischen Setzern (ivars funktionieren nicht). Der neue benutzerdefinierte Setter sieht nun wie folgt aus:

- (void)setDate:(NSDate *)newDate
{
  if (allowedToSetNewDate)
  {
    self.date_arcretain = newDate; //this property is only there as a workaround. ARC properly retains it, but only if the setter is synthesized
    date = newDate;
  }
}

12voto

BJ Homer Punkte 48238

Das sieht für mich nach einem Fehler aus; Ihr Code sollte in Ordnung sein. Wenn Sie es noch nicht getan haben, reichen Sie einen Fehler unter http:// bugreport.apple.com ein und fügen Sie Ihr Beispielprojekt bei.

Editar : Bei näherer Betrachtung Ihres Beispielprojekts stellt sich heraus, dass es sich nicht um einen Fehler handelt.

Das überfremdete Objekt in Ihrem Beispielprojekt ist noNSDate Instanz. Sie können die tc.date = now Aufruf in Ihrem Beispielprojekt vollständig, und Sie werden immer noch den gleichen Absturz zu sehen. Sie können das NSDate-Zeug sogar ganz weglassen. Das übermäßig freigegebene Objekt ist eigentlich das TestVC Objekt selbst.

Es geht um Folgendes.

In iOS 4.0, UIWindow hat eine rootViewController Eigentum. Wo Sie früher einfach die [self.window addSubview:myRootcontroller.view] Beim Starten der Anwendung bedeutet diese Änderung nun, dass das Fenster tatsächlich einen Verweis auf den Root-View-Controller hat. Dies ist wichtig für die Weitergabe von Rotationsbenachrichtigungen usw. Ich glaube, dass UIWindow in der Vergangenheit automatisch versucht hat, den RootViewController zu setzen, wenn die erste Unteransicht hinzugefügt wurde (wenn sie nicht bereits gesetzt war), aber in Ihrem Beispielprojekt ist das eindeutig nicht der Fall. Das kann an der Art und Weise liegen, wie Sie die Ansicht erstellen, oder es kann an einer Änderung in iOS 5.0 liegen. So oder so, es war nie dokumentiert Verhalten, so können Sie nicht darauf verlassen, es geschieht.

In den meisten Fällen wird Ihr App-Delegat einen Ivar haben, der auf den Root-View-Controller zeigt. Das ist nicht unbedingt erforderlich, aber in der Regel der Fall. In dem von Ihnen vorgestellten Beispielprojekt ist der Viewcontroller jedoch nicht im Besitz des App-Delegaten. Auch haben Sie ihn nicht als Root-Viewcontroller des Fensters festgelegt. Infolgedessen wird am Ende des -application:didFinishLaunchingWithOptions: Methode, gibt es nichts mit einem starken Verweis auf den View-Controller verlassen. Ihr App-Delegierter hält sich nicht daran, und das Fenster selbst hält sich nicht daran. Als solche, ARC behandelt es als eine lokale Variable (was es ist), und gibt es am Ende der Methode.

Das ändert natürlich nichts an der Tatsache, dass Ihr UIButton hat immer noch eine Aktionsmethode, die auf Ihren Controller abzielt. Aber wie in der Dokumentation erwähnt, -addTarget:action:forControlEvents: behält das Ziel nicht bei. Daher ist die UIButton hat einen baumelnden Verweis auf Ihren View-Controller, der nun freigegeben wurde, da niemand einen starken Verweis auf ihn hat. Daher der Absturz.

Die Lösung für dieses Problem besteht darin, diese Zeile in Ihrem App-Delegat zu ändern:

[self.window addSubview:tc.view];

dazu:

self.window.rootViewController = tc;

Mit dieser einzigen Änderung funktioniert jetzt alles einwandfrei.

Editar : Vergewissern Sie sich auch, dass die Einstellung "Code für ARC-Migration vorprüfen" nicht aktiviert ist, da dies dazu führen würde, dass der Compiler den Code als manuell verwaltet behandelt, und dies würde nicht die richtigen Retain/Release-Aufrufe einfügen.

2voto

Wolfgang Schreurs Punkte 11699

Ich habe das Problem auf meinem Mac mit Ihrem Projekt behoben. Der Fehler liegt in Ihrem AppDelegate, nicht in Ihrer Datumseigenschaft. Machen Sie den View Controller zu einer retainable Eigenschaft in AppDelegate. Derzeit ist Ihr View-Controller automatisch freigegeben, einschließlich aller Eigenschaften.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X