2014년 8월 5일 화요일

Swift - Xcode 6 Beta 5 변경점​

이번에도 한국시간기준 화요일에 Xcode의 새로운 베타가 올라왔다. 애플 이 녀석들 미국기준 월요일에 릴리즈 한다는 건데 부러운 녀석들... ;ㅁ;

어쨌건 이번에도 많이 바뀌었다. 물론 이번에도 Swift와 관련된 부분만 간추려본다.

Refinements to Optional Types in Swift

UIView, NSView, UIFont, UIApplicationDelegate 에서 implicit unwrapped optionals가 사라지고 일반 Optional로 대체되었다고 한다. 이미 상당수의 사람들이 implicit unwarpped optionals의 존재에 대해 반감을 가지고 있었는데, 점점 좋은 쪽으로 변화한다는 느낌이다.

그나저나 이제 Optionals와 nil과의 비교가 가능하다. == 이나 != 연산자로 말이다. 아니아니, 정확히 말하자면, 이제 옵셔널은 무조건 비교식을 이용해 체크하도록 해야된다.
var optest: Int?

if optest == nil {
    optest = 100
}
if optest != nil {
    optest = nil
}
if optest {            // ERROR!
    optest = 200
}
if !optest {           // ERROR!
    optest = 300
}
Optionals이 BooleanType 프로토콜을 따르지 않게 되었다. 혼동을 없애려는 하는 것 같지만, 기존 코드 수정이 불가피해졌다.

하지만 implicit unwrapped optionals는 여전하다고 한다. 제발 부탁이니 통일 좀 시켜줘!

Optional Unwrapping Operator x! 라고 이제 값을 직접 지정하는 것도 가능하다. 이전에는 읽기만 가능했다는 점에서 개선되었다고 봐야겠다.
var x: Int! = 0
x = 2
x! = 2
x!++
x!++

이제 ?도 체이닝(chaining)이 가능하다. 예를 들어 nested array (배열 안의 배열)을 한번에 참조 가능해졌다. 물론 배열 안의 사전이라던가 사전 안의 배열도 되겠지.
var list: Array<Array<Int>?> = []
list.append([1, 2, 3])
list.append([2, 3, 4])
list[0]?[0]
list[0]?[1]

'??' 오퍼레이터가 추가되었다. Optionals의 값이 nil, 즉 값이 없는 경우 대체값을 지정하기 위한 목적이다. 물론 사용하기에 따라 초기값 지정에도 가능 할 듯 하다.
var myValue: Int?
let defaultValue = 100

myValue = myValue ?? defaultValue

Ranges in Swift

Ranges 라는 것이 내부적으로 세 갈래로 나눠졌다.
  • 일반적인 Ranges는 순차증가형(ForwardIndexType)
  • Intervals 라는 비교가능한(Comparable) 녀석
  • Strideable 이라는 고급형(?)
이런데 이걸 타입별로 하나한 신경 써 가면서 써야하는 건가. 의외로 귀찮다. 그냥 막 쓰는게 더 나을지도 모르겠다.

일단 기본적인 Ranges는 순차증가형(ForwardIndexType)이다. 아래의 코드 예제와 같이 우측에 작은 값이 오면 안된다.
1...10  // Range, ForwardIndexType
10...0  // ERROR: Can't form Range with end < start
HalfOpenInterval 이라는 녀석은 좀 특이한데, 시작점이 정수가 아닌 형태이다. 이터레이션이 안되는 말 그대로 범위만 지정하는 녀석이다.
// ERROR: 'HalfOpenInterval' does not have a member named 'Generator'
for i in 1.3..<12 {
    println(i)
}

