[Swift] Collection Type의 할당과 Copy

[updated 2014.7.9]

Xcode 6 beta 3 시기에 맞춰 Swift  Collection에 대대적 변경이 이뤄졌다. 아래는 Xcode 6 release notes 에서 발췌한 내용이다.

• Array in Swift has been completely redesigned to have full value semantics like Dictionary and
String have always had in Swift.  This resolves various mutability problems – now a ‘let’ array
is completely immutable, and a ‘var’ array is completely mutable – composes properly with
Dictionary and String, and solves other deeper problems.  Value semantics may be surprising
if you are used to NSArray or C arrays: a copy of the array now produces a full and
independent copy of all of the elements using an efficient lazy copy implementation.  This is a
major change for Array, and there are still some performance issues to be addressed.  Please
see the Swift Programming Language for more information.  (17192555)!

이제 Dictionary 와 Array 는 변수 할당시 (or 파라미터로 값 전달 시) 값이 항상 복제(copy)된다.

Collection의 아이템이 Reference Type인 경우, Reference Type이 참조하고 있는 값은 복제되지 않고 유지된다.

본 포스팅의 주제였던  Array의 특이사항들 – 값이 복제 되지 않고 전달되는 케이스 – 는 모두 없던 일이 되었다. Array 메소드인 copy(), unshare() 도 사라졌다.

Classes and Structures – The Swift Programming Language 에서도 Collection Assignment and Copy 섹션은 보이지 않는다.

복잡했던 Swift 의 Collection이 정리되었다. 이제 아래 내용들은 추억으로 남기자.

* 참고로 Swift 는 Reference Type 의 copy 를 권장하지 않는 듯 하다. class 객체를 복제할 일이 생기면, struct 이 더 적합한 것은 아니지 고민을 해보자. (관련한 Apple Developer Forum 글 링크 )

 

Swift의 Collection Type인 Dictionary 와 Array는 Structure로서 Value Type이다. (NSArray, NSDictionary 는 Class로서 Reference Type이다.)

같은 Structure Type 임에도 Dictionary와 Array 는 변수 할당시 (or 파라미터로 값 전달시) 값을 처리하는 로직에서 차이를 보인다.

Dictionary

Value Type 의 기본에 맞게 Dictionary는 새로운 변수(or 상수)에 할당이 되면 값이 복제(Copy) 된다.

Dictionary의 key or value 가 Reference Type (Class or Function) 이면, Reference Type이 참조하고 있는 값은 복제 되지 않고 유지된다. 

<del>// Reference Type Test Class</del>
<del>class TestClass{</del>
<del>    var testProperty = "objc"</del>
<del>}</del>

<del>var baseDictionay : Dictionary<Int, Any> = [1:"abc", 2:TestClass() , 3:"def"]</del>

<del>// 새로운 변수에 할당. copy 됨</del>
<del>var copiedTestDictionay = baseDictionay</del>

<del>// 1. Value Type 변경</del>
<del>baseDictionay[1] = "ghi" // value 값 변경</del>

<del>println("\(baseDictionay[1])") // print "ghi"</del>
<del>println("\(copiedTestDictionay[1])") // print "abc</del>

<del>// => 값 변경이 서로에게 독립적</del>

<del>// 2. Ref. Type 변경</del>

<del>// TestClass 객체의 프로퍼티를 변경</del>
<del>(baseDictionay[2] as TestClass).testProperty = "swift"</del>

<del>println("\((baseDictionay[2] as TestClass).testProperty)")  // print "swift"</del>
<del>println("\((copiedTestDictionay[2] as TestClass).testProperty)") // print "swift"</del>

<del>// => Ref.Type 값 변경은 서로 영향 미침</del>

 Array

Array는 Dictionary와는 다르게 변수에 값이 할당 되거나 함수의 파라미터로 전달 될때, 값이 복제되지 않고 그대로 전달(공유)된다.

Array 값의 copy 여부가 결정되는 시점은 할당(or 전달) 이 아니라, 그 후에 발생하는 값의 변경시점이다.

