2015년 2월 11일 수요일

Swift 1.2 에서 바뀌는 것들

아직 Xcode 6.2가 정식으로 릴리즈 되지도 않았는데 벌써 Xcode 6.3 베타가 나오는 어이없는(?) 상황이 이어지고 있다. Xcode 6.3에서는 드디어 Swift가 업그레이드 되어서 1.2가 들어가게 되는데 이와 관련된 변화점을 살펴보고자 한다.

가급적이면 모든 내용을 다루고 싶지만 이해가 안되는 것도 많고 별로 몰라도 되는 것도 있어서 중요하다 싶은 내용만 다룬다. 이번 업데이트는 크게 Swift, Xcode 그리고 Objective-C 세 가지 파트로 나뉘어진다.

참고로 이 글은 Xcode 6.3 베타 릴리즈 노트를 기준으로 작성하였다. 기타 애플의 블로그의 내용에도 어느 정도 정리되어 있으니 원문을 간단히 보고 싶다면 아래 링크를 방문해보자. 

https://developer.apple.com/swift/blog/?id=22

Swift 1.2

퍼포먼스

직접 대충 체크하는 것 보단 어느 정도 굴려보신 분의 글을 보는게 나을 것 같다.

http://blog.sudeium.com/2015/02/10/swift-1-dot-2-performance-pretty-much-fine/

Set 타입 추가

Set 이라는 새로운 컬렉션 타입이 추가되었다. 기존 파운데이션의 경우 NSSet 이라는 클래스와 유사한 것으로 볼 수 있다. 참고로 Set은 유니크 밸류(즉 중복되지 않는 고유 값)를 보관할 수 있는 배열과 비슷하다. 제공되는 메소드 등은 Array나 Dictionary와 유사하다.

[관련글] Swift 1.2 - Set 타입

if 문의 조건절 기능 추가

기존 if let의 구문은 단 하나의 옵셔널 변수만 테스트 가능했는데 이것이 확장되었다. 거기다 새로운 조건 추가절(where)을 이용 할 수 있게 되었다. 일단 아래 코드를 보자.
if let a = foo() {
    if let b = bar() {
        if a < b {
            if let c = baz() {
                ...
            }
        }
    }
}
'이 불량한(?) 모습의 코드는 무엇이란 말인가!' 라는 생각이 든다면, 1.2에서는 아래처럼 바꿀 수도 있다는 사실에 환호활 지도 모르겠다.
if let a = foo(), b = bar() where a < b, let c = baz() {
    ...
}
이건 너무 편하다. 진작에 이런 기능이 있었어야 했다!

그 외에 예전 옵셔널 값 존재 여부로 기본값을 결정 하던 '??' 오퍼레이터를 if 문에서도 사용 할 수 있게 되었다. (Xcode 6.3 Beta 2)
if someObj?.value ?? 0 > 0 {
    ...
}
참고로 이 문법은 while 등등 조건문을 이용 가능한 곳에서도 동일하게 적용되는 듯 하다.

상수의 인스턴스화 시기

기존에는 클래스의 생성자를 제외하고 let으로 선언되는 상수는 선언 즉시 값을 지정해야만 했었다. 하지만 Swift 1.2 부터는 즉시가 아니라 참조되기 전에 인스턴스화 하면 되는 식으로 변경되었다.
let x: Something

if condition {
    x = foo()
} else {
    x = bar()
}

use(x)
1.1 이전까지만 해도 위 같은 경우를 위해서 x 라는 녀석은 상수가 아닌 변수(var)로 선언했어야 했는데 좋은 변화이다.
상수는 여러 면에서 유용하다. 예를 들어 immutable의 특징을 충족시켜서 thread-safe 등의 안정성도 가질 수 있다.

클래스와 static

static은 기존의 경우 구조체(struct)에서만 사용이 가능했었는데 1.2 부터는 클래스(class)에서도 사용이 가능해진다. 그리고 이 class의 static은 class final의 별칭 형태로 구현된다고 한다. 무슨 말일까? '-'?? 

어쨌거나 static이 가능해졌다는 점은 클래스를 인스턴스화 하지 않고도 lazy property를 사용 할 수 있게 해 준다. 이와 함께 프로토콜(Protocol)에서 class 대신 static을 요구하게 변경되었다. (class-only procotol의 경우를 의미하는 듯)

Objective-C와 호환되는 열거헝(enum)

Swift의 Enum은 C의 enum에 비해 상당히 많은 기능을 제공한다. 그래서 Objective-C와 호환이 되지 않는다는 단점이 있었다. 그런데 이제는 @objc 키워드를 통해 Objective-C 에서도 쓸 수 있는 Enum을 만들 수 있게 되었다.
// Swift Code
@objc
enum Bear: Int {
    case Black, Grizzly, Polar
}

// 위 코드는 Objective-C 파트에서 이런 식으로 사용(Import)된다.
typedef NS_ENUM(NSInteger, Bear) {
    BearBlack, BearGrizzly, BearPolar
};
자연스럽게 열거형 타입 이름이 각 요소 이름 앞에 자동으로 붙어주는게 Objective-C 답다. -_-;

as!

기존에는 Optional<AnyObject> 타입을 변환할 때 as? 같은걸 써서 옵셔널 타입으로 변환하는 방법이 있었지만 직접 값을 추출해 내지는 못 했었다. 1.2 에서는 여기에 as!를 추가했다. 아마도 기존 ! 같은 강제로 옵셔널을 벗기는 것과 의미는 비슷할거라 생각된다.

