Objective-C ARC und übergeben C-Arrays von Objekten

8

Es tut mir leid, wenn das ein bisschen wie eine C-noob-Frage ist: Ich weiß, dass ich meine Zeiger aufschlüsseln muss. Leider habe ich eine Deadline, also habe ich keine Zeit, ein ganzes Buchkapitel abzuarbeiten, also hoffe ich auf etwas gezielteren Rat.

Ich möchte einige Objective-C-Objekte in einem C-Array speichern. Ich benutze ARC. Wenn ich auf dem Mac wäre, könnte ich stattdessen NSPointerArray verwenden, aber ich bin auf iOS und das ist nicht verfügbar.

Ich werde ein dreidimensionales C-Array speichern: konzeptionell sind meine Dimensionen Tag, Höhe und Cache-Nummer. Jedes Element ist entweder ein Zeiger auf ein Ziel-C-Objekt oder NULL.

Die Anzahl der Caches (d. h. die Größe der cacheNumber-Dimension) ist zur Kompilierzeit bekannt, die anderen beiden sind jedoch nicht bekannt. Außerdem könnte das Array sehr groß sein, daher muss ich dynamisch Speicher dafür reservieren.

Bezüglich der Besitz-Semantik benötige ich starke Referenzen zu den Objekten.

Ich möchte, dass das ganze dreidimensionale Array eine Instanzvariable auf einem Objekt-C-Objekt ist.

Ich plane, eine Methode zu haben, die - tableForCacheNumber:(int)num days:(int*)days height:(int*)height ist. Diese Methode sollte ein zweidimensionales Array zurückgeben, das ist eine bestimmte Cache-Nummer. (Es wird auch die Größe des Arrays zurückgegeben, das zurückgegeben wird.)

Meine Fragen:

  • In welcher Reihenfolge sollte ich meine Dimensionen setzen, damit ich einfach einen Zeiger auf das Sub-Array für eine bestimmte Cache-Nummer zurückgeben kann? (Ich denke, es sollte zuerst sein, aber ich bin nicht 100%.)

  • Was sollte der Rückgabetyp meiner Methode sein, damit sich ARC nicht beschwert? Es macht mir nichts aus, wenn das zurückgegebene Array eine erhöhte Referenzanzahl hat oder nicht, solange ich weiß, was es tut.

  • Welchen Typ sollte meine Instanzvariable, die das dreidimensionale Array enthält, sein? Ich denke, es sollte nur ein Zeiger sein, da dieser nur den Zeiger auf das erste Element in meinem Array darstellt. Richtig? Wenn ja, wie kann ich das angeben?

  • Wenn ich das dreidimensionale Array (für meinen ivar) erstelle, mache ich etwas wie calloc(X * Y * Z, sizeof(id)) und greife das Ergebnis auf den Typ für meinen ivar?

  • Beim Zugriff auf Objekte aus dem dreidimensionalen Array im ivar glaube ich, dass ich den Zeiger jedes Mal dereferenzieren muss, mit etwas wie (*myArray)[4][7][2] . Korrekt?

  • Wird auf das zweidimensionale Array, das ich von der Methode zurückgebe, ähnlich zugegriffen?

  • Muss ich das zurückgegebene zweidimensionale Array mit objc_returns_inner_pointer ?

  • kennzeichnen?

Es tut mir leid noch einmal, dass dies eine schlechte Stack Overflow Frage ist (es ist zu lang und mit zu vielen Teilen). Ich hoffe, die SO-Bürger werden mir vergeben. Um mein Interweb-Karma zu verbessern, werde ich es vielleicht als Blogbeitrag schreiben, wenn dieses Projekt ausgeliefert wurde.

    
Amy Worrall 26.03.2012, 09:44
quelle

3 Antworten

3

Ich habe meine eigene Frage beantwortet, weil diese Webseite mir die fehlenden Informationen geliefert hat, die ich brauchte. Ich habe auch Grahams Antwort aufgefrischt, da er sehr hilfreich war, um mich mit der Syntax vertraut zu machen.

Der Trick, den ich vermisste, ist zu wissen, dass ich, wenn ich mit der array[1][5][2] -Syntax auf Elemente im Array verweisen will und die Größen meines Arrays zur Kompilierzeit nicht kenne, nicht einfach calloc() ein einzelner Datenblock dafür.

Am einfachsten zu lesen (obwohl am wenigsten effizient) ist das nur mit einer Schleife:

%Vor%

Ich weise ein dreidimensionales Array von Item * s zu, wobei Item eine Klasse von objective-C ist. (Ich habe natürlich den Fehlerbehandlungscode in diesem Code weggelassen.)

Sobald ich das getan habe, kann ich mit der eckigen Klammern-Syntax auf mein Array verweisen:

%Vor%

Die Webseite, mit der ich oben verlinkt habe, beschreibt auch eine zweite Methode zum Ausführen der Speicherzuordnungen, die nur einen Aufruf von calloc() pro Dimension verwendet. Ich habe diese Methode noch nicht ausprobiert, da die, die ich gerade beschrieben habe, im Moment gut genug funktioniert.

    
Amy Worrall 31.03.2012, 14:41
quelle
5

Zunächst einmal: Während Sie NSPointerArray nicht haben, haben Sie CFMutableArrayRef und Sie können alle Rückrufe, die Sie für retain / release / description haben möchten, einschließlich NULL , übergeben. Es kann einfacher sein (und die Leistung ist etwas, was Sie später messen können), um das zuerst zu versuchen.

Nehmen Sie Ihre Punkte in der richtigen Reihenfolge:

  • Sie sollten Ihre Dimensionen wie erwartet als [cacheNumber][days][height] definieren. Dann ist cache[cacheNumber] ein zweidimensionales Array vom Typ id *[][] . Wie Sie bereits gesagt haben, ist Leistung wichtig. Seien Sie sich bewusst, dass der schnellste Weg zur Iteration dieses Biests ist:

    %Vor%
  • es sollte vom Typ __strong id *** sein: das ist ein Zeiger auf einen Zeiger auf einen Zeiger auf id , was dasselbe ist wie das Array von (Array von (Zeiger auf id )).

  • Ihr ivar muss __strong id **** (!) sein, weil es sich um eine Reihe der oben genannten Dinge handelt.
  • Sie raten falsch in Bezug auf die Zuweisung des Arrays. Wenn Sie ein mehrdimensionales Array verwenden, müssen Sie dies tun (eine Dimension für die Kürze entfernt):

    %Vor%
  • richtig, so benutzen Sie einen solchen Zeiger.

  • Ja, das zweidimensionale Array funktioniert auf die gleiche Weise, sans die erste Dimension.
  • Ich glaube nicht, Sie verteilen __strong Objektzeiger, damit Sie groß sein sollten. Das heißt, wir sind jetzt an der Grenze meiner Fähigkeiten mit diesem Zeug, also könnte ich mich irren.
user23743 26.03.2012 17:29
quelle
0

Ich würde an eine andere Implementierung denken. Wenn es sich nicht um ein nachweisbares Leistungsproblem handelt (d. H. Sie haben es gemessen und quantifiziert), ist der Versuch, Objective-C-Objekte in einfachen C-Arrays zu speichern, häufig ein Code-Geruch.

Es scheint mir, dass Sie ein Zwischencontainer-Objekt benötigen, das wir vorläufig Cache nennen. Eine Instanz wird für jede Cache-Nummer existieren und Ihr Objekt wird ein NS (Mutable) Array von ihnen enthalten. Cache Objekte haben Eigenschaften für die maximalen Tage und die maximale Höhe.

Das Cache-Objekt würde am einfachsten mit einem NSArray der Objekte darin implementiert werden, wobei einfache Arithmetik verwendet wird, um zwei Dimensionen zu simulieren. Ihr Cache-Objekt würde eine Methode -objectAtDay:Height: haben, um auf das Objekt durch seine Koordinaten zuzugreifen.

Auf diese Weise müssen Sie sich nicht um die Speicherverwaltung kümmern, ARC übernimmt das für Sie.

Bearbeiten

Da Leistung ein Problem ist, würde ich ein 1D-Array verwenden und meine eigene Arithmetik berechnen, um Offsets zu berechnen. Der Typ Ihrer Instanzvariable wäre:

%Vor%

Sie können nur C-Multilevel-Indizes ( array[i][j][k] ) verwenden, wenn Sie den Bereich aller Dimensionen kennen (mit Ausnahme der ersten). Dies liegt daran, dass der tatsächliche Offset als

berechnet wird %Vor%

Wenn der Compiler max_j und max_k nicht kennt, kann er es nicht tun. Das ist genau die Situation, in der du dich befindest.

Da Sie ein 1D-Array verwenden und die Offsets manuell berechnen müssen, wird das Apple-Beispiel für Sie funktionieren.

    
JeremyP 26.03.2012 10:19
quelle