2015-12-27 13 views
6

Chcę przekazać tablicę do obiektu i zapisać referencję do tej tablicy. Chcę móc modyfikować tę tablicę w tym obiekcie i upewnić się, że jest ona modyfikowana wszędzie indziej.Przechowywanie odwołania do tablicy w trybie szybkim

Oto co próbuję wykonać (jak kod nie działa)

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 

moich ustaleń dotychczas

a) array (domyślnie) są przekazywane dziwny sposób. Jest to odniesienie, dopóki nie zmodyfikujesz długości tablicy. Po zmodyfikowaniu długości zostanie ona skopiowana i zmodyfikowana. W rezultacie, jeśli dodaję lub usuwam z niego cokolwiek w obiekcie, nie będzie można go zobaczyć poza BN2. Mogę użyć parametru inout dla parametru funkcji. To pozwoli mi modyfikować go w ramach tej funkcji. Jednak, gdy tylko spróbuję przypisać go do jakiegoś elementu obiektu, znowu uderzy mnie A)

C) Mogę owinąć tablicę w jakiejś klasie Container. To prawdopodobnie jest najczystszy sposób. Jednakże serializuję/deserializuję te obiekty i wolałbym nie umieszczać ich w Container (ponieważ będę musiał obejść pewne rzeczy dla serializacji i deserializacji i wysłania ich na serwer).

Czy jest coś jeszcze? Czy brakuje mi konstrukcji Swift, która pozwala mi to zrobić?

+0

Jak można by się spodziewać, że "a" zostanie oznaczone jako stała? Musiałbyś również przekazać go za pomocą parametru inout i przekazać go do swojej metody z prefiksem "&" –

+0

Mogę zmienić "a" na var. Korzystanie z parametru & i inout będzie działało tylko wtedy, gdy zmienię tablicę tylko w metodzie, do której został przekazany. Jak tylko wykonam "self.foo = foo", przestanie działać. Znaczenie modyfikacji self.foo nie wpłynie na "a" –

+0

oznaczyć jako var, define var foo: [Int] = [] {willSet (newArray) {a = newArray}} –

Odpowiedz

0

Od the Swift Programming Language,

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

Jeśli przeanalizować zawartość zmiennej tablicy, widać, że rzeczywiście append działa:

 
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)") 

produkuje

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

Zrobiłeś c opy o numerze, a nie o odniesienie do.

+0

Tak. Wiem, że dołączy on do tablicy wewnątrz obiektu. Moim celem jest upewnienie się, że tablica (w tym przypadku "a"), która została przekazana do obiektu, została również zmodyfikowana. Tak więc szukam sposobu na wydrukowanie [1,2,3,4,5] –

+0

. Byłbym bardzo ostrożny, myśląc o tym, co próbujesz tutaj zrobić. Chcąc zduplikować zawartość tablicy zewnętrznej za każdym razem, gdy dokonasz zmiany w wewnętrznej zmiennej tablicowej, podważasz pojęcie "enkapsulacji". Czy nie byłoby lepiej manipulować wewnętrzną zmienną tablicową, a następnie ujawnić ją w razie potrzeby? Myślę, że odpowiedź Leo jest sprytna dzięki użyciu siły obserwatorów właściwości, ale kopiuje całą tablicę za każdym razem, gdy zmienia się element tablicy. – timbo

+0

Nie zgadzam się co do enkapsulacji. Powiedzmy, że przechodziliśmy i sprzeciwiamy się Foo, a Foo wywołał jakąś metodę, która zmodyfikuje zawartość tego obiektu. To nie złamie enkapsulacji. Ogólnie rzecz biorąc, obiektem jest Array . Nazywamy metodę (dopisz). Więc nie powinno być traktowane inaczej. –

4

Będziesz musiał użyć NSArray lub NSMutableArray, ponieważ Swift Arrays są typami wartości, więc każde zadanie utworzy kopię.

0

a jest deklarowana jako stała, dlatego nie można jej modyfikować. Jeśli planujesz zmodyfikować zawartość a, zadeklaruj ją jako zmienną. tj.,

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

Masz rację. a powinno być zadeklarowane jako "var". Jednak problem jest głębszy. –

1

Możesz użyć jerzyków (bardzo nie-śmigających) UnsafeMutablePointer.

Ponieważ (z Twojego postu) zachowanie odniesienia do tablic mogą naprawdę nie wydają się ufać, zamiast zachować UnsafeMutablePointer towarzysza do klasy wewnętrznej tablicy foo jak również żadnych „zewnętrznych” tablice, które mają być zbindowanych do foo , w tym sensie, że oba są po prostu wskaźnikami do tego samego adresu w pamięci.

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) 

Wskaźniki mogą być niebezpieczne, choć (stąd nazwa typu okucia) oraz, ponownie, bardzo un-swifty. Tak czy inaczej...

/* 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 

Teraz, co się dzieje, gdy obaj a lub foo wykracza poza zakres, będzie przerwa zmiennej, które nie znajdują się poza zakresem, czy też Swift zawierać jakiś sprytny liczenia odniesienia, który realizuje adres pamięci jest nadal w użyciu ? Nie zbadałem tego, ale nie krępuj się.

+0

Interesujące. Będziesz musiał przekazać pOuter w każdej metodzie, która modyfikuje wewnętrzną tablicę. –

+0

Tak, to nie jest Najszybsze lub najładniejsze rozwiązanie, ale będziesz wiedzieć na pewno, że twoje tablice są nie tylko "modyfikowane gdziekolwiek indziej", ale są w rzeczywistości tymi samymi tablicami w pamięci. Zwróć też uwagę, że dla punktu A) w twojej odpowiedzi: swift przydziela dokładnie tyle miejsca, ile potrzebujesz podczas inicjowania tablicy wartościami. Jeśli dodasz wartość, musisz przydzielić miejsce na pełną tablicę + nowy element gdzie indziej, a następnie skopiować "starą" tablicę + nowy wpis do nowej przestrzeni, a na końcu zwolnić starą przestrzeń. Dlatego wydaje się, że jest losowo przy użyciu kopii referencyjnej/wartości. – dfri

Powiązane problemy