Swift : 참조로 배열을 전달 하시겠습니까?


125

내가 스위프트를 통과하고 싶은가 Array account.chatschatsViewController.chats참조로 (그래서가 채팅을 추가 할 때 account.chats, chatsViewController.chats여전히 포인트를 account.chats). 즉, 길이가 account.chats변경 될 때 Swift가 두 배열을 분리하는 것을 원하지 않습니다 .


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 Array는 여전히 의미 론적으로 복사 / 값으로 전달되지만 COW 를 사용하도록 최적화되어 있습니다 .
eonil

4
그리고 Swift 배열에는 (참조 유형과 같은) 미묘한 의미 차이가 있고 더 많은 버그로 이어질 수 NSArray있기 때문에 사용하지 않는 것이 좋습니다 NSArray.
eonil

1
이것은 나를 심각하게 죽이고 있었다. 나는 왜 일이 제대로 작동하지 않는지 내 머리를 두드렸다.
khunshan 2015

2
inoutStructs와 함께 사용하면 어떨까요?
Alston

137

함수 매개 변수 연산자의 경우 다음을 사용합니다.

let (기본 연산자이므로 let 을 생략 할 수 있음 )을 사용하여 매개 변수를 상수로 만듭니다.

var 를 변수로 만듭니다 (로컬로 수정할 수 있지만 함수에 전달 된 외부 변수에는 영향을주지 않습니다). 및

inout의는 그것에게 입출력 매개 변수를 확인합니다. 인-아웃은 실제로 값이 아닌 참조로 변수를 전달하는 것을 의미합니다. 그리고 그렇게 참조하여 패스를 통과, 참조 값을 받아도에 의해뿐만 아니라 필요 - 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 2015 년

2
즉, 그 사본을 - 나는 입출력 임시로 배열을 복사 한 다음 함수의 밖으로 길을 재설정하기 위해 게터와 세터를 사용 생각했다
dumbledad

2
실제로 in-out은 copy-in copy-out 또는 call by value 결과를 사용합니다. 그러나 최적화로 참조로 사용할 수 있습니다. "최적화로서 인수가 메모리의 물리적 주소에 저장된 값인 경우 함수 본문 내부와 외부에서 동일한 메모리 위치가 사용됩니다. 최적화 된 동작을 참조에 의한 호출이라고합니다. 이는 모든 요구 사항을 충족합니다. 복사의 오버 헤드를 제거하는 동안 복사 복사 모델. "
Tod Cunningham

11
Swift 3에서 inout위치가 변경되었습니다. 즉func addItem(localArr: inout [Int])
elquimista

3
또한 var더 이상 함수 매개 변수 속성에 사용할 수 없습니다.
elquimista

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 2015 년

글쎄, 그것은 '참조 의미에 의한 통과'를 얻기 위해 'NSArray 사용'으로 돌아가는 것보다 낫습니다!
GoZoner 2015 년

13
클래스 대신 구조체로 배열을 정의하는 느낌이 언어 설계 실수입니다.
Matej Ukmar 2015 년

나는 동의한다. 가증도 있습니다 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
나는 질문이 두 개의 다른 객체의 속성 이 동일한 배열을 가리키는 수단을 요구한다고 생각합니다 . 이 경우 Kaan의 대답은 정확합니다. 클래스에서 배열을 래핑하거나 NSArray를 사용해야합니다.
Wes Campaigne

1
오른쪽 입출력는 (아무 폐쇄 동작) 함수 본문의 수명에 대해서만 작동합니다
기독교 디트리히

Minor nit : 그것은 func test(b: inout [Int])... 아마도 이것은 오래된 구문입니다. 저는 2016 년에만 Swift에 들어갔고이 답변은 2014 년의 것이므로 상황이 달라졌 을까요?
Ray Toal

2

한 가지 다른 옵션은 어레이 소비자가 필요에 따라 소유자에게 요청하도록하는 것입니다. 예를 들어 다음과 같은 내용이 있습니다.

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은 하나의 해결책이지만 배열이 값 유형이기 때문에 나에게 매우 신속하게 느껴지지 않습니다. 스타일 적으로 저는 개인적으로 변형 된 사본을 반환하는 것을 선호합니다.

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그것이 배터리를 절약하기 때문에 보편적으로 더 좋다고 제안했다고 생각 했습니다. 이 솔루션의 가독성, 불변성 및 스레드 안전성이 보편적으로 더 우수하며 사용 사례에서 보장하는 드문 경우에만 최적화로 사용해야한다고 말하고 싶습니다.
ToddH

0

클래스 인 a NSMutableArray또는 a 사용NSArray

이렇게하면 래퍼를 구현할 필요가 없으며 브리징에서 빌드를 사용할 수 있습니다.

open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.