Swift:配列を参照で渡しますか?


125

Swift Array account.chatschatsViewController.chats参照渡ししたい(チャットをに追加してもaccount.chatschatsViewController.chatsまだを指しているaccount.chats)。つまり、長さがaccount.chats変更されたときにSwiftで2つの配列を分離したくありません。


1
結局account、グローバル変数を作成してas のchatsプロパティを定義するだけになりました。ChatsViewControllervar chats: [Chat] { return account.chats }
ma11hew28

回答:


72

Swiftの構造体は値で渡されますが、inout修飾子を使用して配列を変更できます(以下の回答を参照)。クラスは参照によって渡されます。ArrayそしてDictionarySwiftでは構造体として実装されています。


2
配列はコピーされず、Swiftで値によって渡されません。Swiftでは、通常の構造体と比較して動作が大きく異なります。stackoverflow.com/questions/24450284/…を
Boon

14
@Boon配列はまだ意味的にコピー/値渡しされていますが、COWを使用するように最適化されています。
eonil、2014年

4
また、Swift配列には意味的な違い(参照型など)があり、さらに多くのバグが発生する可能性があるNSArrayためNSArray、ofの使用はお勧めしません。
2014年

1
これは私を真剣に殺していました。どうしてうまくいかないのか頭を叩いていた。
khunshan 2015年

2
inoutStructsで使用する場合はどうなりますか?
Alston、2016

137

関数パラメーター演算子の場合、次の

ように使用します。let(これはデフォルトの演算子なので、letを省略できます)でパラメーターを定数にします(つまり、ローカルコピーも変更できないことを意味します)。

varで変数にします(ローカルで変更できますが、関数に渡された外部変数には影響しません)。そして、それを

入出力パラメーターにするための入出力。インアウトとは、実際には、値ではなく参照によって変数を渡すことを意味します。そして、参照で値を受け入れるだけでなく、参照で渡すことも必要なので、 -foo(&myVar)、単に代わりにありますfoo(myVar)

したがって、次のようにします。

var arr = [1, 2, 3]

func addItem(inout localArr: [Int]) {
    localArr.append(4)
}

addItem(&arr)    
println(arr) // it will print [1, 2, 3, 4]

正確には、これは単なる参照ではなく、外部変数の実際のエイリアスです。そのため、任意の変数タイプ、たとえば整数を使用してそのようなトリックを行うことができます(整数に新しい値を割り当てることができます)。このような基本的なデータ型を変更するのは混乱するかもしれません。


11
これは、コピーされずに参照されるインスタンス変数として配列を使用する方法を実際には説明していません。
Matej Ukmar

2
私は、inoutがゲッターとセッターを使用して配列を一時的にコピーし、関数の途中でリセットすることを考えました。つまり、コピーします
dumbledad

2
実際には、インアウトはコピーインコピーアウトまたは値による呼び出しの結果を使用します。ただし、最適化として参照により使用される場合があります。「最適化として、引数がメモリの物理アドレスに格納された値である場合、関数本体の内部と外部の両方で同じメモリ位置が使用されます。最適化された動作は参照渡しと呼ばれます。これは、コピーのオーバーヘッドを取り除き、コピーインコピーアウトモデル。」
Tod Cunningham

11
スウィフト3では、inout位置は、すなわち変更されましたfunc addItem(localArr: inout [Int])
elquimista

3
また、var関数のパラメーター属性では使用できなくなりました。
elquimista 16

23

インターフェイスBoxedArray<T>を実装するArrayが、すべての関数を格納されたプロパティに委任する自分自身を定義します。など

class BoxedArray<T> : MutableCollection, Reflectable, ... {
  var array : Array<T>

  // ...

  subscript (index: Int) -> T { 
    get { return array[index] }
    set(newValue) { array[index] = newValue }
  }
}

を使用するBoxedArray場所ならどこでも使用できますArray。の割り当てはBoxedArray参照によって行われます。これはクラスであるため、格納されたプロパティへの変更は、Arrayインターフェイスを介して、すべての参照に対して表示されます。