// HalfOpenInterval
let hor = 1.3..<12
반면에 ClosedInterval 이라는 녀석은 끝 지점이 정수가 아닌 형태인데 그나마 한계적으로(?) 이터레이션은 된다.
// ClosedInterval
for i in 1..<10.1 {
// 1, 2, 3, 4, 5, 6, 7, 8, 9
그리고 ReversedRange 라는 녀석이 사라지고 .reverse() 라는 메소드를 쓰라고 한다. 사실 이런게 있는 줄도 몰랐다 -_-;; 다만 reverse를 쓰기 위해서는 lazy한 range를 만들어야 한다.
(1...10).reverse()
// ERROR: 'ClosedInterval does not have a member named 'reverse'

// Reversed Range with .reverse()
lazy(1...10).reverse()
stride의 새로운 형태도 생겼다. 일반적인 레인지와 비슷하게 오른쪽 같이 끝인지 아니면 이 값보다 작은 것인지의 형태를 결정하는 타입이다.
for i in stride(from: 0, to: 10, by: 2) {
    println(i)
}
// 0, 2, 4, 6, 8

for i in stride(from: 0, through: 10, by: 2) {
    println(i)
}
// 0, 2, 4, 6, 8, 10

Swift Standard Library

몇몇 프로토콜의 이름이 바뀌었다. 이들 프로토콜 이름은 -ible, -able, -Type 이라는 이름으로 끝난다. 결국 프로토콜의 용도를 '뭐뭐가 가능하게' 혹은 '무슨무슨 형식으로' 라는 의미로 쓰라는 것이다. 그래도 딜리게이트(delegate)는 여전히 같은 이름이 쓰이겠지. 이건 Swift가 아니라 AppKit이나 UIKit의 영역이니깐...

UnsafeConstPointer는 UnsafePointer로, UnsafePointer는 UnsafeMutablePointer로 이름이 바뀌었다. 기본이 immutable 이라는 Objective-C(정확히는 Cocoa나 Cocoa Touch에서 쓰이는 형태) 스타일을 그대로 가져오려는 듯. C 포인터를 쓰지 않는다면 신경쓰지 말자. 하지만 나같이 많이 신경써야 하는 사람이 있을지도...

마찬가지로 UnsafeArray가 UnsafeBufferPointer로, UnsafeMutableArray는 UnsafeBufferMutablePointer 로 변경. 위와 동일하게 기본이 immutable이라는 의도로 생각된다. 역시나 요 두 녀석도 포인터 엑세스를 위한 C 코드와의 호환을 위해서 사용되니 크게 신경쓰진 않아도 될 듯. 나는 신경 많이 써서 짜증난다. -_-

Array가 비어있을 경우 first와 last 프로퍼티는 nil을 리턴한다.
var arr: Array<Int> = []
arr.first   // nil
arr.last    // nil
참고로 var arr = [] 이런 식으로 빈 배열을 그냥 막(?) 선언하면 Array가 아니라 NSArray가 된다.

Dynamic declaration modifier

'dynamic' 이라는 키워드가 생겼다. Objective-C에서 참조 가능하게 만든다는 점에서 @objc와 비슷하지만, 디스패치(dispatch)를 다이나믹(런타임?)하게 하기 위한 용도라는 듯. 그렇다면 다르게 @objc 라는 키워드는 inline 하게 처리한다는 것일까? 그렇다면 @objc가 속도 면에서는 훨신 이득이다. 하지만 다이나믹하지 못 하다는 점은 서브클래싱에서 치명적인 부작용이 있다.
final class Foo: SomeBaseClass {
    // objc_msgSend에 항상 엑세스 됨: final이든 아니든 런타임 엑세스
    dynamic var x: Int
    
    // ObjC에서 엑세스 가능
    @objc var y: Int
    
    // 그냥은 ObjC에서 쓸 수 없다. ObjC 클래스를 상속받게 만들면 그때서야 보이겠지.
    var z: Int
}
그런데 위 코드는 릴리즈 노트에 있던 코드인데 이상하게도 문제가 있다. final이랑 dynamic은 같이 못 쓴다는 오류가 뜬다. 뭘까? 뭐지? 버그인가?

참고사항:
  • final은 상속 시 오버라이드를 금지시키기 위해 필요하다. Private API로 디자인한다면 이렇게 final로 마킹하면 될 듯.
  • @objc 는 Objective-C 코드에서 Objective-C의 자식클래스가 아닌 Swift의 클래스 멤버를 참조할 때 필요하다.

Swift Operators and Attributes

@auto_closure 는 @autoclosure로 변경되었다. 타이핑이 약간 더 편해졌다. 엄청난 변화다! -_-;;

@assignment가 사라졌다. 어라... 그렇다면 이제 @assignment없이 inout으로 left나 right를 적당히 요리해주면 된다는 의미일까?
postfix operator ** {}
postfix func ** (inout v: Int) -> Int {
    v = v * v
    return v
}

var v1 = 10
let v2 = v1**
// v2 = 100
아마 예상이 맞는 것 같지만 assignment의 기존 용도가 이게 맞는지 의문이 생기게 되었다. 어쨌든 사라졌으니 안심(?)

그런데 실제로 코딩을 하다보니 infix도 사라졌다는 것을 알게 되었다. 릴리즈 노트에서 빼 먹은건가 아니면 내가 빼고 본 건가 -_-;;;;

beta4 부터 시작된 골뱅이 떼기 작업은 여전히 진행 중이다. 오퍼레이터 오버로딩에 쓰였던 @prefix, @postfix 가 그 대상이다.

클래스 전용 프로토콜을 정의할 때 쓰던 @class_protocol이 사라졌다. 대신 그 역활은 프로토콜 정의 시 클래스(class)라는 이름을 명시하면 가능하다고 한다.
protocol MyProtocol: class {
    ...
}

배열에 += 오퍼레이터를 이용해 아이템(Element)을 추가할 수 있던 것이 이제는 안된다. 이 명령어는 이제 배열끼리 합칠 때나 가능하다.
var arr1 = [1, 2, 3]
arr1 += 5
// '[Int]' is not identical to 'UInt8' 잉 왜 이딴 에러가?

var arr2 = [4, 5, 6]
arr1 += arr2
// arr1 = [1, 2, 3, 4, 5, 6]
앞서 이야기 한 대로 += 은 배열끼리의 연산만 가능하게 되었다는 건 맞는 것 같은데 에러메시지는 왜 저딴식일까? 버그인가? 어쨌거나 기존 방법이 필요하다면 타입을 확실하게 명시해 놓은 오퍼레이터 오버로드가 필요할 것 같다.

Swift Command Line Interface

UNIX나 리눅스에서 쉘 스크립트를 만들던 이들에겐 익숙한 '#!' 을 이용해 실행 인터프리터 지정이 가능해졌다. 이제 쉘스크립트를 Swift로도 짤 수 있게 되었다!

최적화 옵션인 -Ofast 가 -Ounchecked 로 변경. 그런데 이 두 옵션은 나중에 사라진다고 한다. 이런... C++과의 속도 비교 시 유리함을 주던 옵션이 사라졌다. 사실 이 옵션은 Swift 가 내세우던 특징인 안정성(오버플로우나 한계 검사 등등)을 무시하도록 해서 속도를 향상시키던 것이었으니 맞는 것일지도 모르겠다. 그렇다면 별다른 퍼포먼스 향상을 다른 방법으로 추구하겠다는 것일까?

Miscellaneous Swift

class 배열이나 @objc 프로토콜 배열을 강제로 캐스팅(형변환) 할 수 있는 [C] 라는 것이 추가되었다는데 뭔 말인지 무슨 용도로 써야하는지는 잘 모르겠다.

주석문 중 /** 나 /// 같은 형식은 문서화를 위해서도 사용된다. 참고하자.

퍼포먼스 개선: 루프나 복잡한 제어문 그리고 디버그 빌드. 물론 '나중에 더 개선될거다' 라는 단서도 달고 있다. 앞서 -Ofast가 제거될거라고 한 이유는 여기에 있는 듯.

disignated initializer는 반드시 override를 명기해야 한다.
class TestClass: NSObject {
    var value = 0
    
    override init() {
        super.init()
    }
}
참고:
  • Designated Initializer 란 그냥 생성자(init)를 의미한다고 생각하자. 정확히는 최우선 생성자(Primary Initializer)라고 생각해도 된다.
  • 여기서는 NSObject를 상속받았기 때문에 init을 override 한다고 해야한다. 하지만 베이스클래스를 지정하지 않으면 (즉 별다른 상속이 없다면) override 해서는 안된다.

Xcode support for Swift

프로젝트 설정 중 -Ofast 옵션은 -Ounchecked 옵션으로 바뀐다. 물론 앞서 이야기한대로 -Ofast도 -Ounchecked도 곧 사라진 운명이니 그냥 아예 쓰질 말자 -_-

Swift REPL Breakpoints

말 그대로 REPL에서 브레이크포인트를 지원하게 된다. :b 라는 키워드 뒤에 라인 번호를 입력하면 되나본데 터미널에서 Swift REPL을 쓴다면 가끔 쓸 일이 있을지도...

기타 버그 수정

CGFloat 값을 정수로 생성하는게 불가능 했었나 보다. 이젠 고쳐졌다고 하는 듯.
let cgf1 = CGFloat(20)  // 20.0
let cgf2: CGFloat = 30  // 30.0

기타 많은 수정 내역이 있긴 하지만 Swift 자체와는 크게 관계는 없는 것 같다. 그리고 릴리즈 노트에는 중요한 내용도 그 아래에 이어지는데 많은 버그들이 있다는 내용이다... -_-;;

개인적으로도 제대로 이해한건지 불안정한 내용도 많고 잘 모르던 것도 많다. 혹시나 이상한 점이 있다면 과감하게 댓글로 찔러주시길... ;-)

관련글: 스위프트(Swift) 가이드​

댓글 없음 :