[Swift] String 타입에 length 메소드가 없는 이유

Swift의 String 타입에는 length 프로퍼티나 메소드가 없다. 이상하게 들리겠지만 실제로 그렇다. Swift에서 문자열의 길이를 구하려면 이럼 메소드 대신에 countElements(_:)라는 글로벌 함수를 사용해야 한다.

더군다나 이 countElements(_:) 함수는 문자열을 처음부터 순회(iterate)하기 때문에 매우 긴 문자열을 다룰 때라면 주의해야 한다. NSStringlength 프로퍼티를 제공하는데 왜 Swift의 문자열 타입은 길이 프로퍼티를 갖지 않을까?

NSString은 내부적으로 유니코드 문자열(정확히는 일련의 코드값)을 UTF16 형식으로 인코딩한 값을 갖는다. UTF16에서는 각 유니코드 스칼라값은 16비트 크기의 값으로 변환된다. 여기서 주의할 것은 “한 개의 유니코드 문자”가 아닌 “한 개의 유니코드 값”이라는 점인데, 유니코드는 하나의 값이 다른 값과 조합되어 문자로 표기되는 경우가 종종 있기 때문이다. 이를 테면 U+301은 영문자 모음에 붙는 첨자인데, “café”를 표현할 때 e와 결합한다. 즉

var word = "cafe"
word += "\u{301}"
println(word)
//  café

이렇게 된다. 앞서 언급한 countElements() 함수는 이 때 é를 한 개 글자로 계산한다. 하지만 이 문자열을 표현할 때 사용된 실제 유니코드값은 5개이다. 이는 유니코드 문자열은 표현방식에 따라서 사용되는 메모리의 크기가 달라질 수 있다는 점이다. 따라서 불가피하게 조합가능한 유니코드 값들을 찾기 위해서는 문자열 내부를 순회하는 작업이 필요하고, 단순한 프로퍼티로는 이를 제공하지 않는다.

println(countElements(word))
//  4

반대로 NSString의 길이는 사용된 유니코드값들이 16비트 단위로 저장되므로 “문자열의 길이”가 곧 사용된 정보의 개수와 같다. 따라서 “몇 자”가 아닌 “몇 개의 유니코드 값”의 의미가 된다.

println(NSString(format:"%s", word).length)
//  5

물론, Swift 문자열이 NSString의 length 프로퍼티와 같은 값을 구하고자 한다면 utf16Count라는 프로퍼티를 사용하면 된다.

println(word.utf16Count)
//  5

끗.

[Swift] 클로저

Closures

Swift에서의 클로저는 코드 블럭과 거의 동일한, 실행 가능한 코드 조각 객체이다. 클로저는 문맥에 따라 주위의 변수들을 캡쳐하여 그 사본을 내부에 저장한다. 함수나 네스팅된 함수는 모두 클로저의 특별한 형태이다.

let myClosure = { (a: Int, b: Int) -> Int in return a + b}

클로저는 그 자체가 문법상 중괄호로 둘러싸져 있으므로 파라미터와 타입시그니처를 그 내부에 작성한다. 여기서 in 문법이 쓰인다. 타입을 추론할 수 있는 경우에는 타입 명시가 필요 없으며, sort 함수의 인자로 쓰이는 등 [Swift] 클로저 더보기

Swift의 클래스 초기화

공식 문서 일부를 번역한 글 입니다. (수정: 2019.06.18)

용어 번역 및 의미

  • initializer – 초기화 메소드. 객체 인스턴스가 생성될 때 호출되는 init(*)류의 이니셜라이저
  • designated initailizer – 지정 초기화메소드. 해당 클래스 레벨에서 새롭게 추가된 프로퍼티를 모두 초기화할 수 있는 메소드. 부모 클래스의 지정 초기화메소드를 호출해야 한다.
  • convenience initializer – 편의 초기화메소드. 프로퍼티에 적절한 기본값을 추가하여 호출을 간단하게 만든 초기화메소드. 반드시 해당 클래스의 지정 초기화 메소드를 호출해야 한다.
  • 초기화 – Swift에서는 객체를 생성할 때 객체가 요구하는 크기만큼의 메모리를 할당받은 후, 모든 프로퍼티는 해당 객체가 사용되기 전에 반드시 적절한 초기값을 가져야 한다. 프로퍼티에 초기값을 지정해 주는 행위
  • 멤버 – 프로퍼티. 일반적인 의미로 멤버라고 부른다.

클래스 상속과 초기화

프로퍼티가 자동으로 0 혹은 nil로 초기화되는 Objective-C와는 달리 Swift에서는 모든 저장 프로퍼티에 대해서 명시적으로 초기화가 이루어져야 한다. (그렇지 않은 경우 컴파일러가 오류를 내놓는다.)

Swift의 클래스 초기화 더보기

implicit optional unwrapping

암시적 언래핑은 옵셔널 타입 변수를 선언하면서 ? 대신 !를 선언한다. 특히 Objective-C API들은 객체를 리턴하는 경우가 많은데 이 때 객체들은 실질적으로 포인터이며 이는 항상 nil이 될 수 있으므로 옵셔널 타입이 된다. 즉, idAnyObject?가 되고, NSMutableArrayArray?가 된다는 의미이다. 그렇다고 해서 Objective-C API와 연동하는 코드 모든 곳에서 느낌표를 매번 붙여서 언래핑하는 것은 매우 불편하기 때문에 등장한 것이 아닌가 한다. (순수 Swift 코드에서는 나올 일이 별로 없을 것 같다.)

implicit optional unwrapping 더보기

swift 커스텀 연산자

커스텀 연산자 >->를 만들어 보자. 함수 외부에서 파라미터를 함수로 주입하는 연산자로 이는 swift에서 기본적으로 정의하지 않고 있는 연산자이다. 따라서 연산자를 먼저 선언해준다.

infix operater >-> {}

내용을 구현해준다. 내용은 제네릭 함수로…

func >-> <T, U>(left:T, right:(T)->U) -> U {
    return right(left)
}

이제 테스트해보자…

func someIncrement(a:Int) -> Int {
    return a + 3
}

4 >-> someIncrement
// 7