'as!' 오퍼레이터는 다운캐스트(부모클래스 타입에서 자식클래스 타입으로 형변환) 할 때 필요한 오퍼레이터이다. 1.2 부터는 다운캐스트가 그냥은 되지 않게 바뀌었다.

[관련글] Swift - 'as!' 오퍼레이터 살펴보기

대충 형변환을 막아버리다

예를 들어, NSString 타입에서 Swift의 String로 별 다른 키워드 없이 대충 형변환(암시적 형변한)은 가능했었는데 이걸 막하버렸다는 이야기이다.

형변환이 아주 막혔다는 의미는 아니다. 실제로 as 커맨드를 이용해 String과 NSString 사이의 형변환은 자유롭다. 그저, as를 쓰지 않고 암시적 형변환(Implicit Conversions)을 했던 코드는 이제 오류가 발생한다는 이야기다.

다만 Swift String을 NSString으로 암시적으로 형변환 하는 건 여전히 유효하다고 한다. 물론 위의 이야기는 String과 NSString 사이에 한정한 것인데 Array와 NSArray, Dictionary와 NSDictionary 사이도 동일한 룰이 적용될 것이다.

@noescape 속성 추가

클로져 속성 중 @noescape 가 추가되었다. 이 녀석은 클로져 호출이 끝나면 클로져를 자동으로 릴리즈 시켜버리는 것으로 유추된다.

(원문의 말이 너무 어렵다 -_- 그냥 생존기간이 호출 시의 시간을 초과하지 않도록 이라는데 뭔 말이야...) 

일단 약간의 퍼포먼스 향상과 함께 클로져 인자에서 self를 사용 할 수 없게 만든다는데 의미불명이다. -_-;;

noescape에 관한 글을 별도로 작성하였으니 이 글을 참고하자.

@autoclosure

@autoclosure 를 붙이는 위치가 바뀌었다. 이제는 아래와 같이 필드 이름 앞에 명시해야 한다.
// 기존
func assert(predicate: @autoclosure () -> Bool) { ... }

// Swift 1.2
func assert(@autoclosure predicate: () -> Bool) { ... }
모호함을 해소하려는 문법의 변화 같다. 이와 함께 @noescape 속성도 함께 지닌다고 한다. 근데 뭔지는 아직 명확하지 않다. -_-;;

Xcode 6.3 Beta 2에서 autoclosure의 escaping 속성이 추가되었다. 아래는 공식 릴리즈 노트의 예제이다.
func lazyAssertion(@autoclosure(escaping) condition: () -> Bool,
                   message: String = "") {
    lazyAssertions.append(condition) // escapes
}
lazyAssertion(1 == 2, message: "fail eventually")
일단 좀 더 이해가 필요할 것 같다. 기회가 되면 이와 관련해 별도의 글을 써 볼 생각이다.

[관련글] Swift - @autoclosure 이야기

Xcode 6.3

Swift의 문법적인 변화 때문인지 마이그레이션 기능을 제공하기 시작했다. 메뉴에서 'Edit - Convert - To Swift 1.2' 를 선택하면 마이그레이션이 된다.

Objective-C

널 가능성 문법

Swift과 Objective-C 사이의 호환성 문제가 걸리는 것 중 하나가 바로 옵셔널이다. Swift의 옵셔널은 별도의 타입이지만 Objective-C에서 NULL은 그저 NULL이라는 값이다. 그래서 이 둘 사이 브릿지 임포트 하는 경우 호환성에 문제가 생긴다. 특히, Objective-C 코드를 Swift에서 쓰는 경우 옵셔널이 명확하지가 않다. (포인터의 경우 전부 Implicit Unwrapped Optionals 투성이 -_-)

그래서 Objective-C 언어 문법에도 특수한 문법이 추가되었다. 대충 널 가능성 표현(Nullability Annotations) 이라고 칭하자. 아래의 세 가지 키워드를 보자.
  • nonnull or __nonnull: Non-optional
  • nullable or __nullable: Optional
  • null_unspecified or __null_unspecified: Implicitly-unwrapped optional
이름 앞에 언더바가 두개씩 붙은 이름은 Objective-C파트가 아닌 C언어 코드일 경우 쓰는 거라고 보면 될 것 같다.

어쨌거나, 이들 키워드가 어떤 식으로 쓰여지고 어떻게 Swift에서 인식되는지를 보자.
// Objective-C
- (void)registerNib:(nonnull UINib *)nib forCellReuseIdentifier:(nonnull NSString *)identifier;
// Swift
func registerNib(nib: UINib, forCellReuseIdentifier identifier: String)

// Objective-C
- (nullable UITableViewCell *)cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath;
// Swift
func cellForRowAtIndexPath(indexPath: NSIndexPath) -> UITableViewCell?

// Objective-C
@property (nonatomic, readwrite, retain, nullable) UIView *backgroundView;
// Swift
var backgroundView: UIView?
좋은 변화다.

[관련글] 스위프트(Swift) 가이드

댓글 2개 :

jusung :

정보 공유해 주셔서 감사합니다. ^^

Renn Seo :

as! 에 대한 내용이 잘못되어서 좀 수정하였습니다. :-)