少し恐ろしい解決策:)-正確にはエレガントではありませんが、うまくいくようです。
Matej Ukmar

まあ、確かに「Use NSArray」にフォールバックして「pass by reference semantics」を取得するよりはましです。
GoZoner 2015年

13
クラスの代わりに構造体が配列を言語設計の誤りであると定義している感じがします。
Matej Ukmar

同意する。醜態もありStringのサブタイプであるAnyあなたが場合はimport Foundation、その後StringのサブタイプになりはAnyObject
GoZoner 2015年

19

Swiftバージョン3-4(XCode 8-9)の場合は、

var arr = [1, 2, 3]

func addItem(_ localArr: inout [Int]) {
    localArr.append(4)
}

addItem(&arr)
print(arr)

3

何かのようなもの

var a : Int[] = []
func test(inout b : Int[]) {
    b += [1,2,3,4,5]
}
test(&a)
println(a)

???


3
問題は、2つの異なるオブジェクトのプロパティが同じ配列を指すようにする手段を求めることだと思います。その場合、Kaanの答えは正しいです。配列をクラスでラップするか、NSArrayを使用する必要があります。
Wes Campaigne

1
右、inoutは関数本体の存続期間中のみ機能します(閉鎖動作はありません)
クリスチャンディートリッヒ

マイナーnit:それfunc test(b: inout [Int])は...多分これは古い構文です。私は2016年にSwiftに参加しただけで、この回答は2014年からのものなので、以前は異なっていたのでしょうか?
Ray Toal

2

もう1つのオプションは、アレイのコンシューマーに、必要に応じて所有者に要求することです。たとえば、次のようなものがあります。

class Account {
    var chats : [String]!
    var chatsViewController : ChatsViewController!

    func InitViewController() {
        chatsViewController.getChats = { return self.chats }
    }

}

class ChatsViewController {
    var getChats: (() -> ([String]))!

    func doSomethingWithChats() {
        let chats = getChats()
        // use it as needed
    }
}

その後、Accountクラス内で好きなだけ配列を変更できます。ビューコントローラークラスから配列も変更する場合、これは役に立たないことに注意してください。


0

使用inoutは1つの解決策ですが、配列は値型であるため、それほど迅速ではありません。文体的には、個人的には変異したコピーを返すことを好みます。

func doSomething(to arr: [Int]) -> [Int] {
    var arr = arr
    arr.append(3) // or likely some more complex operation
    return arr
}

var ids = [1, 2]
ids = doSomething(to: ids)
print(ids) // [1,2,3]

1
このようなことにはパフォーマンスのマイナス面があります。それは少し元のアレイを変更するために少し少ない電話のバッテリーを使用します:)
David Rector

私は敬意を払いません。この場合、通常、呼び出しサイトでの読みやすさがパフォーマンスへの影響よりも優先されます。不変コードから始めて、後で変更可能にすることで最適化します。大規模な配列の場合は正しい場合がありますが、ほとんどのアプリでは0.01%のエッジケースです。
ToddH

1
私はあなたが何に反対しているのか分かりません。アレイをコピーするとパフォーマンスが低下し、パフォーマンスが低下するとCPUの使用量が増えるため、バッテリーの消費量も増えます。私はそれがより良い解決策であると言っていたとあなたは思ったと思いますが、私はそうではありませんでした。私は人々が情報に基づいた選択をするために利用可能なすべての情報を持っていることを確認していました。
David Rector

1
ええ、あなたは正しいです、質問はありませんinoutの方が効率的です。inoutそれはバッテリーを節約するので、それは普遍的により良いことをあなたが提案していると思ったと思いました。私が言うには、このソリューションの可読性、不変性、およびスレッドセーフは全体的に優れており、そのinoutは、ユースケースで保証されているまれなケースでのみ最適化として使用する必要があります。
ToddH

0

クラスであるa NSMutableArrayまたはを使用するNSArray

この方法では、ラッパーを実装する必要はなく、ブリッジングでビルドを使用できます

open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.