값의 변경이란 아이템 추가, 아이템 삭제, ranged subscript 를 이용한 값의 변경처럼 Array의 length에 영향을 미치는 동작을 의미한다.  (ranged subscript 란 [..] or […] 로 Array의 값을 변경하는 것으로 아이템 삭제가 가능)

Array 값의 Copy 처리는 Dictionay와 동일하다. (Array 아이템이 Reference Type 이면 Reference Type이 참조하는 값은 copy 되지 않고 공유된다.)

<del>var baseArray = [1,2,3,4,5]</del>
<del>var copiedArray_1 = baseArray // no copy</del>
<del>var copiedArray_2 = baseArray // no copy</del>

<del>//1. 단순 값 변경</del>
<del>baseArray[0] = 10</del>

<del>println(baseArray[0]) // print 10</del>
<del>println(copiedArray_1[0]) // print 10</del>
<del>println(copiedArray_2[0]) // print 10</del>

<del>// => copy 되지 않앗으므로, 변경 값이 모든 array에 영향</del>

<del>// 2. ranged subscript 이용한 값 변경</del>

<del>// baseArray의 값 변경 (실질 size 변경은 없지만 ranged subscript는 size 변경시킬 수 있는 능력을 소유)</del>
<del>// 결국 baseArray는 복제된 값을 가짐</del>
<del>baseArray[2..4] = [6,7,8] // copy</del>

<del>println(baseArray[3]) // print 7</del>
<del>println(copiedArray_1[3]) // print 4</del>
<del>println(copiedArray_2[3]) // print 4</del>

<del>// => copy되었으므로 다른 array에 영향 미치지 못함</del>

<del>// 남은 Array의 값을 변경</del>
<del>copiedArray_1[3] = 11</del>

<del>println(baseArray[3]) // print 7, 앞서 복제되었기에 독립적</del>
<del>println(copiedArray_1[3]) // print 11</del>
<del>println(copiedArray_2[3]) // print 11</del>
<del>// => copiedArray_1과 copiedArray_2 는 여전히 같은 값을 공유함</del>

Array 값을 변경하기 전에 명시적으로 값이 copy 되도록 할 수 있다.

unshare()와 copy() 메소드를 이용한다.

</del>
<del>// 위 코드 계속</del>

<del>copiedArray_1.unshare() // 독립선언</del>
<del>copiedArray_1[3] = 20</del>

<del>println(baseArray[3) // print 7</del>
<del>println(copiedArray_1[3]) // print 20</del>
<del>println(copiedArray_2[3]) // print 11</del>

<del>

unshare()는 return이 없는 함수다.

여러 변수가 하나의 Array를 공유하는 경우,  그 중 하나의 변수에서 unshare()를 호출하면 복제된 값을 변수가 갖는다. 만약 Array 값을 가지는 변수가 이미 하나라면 (unique 하다면) unshare() 메소드는 호출되더라도 값을 복제하지 않는다.

반면 copy()는 Array 값을 지니는 변수의 unique 여부와 상관없이 항상 값을 복제한다. 이런 행위에 맞게 copy()는 Array<T> 를 반환하도록 정의되어 있다.

<del>// 위 코드 계속</del>

<del>var copiedArray_3 = copiedArray_2.copy() // 명시적 copy</del>
<del>copiedArray_3[3] = 100</del>

<del>println(copiedArray_2[3]) // print 11</del>
<del>println(copiedArray_3[3]) // print 100</del>

궁금증

  • Array 를 이처럼 복잡하게 만들어 놓은 이유는 뭘까? Value Type 과 Reference Type이 애매하게 섞여있다.  어느 부분에 이점이 있는걸까?
  • 그럼 Dictionary 는 왜 Array 처럼 처리하지 않을까?
  • Array 나 Dictionary 가 copy 되어도 Reference Type의 값은 copy 되지 않는데, copy의 의미를 봤을 때 헷갈리지 않을까?
  • 온전히 독립적인 Collection을 만들려면 Reference Type 의 복제는 별도로 구현해야 하나?

 

* Reference

 

Advertisements
%d bloggers like this: