2017년 3월 28일 화요일

Swift 3.1 릴리즈

오늘 Xcode 8.3 이 정식으로 릴리즈 되면서 Swift 3.1 도 함께 정식 릴리즈 되었다. 그래서 변화점을 간략히 정리해 보려고 한다. 주관적(?)으로 정리할 것이기 때문에 정확한 내용은 영어 울렁증(?)이 없다면 공식 사이트의 릴리즈 노트 를 보는 것이 좋을지도 모른다.

1. Sequence 프로토콜에 새로 추가된 메소드 drop, prefix

기존에도 drop 류나 prefix 류 메소드가 있었는데, 이번에 추가된 것은 클로져를 이용하는 버전이다. 아래 두 녀석이다.
func drop(while predicate: (Self.Iterator.Element) throws -> Bool) rethrows -> Self.SubSequence
func prefix(while predicate: (Self.Iterator.Element) throws -> Bool) rethrows -> Self.SubSequence
영어 설명을 보면 알겠지만 이 두 녀석은 서브시퀀스를 추출해 낼 때 사용한다. 예를 들어 배열에서 일부 배열만을 추출해 내는 등이다. 마치 filter 와 비슷하다. 대신 약간(?) 다르게 동작한다.

우선 drop 의 경우는 false 가 나오면 이터레이션이 끝나게 된다. 즉, true가 나오면 해당 아이템을 버리는데 false 가 나오면 이후 콜렉션이 호출되지 않고 나머지는 모두 false 처리된다.
let result = (1...10).drop { (value) -> Bool in
  return value < 5
}

for value in result {
  print(value)
}
참고로 drop 된 결과인 result 는 RandomAccessSlice<CountableClosedRange<Int>> 라는 특수한 타입이다. 이 값은 그대로 보이진 않지만 기존 시퀀스와 비슷하게 이터레이션이 가능하다.

위 코드를 실행시키면 콘솔에 5, 6, 7, 8, 9, 10 이 찍힌다. 즉 클로져에서 value 가 5 미만인 경우 true 가 리턴되기에 5미만의 값들이 다 버려지게(drop) 된다.

그런데 위 코드의 클로져에 5 값이 들어오면 처음으로 false가 반환되는데 이 후에는 이 클로져는 더이상 불리지 않는다. 즉 drop 하는 최소 값이 결정되면 더 이상 호출되지 않는다는 점이 filter 와 확연히 다르다.

prefix 의 경우는 drop 과 반대의 역활을 한다.
let prefixResult = (1...10).prefix { (value) -> Bool in
  return value < 5
}

for value in prefixResult {
  print(value)
}
위 코드의 실행 결과는 1, 2, 3, 4 이다. prefix 의 역활은 drop 과는 다르게 true 가 되는 값 만을 취득하다가(filter 의 동작과 비슷하다) 최소로 false 가 되면 더이상 클로져가 호출되지 않고 prefix 의 실행이 종료된다. 결과적으로 5 미만의 값들만을 추출해 낸다.

filter 가 있는데 왜 비슷한 이런 프로토콜이 추가된 것일까 생각할 수 있는데, drop 과 prefix 의 방식은 루프를 최소화 시킬 수 있다. 즉 filter 는 더 이상 볼 필요가 없는 시퀀스의 모든 멤버도 둘러봐야 하는데 이는 퍼포먼스 면에서 그다지 좋지 못 하다. 만약 수치 배열에서 특정 수치 이상 혹은 이하의 값 만을 filter 하고 싶다면 drop 이나 prefix 를 사용하는 편이 더 좋은 성능을 낸다는 이야기다.

Availability

이 부분은 공식 사이트에 올라온 아래 예제 만으로도 충분할 것 같다.
@available(swift, obsoleted: 3.1)
class Foo {
  // ...
}
기존에는 OS 종류를 기준으로 동작하는 #availability 가 @availibity 로 바뀌면서 이제 OS 뿐만이 아니라 언어 버전도 체크가 가능해졌다.

Improved numberic conversion initializers

스위프트에서 제공되는 기본 타입의 경우 수치를 이용해 다른 타입을 생성하는 경우 해당 타입에 적당한 값으로 변환이 되었다. 예를 들어 아래와 같다.
Int(10)     // 10
Int(10.2)   // 10
위의 경우 둘 다 10 이라는 값이 생성된다.

하지만 정확하게 생성해야 하는 경우, 예를 들어 수치가 정수가 아니면 아예 생성에 실패하게 해야 하는 경우가 있다면 Swift 3.1 부터는 쉽게 가능하다.
Int(exactly: 10)    // 10
Int(exactly: 10.2)  // nil
모든 수치 타입에 exactly 라는 라벨이 붙은 생성자가 추가되었는데 타입이 동일한 수치일 경우에만 생성된다. 따라서 수치 만으로 어느 정도 타입 확인이 가능하게 되었다.

기타

UnsafeMutablePointer.initialize(from:) 메소드가 더이상 콜렉션 타입을 받아들이지 않는다고 한다. 이 경우 UnsafeMutableBufferPointer 를 사용하라고 하는데 어떻게 보면 당연한 이야기 같기도 하다. 애초에 Swift 에서 포인터를 쓴다는 것 자체가 이상한(?) 일인것 같기도 하고 중요도는 좀 떨어지는 듯 하다.

(다만 나에겐 포인터 관련 글들을 몽땅 수정해야 한다는 귀찮음을 만들어 준 애플 나빠요!!!)

기타 리눅스 관련 지원이나 SPM(Swift Package Manager) 관련 내역은 언어 자체와는 무관하니 생략한다. :-)

[관련글] 스위프트(Swift) 가이드​
[관련글] Swift 속의 C Pointer 이야기 - 시작

댓글 없음 :