2015-12-27 1 views
6

Ich möchte ein Array an ein Objekt übergeben und einen Verweis auf dieses Array speichern. Ich möchte in der Lage sein, dieses Array innerhalb dieses Objekts zu ändern und sicherzustellen, dass es überall anders geändert wird.Speichern einer Referenz auf Array in swift

Hier ist, was ich zu tun versuchen, (wie der Code funktioniert nicht)

class Foo { 
    var foo : Array<Int> 

    init(foo: Array<Int>) { 
     self.foo = foo 
    } 

    func modify() { 
     foo.append(5) 
    } 
} 

var a = [1,2,3,4] 
let bar = Foo(a) 
bar.modify() 
print(a) // My goal is that it will print 1,2,3,4,5 

Meine bisherigen Ergebnisse

A) Das Array (Standardeinstellung) sind seltsame Weise übergeben. Es ist eine Referenz, bis Sie eine Array-Länge ändern. Sobald Sie eine Länge ändern, wird diese kopiert und geändert. Als Ergebnis, wenn ich irgendetwas davon in das Objekt anhänge oder lösche, wird es außerhalb nicht gesehen

B) Ich kann inout auf einem Funktionsparameter verwenden. Dies erlaubt mir, es innerhalb dieser Funktion zu modifizieren. Sobald ich jedoch versuche, es einem Objekt zuzuweisen, fällt mir wieder ein: A)

C) Ich kann ein Array in eine Container-Klasse einbinden. Dies ist wahrscheinlich der sauberste Weg. Diese Objekte serialisiere/deserialisiere ich jedoch, und ich würde es lieber nicht in Container einfügen (weil ich einige Dinge für die Serialisierung und Deserialisierung umgehen und sie an den Server senden muss).

Gibt es noch etwas? Vermisse ich ein Swift-Konstrukt, das mir erlaubt, das zu tun?

+0

Wie würden Sie erwarten, dass Sie "a" als Konstante deklarieren? Auch Sie müssten es übergeben mit outout-Parameter und übergeben Sie es an Ihre Methode mit "&" Präfix –

+0

Ich kann "a" zu einem var ändern. Die Verwendung von & und inout-Parameter funktioniert nur, wenn ich das Array nur in der Methode ändere, an die es übergeben wurde. Sobald ich "self.foo = foo" mache, hört es auf zu arbeiten. Bedeutung Änderung von self.foo hat keinen Einfluss auf "a" –

+0

deklariere es als var, definiere var foo: [Int] = [] {willSet (newArray) {a = newArray}} –

Antwort

0

Von the Swift Programming Language,

Structures are always copied when they are passed around in your code, and do not use reference counting.

Wenn Sie den Inhalt der Array-Variablen zu untersuchen, werden Sie, dass in der Tat die append sehen funktioniert:

 
class Foo { 
    var foo : Array 

    init(_ foo: Array) { 
    self.foo = foo 
    } 

    func modify() { 
    foo.append(5) 
    } 

    func printFoo() { 
    print("self.foo: \(foo)") 
    } 
} 

let a = [1,2,3,4] 
let bar = Foo(a) 
bar.modify() 
bar.printFoo() 
print("a: \(a)") 

produziert

 
self.foo: [1, 2, 3, 4, 5] 
a: [1, 2, 3, 4] 

Sie haben eine c genommen Opy of a, kein Verweis auf eine.

+0

Ja. Ich weiß, dass es an ein Array innerhalb des Objekts anhängen wird. Mein Ziel ist es, sicherzustellen, dass ein Array (in diesem Fall "a"), das an das Objekt übergeben wurde, ebenfalls geändert wird. Also, ich suche nach einer Möglichkeit, es auszudrucken [1,2,3,4,5] –

+0

Ich würde sehr sorgfältig darüber nachdenken, was Sie hier zu tun versuchen. Wenn Sie den Inhalt eines externen Arrays bei jeder Änderung einer internen Array-Variablen duplizieren möchten, unterlaufen Sie das Konzept der Kapselung. Wäre es nicht besser, die interne Array-Variable zu manipulieren und sie dann bei Bedarf freizugeben? Ich denke, dass Leos Antwort clever ist, wenn man die Macht von Eigenschaftenbeobachtern benutzt, aber es kopiert das ganze Array jedes Mal, wenn ein Array-Element geändert wird. – timbo

+0

Ich stimme der Kapselung nicht zu. Nehmen wir an, wir würden Foo übergeben, und Foo würde eine Methode aufrufen, die den Inhalt dieses Objekts verändern würde. Dies wird die Kapselung nicht unterbrechen. Im Allgemeinen ist Array das Objekt. Wir rufen eine Methode (append) darauf auf. Also, es sollte nicht anders behandelt werden. –

4

Sie müssen hierfür ein NSArray oder NSMutableArray verwenden, da Swift-Arrays Werttypen sind, sodass bei jeder Zuweisung eine Kopie erstellt wird.

0

wird eine Konstante deklariert und kann daher nicht geändert werden. Wenn Sie planen, den Inhalt von a zu ändern, deklarieren Sie es als Variable. das heißt

var a = [1,2,3,4] 
+0

Sie haben Recht. a sollte als "var" deklariert werden. Das Problem ist jedoch tiefer. –

1

Sie könnten die Verwendung von Swifts (sehr un-swifty) UnsafeMutablePointer machen.

Da (aus Ihrem Post) das Verhalten Referenzen auf Arrays kann nicht wirklich zu trauen scheint, anstatt eine UnsafeMutablePointer Begleiter auf die innere Array Klasse halten foo sowie alle „externen“ Arrays, die Sie foo binded werden möchten in dem Sinne, dass sie beide nur Zeiger auf die gleiche Adresse im Speicher sind.

class Foo { 
    var foo : [Int] 
    var pInner: UnsafeMutablePointer<Int> 

    init(foo: [Int]) { 
     pInner = UnsafeMutablePointer(foo) 
     self.foo = Array(UnsafeBufferPointer(start: pInner, count: foo.count)) 
    } 

    func modify(inout pOuter: UnsafeMutablePointer<Int>) { 
     foo.append(5) // <-- foo gets new memory adress 
     pInner = UnsafeMutablePointer(foo) 
     pOuter = pInner 
    } 
} 

var a = [1,2,3,4] // first alloc in memory 
var pOuter: UnsafeMutablePointer<Int> = UnsafeMutablePointer(a) 
var bar = Foo(foo: a) // 'bar.foo' now at same address as 'a' 
print(bar.foo) // [1,2,3,4] 
bar.modify(&pOuter) // -> [1,2,3,4,5] 
a = Array(UnsafeBufferPointer(start: pOuter, count: bar.foo.count)) 

/* Same pointer adress, OK! */ 
print(bar.pInner) 
print(pOuter) 

/* Naturally same value (same address in memory) */ 
print(bar.foo) 
print(a) 

Pointers kann gefährlich sein, wenn (daher der Einpasstyps Name), und wiederum sehr un-swifty. Sowieso...

/* When you're done: clear pointers. Usually when using 
    pointers like these you should take care to .destroy 
    and .dealloc, but here your pointers are just companions 
    to an Array property (which has a pointer an reference 
    counter itself), and the latter will take care of the 
    objects in memory when it goes out of scope.   */ 
bar.pInner = nil 
pOuter = nil 

Nun, was passiert, wenn entweder a oder foo den Gültigkeitsbereich verlässt, wird die Variable brechen, die nicht aus dem Rahmen sind, oder hat Swift einige clevere Referenzzählung enthalten, die eine Speicheradresse ist noch in Gebrauch realisiert ? Ich habe das nicht untersucht, aber fühlen Sie sich frei, sich darin zu verwöhnen.

+0

Interessant. Sie müssen pOuter in jeder Methode übergeben, die das innere Array ändert. –

+0

Ja, es ist nicht die schnellste oder schönste Lösung, aber Sie werden sicher wissen, dass Ihre Arrays nicht nur "überall anders modifiziert" sind, sondern in Wirklichkeit dieselben Arrays im Speicher sind. Beachten Sie auch, dass für Punkt A) in Ihrer Antwort: swift genau den Speicherplatz reserviert, den Sie benötigen, wenn Sie ein Array mit Werten initialisieren. Wenn Sie einen Wert anhängen, muss er Platz für das gesamte Array + neues Element an anderer Stelle zuweisen, dann "altes" Array + neuen Eintrag in neues Leerzeichen kopieren und schließlich altes Leerzeichen freigeben. Aus diesem Grund scheint es, als ob es zufällig Referenz/Wert-Kopie verwendet. – dfri