[swift2] 키워드로 보는 Swift

Swift 2015. 11. 30. 21:33

Swift 에서 사용하는 예약어를 마인드맵으로 그려보았습니다. 예약어들을 보고 얼마나 알고 있고, 또 설명할 수 있는지 시험해보세요.

예약어 목록은 The Swift Programming Language 문서를 참고하였습니다.





선언에 사용되는 키워드

  • import : 모듈(프레임워크)을 로드하여, 현재 파일에서 심볼을 접근할 수 있게함.
// UIKit 전체모듈 로드
import UIKit    

// UIKit 모듈의 NSText 서브모듈만 로드
import UIKit.NSText
  • class : 클래스 선언
  • protocol : 프로토콜 선언
  • extension : 클래스 확장
  • typealias : 기존타입에 별칭을 부여합니다.
 typealias AudioSample = UInt16
  • enum : 열거형 선언
  • struct 구조체 선언
  • init : 생성자
  • deinit : 소멸자
  • func : 함수 정의
  • var : 변수 정의해 사용하는 키워드
  • let : 상수 정의해 사용하는 키워드
  • static : 해당 타입의 모든 인스턴스가 공유하는 type 속성, 메소드 정의를 위한 키워드

    value(struct, enum) 타입에는 static, 클래스 타입에는 class 키워드를 사용합니다. 클래스 타입의 저장속성 정의에서는 class 키워드를 지원하지 않습니다.

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 10
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 10
    }
}
class SomeClass {
    // class stored properties not yet supported in classes
    // static var storedTypeProperty = "Some value."
    class var computedTypeProperty:Int {
        return 10
    }
}
  • subscript : 클래스, 구조체, 열거형의 속성을 인덱스 문법으로 접근하기 위한 인터페이스.
  • internal
  • public
  • private
  • inout : 함수 내부에서 변경된 인자값이 원래 변수값에 반영되야 할경우 사용하는 키워드

func swapTwoInts(inout a:Int, inout b:Int) {
    let tmp = a
    a = b
    b = tmp
}

var a = 5
var b = 10
swapTwoInts(&a, b: &b)

// a = 10, b = 5  
  • operator


문장에 사용되는 키워드

  • switch
  • case
  • break
  • fallthrough : 코드 제어흐름을 다음(next) case 문으로 이동.
let ch = "a"
switch ch {
    case "a":
        print("a", terminator: " ")
        fallthrough
    case "b":
        print("b", terminator: " ")
    default:
        print("unknown")
}
// a b 를 출력함 
  • default
  • continue
  • if
  • else
  • where
  • for
  • in
  • do
  • while
  • return
  • repeat : while 문과 조합하여 1번이상의 반복문을 수행. (기존의 do-while문)
var i = 0
repeat {
    i++
} while i < 10  
  • defer : 실행흐름이 현재 코드블럭을 떠나기 전에 특정 문장집합을 실행하고 싶을 때 사용.
if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // 라인 처리 
        }
        // 블럭을 빠져나가기 전 close(file)를 호출함.
    }  
  • guard : 에러조건이 발생하여 메서드를 조기 종료하고 에러를 보고하기 위해 사용함.
func vend(itemNamed name: String) throws {
        // 에러조건에서 미리 메소드를 종료하고, 에러를 보고
        guard var item = inventory[name] else {
            throw VendingMachineError.InvalidSelection
        }

        guard item.count > 0 else {
            throw VendingMachineError.OutOfStock
        }

        // 정상흐름의 로직 처리
        // ...
}


표현식과 타입

  • is
  • as
  • dynamicType : 인스턴스의 타입 (런타임 시점에 타입을 반환함)
var car = Car()

// 인스턴스의 타입
print(car.dynamicType)
// Car  

// self를 사용하여 타입을 알수도 있음.
print(Car.self)
  • super
  • self
  • Self : 프로토콜 선언에서 사용되며, 해당 프로토콜을 구현한 구현체의 타입을 의미함
protocol Drawable {
    func draw(drawable:Self)
    // func draw(drawable:Drawable)
}

// Drawable 프토토콜을 구현한 Circle 구조체에서 Self = Circle.
struct Circle : Drawable {
    var radus = 10
    func draw(drawable:Circle) {
        print("radus: \(self.radus)")
    }
}
  • __COLUMN__ : 시작열 번호를 나타내는 리터럴
  • __FILE__ : 파일의 이름을 나타내는 리터럴
  • __FUNCTION__ : 선언부의 이름을 나타내는 리터럴.
  • __LINE__ : 시작라인 번호를 나타내는 리터럴

  • try

  • throws
  • rethrows
  • catch


특정문맥 예약어

  • willSet
  • didSet
  • get
  • set
  • left
  • right
  • mutating
  • nonmutating
  • none
  • prefix : 피연산자의 앞에 붙는 연산자를 나타내는 키워드 예) -i
  • infix : 피연산자들 사이에 붙는 연산자를 나타내는 키워드 예) 1 + 2
  • postfix : 피연산자의 뒤에 붙는 연산자를 나타내느 키워드 예) i++
struct Vector2D {
    var x = 0.0, y = 0.0
}

prefix func - (vector:Vector2D) -> Vector2D {
    return Vector2D(x: -vector.x, y: -vector.y)
}

func + (left:Vector2D, right:Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y + right.y)
}

postfix func ++ (inout vector:Vector2D) -> Vector2D {
    vector.x++
    vector.y++
    return vector
}

var left = Vector2D(x: 1, y: 1)
var right = Vector2D(x: 1, y: 1)

left = -left
// left 는 x=-1, y=-1

var sum = left + right
// sum 은 x=0, y=0

sum++
// sum 은 x=1, y=1
  • precedence : 연산자 우선순위.

    둘 이상의 연산자를 포함하는 식에서 연산자의 순서를 나타내는 값.

  • associativity : 결합성

    동일한 우선순위를 갖는 연산자들을 포함하는 식에서 피연산자가 왼쪽 또는 오른쪽의 연산자와 그룹화 되는지 나타내는 값.

  • override

  • unowned
  • weak
  • lazy : 프로퍼티를 처음 사용할 때까지, 초기화 되도록 함. (처음 사용할때까지 초기화를 지연시킴)
class DataImporter {
    var fileName = "data.txt"

    init() {
        print("DataImporter init")
    }
}

class DataManager {
    lazy var importer = DataImporter()
}

let manager = DataManager()

manager.importer
// 처음 lazy 속성에 접근할 때 초기화됨.
  • convenience : 클래스를 위한 convenience 생성자를 선언할때 사용함. convenience 생성자는 다른 convenience 생성자를 호출하거나, designated 생성자를 호출함 수 있음.
  • dynamic : Objective-C 로 표현할수 있는 클래스 멤버에 적용. 멤버에 접근할 때 항상 Objective-C 런타임을 사용하여 디스패치하도록 함.
  • final : 클래스에 적용하면 서브클래싱 할수 없으며, 클래스 멤버에 적용하면 서브클래스에서 오버라이드 할 수 없음.
  • indirect : enum의 연관값의 데이터 구조를 재귀적으로 정의할수 있게함.
enum Tree {
    case Empty
    indirect case Node(value:Int, left:Tree, right:Tree)
}
let tree = Tree.Node(value: 0, left: Tree.Empty, right: Tree.Node(value: 1, left: Tree.Empty, right: Tree.Empty))
  • required : required로 선언된 생성자는 반드시 하위클래스에서 convenience 생성자에서 호출되거나 또는 오버라이드 되야함.
class A {
    var num:Int

    required init(num:Int) {
        self.num = num
    }
}


class B:A {

}

class C:A {
    convenience init(str: String) {
        self.init(num:Int(str)!)
    }
}

class D: A {
    init(str:String) {
        super.init(num: Int(str)! )
    }
    // 컴파일 에러 - 커스텀 designated 생성자를 정의했다면
    // required init(num:Int)를 오버라이드 해야함.
}
  • Type




[swift2] Currying 함수

Swift 2015. 11. 25. 02:10

수학과 컴퓨터 과학에서, Currying은 여러 인자를 취하는 함수를 일련의 단일 인자를 받는 함수들로 변환하는 테크닉을 의미합니다.


두 개의 인자를 받고 그 합을 반환하는 함수를 정의해보겠습니다. 대부분 개발자들에게 익숙한 형태는 다음과 같습니다.

func add1(x:Int, y:Int) -> Int {
    return x + y
}

add1 함수는 2개의 정수를 받아 그 합을 반환합니다. 


Swift 에서는 결과는 동일하지만 다른 형태의 함수를 다음과 같이 정의할 수 있습니다.

func add2(x:Int) -> (Int -> Int) {
    return { y in return x + y }
}

add2 함수는 한개의 인자를 받고, 두번째 인자를 필요로 하는 클로저를 반환합니다. add2는 add1과는 다른 형태로 호출됩니다.

add1(1, 2)
add2(1)(2)


return 문을 생략하고 다음 형태로도 정의 할 수 있습니다.

func add3(x:Int) -> (Int -> Int) {
    return { y in x + y }
}


함수 화살표(->) 는 오른쪽으로 연관을 짓기 때문에, A -> B -> C는 A -> (B-> C)라고 읽을 수 있습니다.  (즉 A 인자를 받고, B->C 타입의 함수를 반환하는 함수)


위와 같이 여러 인자를 요구하는 함수를 하나의 인자를 받는 함수를 연쇄적으로 호출하는 방식으로  변환하는 과정을 논리학자 Haskell Curry 에서 유래하여 currying 이라고 합니다.

add2는 add1의 curried 버전이라고 합니다. Swift에서 curry 함수를 정의할수 있는 세 번째 방법이 있습니다.

func add4(x:Int)(_ y:Int) -> Int {
    return x + y
}

add4(1)(2)


currying의 흥미로운 점은 다른 함수에 인자로 함수를 넘겨야 경우에 알수 있습니다.  add1 같은 uncurried 함수만 정의했다면 함수에 두개의 파라미터를 전달해야합니다. 반면 add2와 같이 curried 함수들에서는 한개의 파라미터 또는 두 개의 파라미터를 전달할지 선택할 수 있습니다.  


// 2개의 파라미터를 전달하여 즉시 결과를 얻는다.
var result = add4(0)(4)
// 4 

// 1개의 파리미터를 전달하고, 결과함수를 보관하여 재활용한다.
var addFour = add4(4)

var array = [1, 2, 3, 4, 5, 6]

var addedArray = array.map { addFour($0) }
// [5, 6, 7, 8, 9, 10]
 




[swift2] Core Image Filter 예제

Swift 2015. 11. 10. 23:42

절차지향 또는 객체지향 프로그래밍에 익숙한 상태에서, 함수형 프로그래밍에 적응하기란 쉽지 않습니다. 다른 프로그래밍 방법론을 공부했을 때처럼 많은 함수형 프로그램 코드를 읽고 이해하고, 또 많은 예제를 실제로 작성해봐야만 숙달될 수 있습니다.

swift 함수형 프로그램의 예로 Core Image 필터 예제를 소개합니다. 이 예제는 objc.io 에서 출판된 Funtional Programming in Swift 에서 발췌하였습니다.

출처: Funtional Programming in Swift

  • 이 예제에서는 MaxOSX Core Image 프레임워크의 함수형 wrapper API 작성을 목표로 합니다.
  • 또한 고차원함수(high-order function)와 같은 함수형 프로그래밍의 실질적인 활용예를 보는 것입니다.


Core Image

  • Core Image는 강력한 이미지 프로세싱 프레임워크지만 사용하기가 불편합니다.
  • Core Image API는 키밸류 코딩을 사용해서 설정할 수 있는 느슨하게 타이핑된 API 입니다.
  • 파라미터 이름을 입력할 때 실수할 수 있는 여지가 많아 런타임 에러를 발생시킬 수 있습니다.
  • 이번에 개발할 API 는 안전하고, 모듈화되었으며, (오타로 인한)런타임 에러가 발생하지 않도록 타입을 장점을 활용합니다.


Filter Type

  • Core Image의 핵심클래스는 이미지 필터를 생성하는데 사용하는 CIFilter 클래스입니다.
  • CIFilter 객체 생성시 반드시 kCIInputImageKey 키에 해당하는 input image를 전달해야합니다.
  • kCIOutputImageKey 키를 사용해서 필터가 적용된 결과 이미지를 얻어올 수 있습니다.
  • 또한 이 결과 이미지는 다른 필터의 입력으로 사용할 수 있습니다.

CIImage 타입을 파라미터로 받아 CIImage 타입의 결과 이미지를 반환하는 함수타입 Filter를 정의합니다.

typealias Filter = CIImage -> CIImage


Blur 필터 함수

  • 이미지에 블러를 적용하는 blur 함수를 정의해보겠습니다.
  • 이 블러필터는 한개의 blur radius 파라미터를 받습니다.
func blur(radius:Double) -> Filter {
    return { image in
        let parameters = [
            kCIInputRadiusKey : radius,
            kCIInputImageKey  : image
        ]
        let filter = CIFilter(name: "CIGaussianBlur", withInputParameters: parameters)
        return filter!.outputImage!
    }
}
  • blur 함수는 CIImage 타입의 이미지를 인자로 받고, 결과 이미지(filter!.outputImage)를 리턴하는 Filter 함수를 반환합니다.


Color Overlay 필터 함수

  • 이미지 위에 특정 컬러 레이어로 덮는 오버레이 필터를 정의해보겠습니다.
  • CoreImage 에는 오버레이 필터가 없지만 이미 있는 필터들을 조합하여 만들것입니다.
  • 컬러를 생성하는 color generator 필터와 레이어를 얹는 source-over compositing 필터를 사용하겠습니다.


Color 레이어 생성필터

func colorGenerator(color:NSColor) -> Filter {
    return { image in
        let parameters = [kCIInputColorKey:CIColor(color: color)!]
        let filter = CIFilter(name: "CIConstantColorGenerator", withInputParameters: parameters)
        return filter!.outputImage!.imageByCroppingToRect(image.extent)
    }
}   
  • blur 필터와 유사하지만 input 이미지가 필요하지 않다는 차이가 있습니다.
  • 생성된 컬러 레이어 이미지를 특정크기(인자로 받은 이미지의 크기)로 잘라주어야 합니다.
  • 다음으로 두 이미지를 복합하는 필터를 정의합니다.


레이어 오버필터

func compositeSourceOver(overlay:CIImage) -> Filter {
    return { image in
        let parameters = [
            kCIInputBackgroundImageKey: image,
            kCIInputImageKey: overlay
        ]
        let filter = CIFilter(name: "CISourceOverCompositing", withInputParameters: parameters)
        return filter!.outputImage!.imageByCroppingToRect(image.extent)
    }
}
  • 마지막에 input 이미지의 크기만큼 결과 이미지를 자릅니다.
  • 두 필터를 결합하여 color overlay 필터를 생성합니다.


Color Overlay 필터

func colorOverlay(color:NSColor) -> Filter {
    return { image in
        let overlay = colorGenerator(color)(image)
        return compositeSourceOver(overlay)(image)
    }
}
  • colorOverlay 함수는 컬러를 인자로 받아 필터함수를 반환합니다.
  • 이 필터함수에 이미지를 전달하면, 앞서 전달한 컬러로 오버레이된 이미지를 반환합니다.


Filter 복합하기

  • blur 필터와 color overlay 필터를 복합해서 사용해보겠습니다.
  • 원본 이미지 파일을 읽습니다.
let path = NSBundle.mainBundle().pathForResource("16.jpg", ofType: nil)!
let image = CIImage(contentsOfURL: NSURL(fileURLWithPath: path))!



  • blur 필터를 적용합니다.
let blurRadius = 5.0
let blurredImage = blur(blurRadius)(image)



  • 빨간색 레이어를 오버레이합니다.
let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2)
let overlaidImage =  colorOverlay(overlayColor)(blurredImage)



Function Composition

  • 위 예제처럼 두 필터함수를 한문장의 표현식으로 표현할 수 있습니다.
let result = colorOverlay(overlayColor)(blur(blurRadius)(image))
  • 하지만 여러 괄호들로 인해 조합된 필터의 수가 많아질 수록 가독성이 나타집니다.
  • 더 좋은 방법은 필터 조합을 위한 커스텀 연산자를 정의하는 것입니다.
  • 먼저 필터를 조합하는 함수를 정의해보겠습니다.
func composeFilters(filter1 filter1:Filter, filter2:Filter) -> Filter {
    return { image in filter2(filter1(image)) }
}
  • 위 함수는 두개의 필터를 인자로 받아 조합하여 새로운 필터를 정의합니다.
  • 반환된 필터 함수는 CIImage 타입의 image 인자를 filter1과 filter2에 순차적으로 전달합니다.
  • 아래와 같이 사용할 수 있습니다.
let myFilter1 = composeFilters(filter1: blur(blurRadius), filter2: colorOverlay(overlayColor))
let result1 = myFilter1(image)
  • 이제 필터 조합을 위한 연산자를 정의하여 좀더 가독성 있게 만들어보겠습니다.
  • 모든 곳에 자체 연산자를 정의할 필요는 없지만, 필터 조합은 이미지 처리 라이브러리에서 반복해서 발생하는 작업이기 때문에 그 용도가 적합합니다.
infix operator >>> { associativity left }

func >>> (filter1:Filter, filter2:Filter) -> Filter {
    return { img in filter2(filter1(img)) }
}
  • 이제 >>> 연산자를 이전에 정의했던 composeFilters 함수처럼 사용할 수 있습니다.
let myFilter2 = blur(blurRadius) >>> colorOverlay(overlayColor)
let result2 = myFilter2(image)
  • >>> 연산자를 left-associativity 로 정의했기 때문에, 유닉스 파이프처럼 왼쪽에서 오른쪽으로 필터가 적용됩니다.
  • 우리가 정의한 필터 조합은 Function Composition 의 한 예입니다.


전체소스

import Cocoa
import XCPlayground


// define Filter Type
typealias Filter = CIImage -> CIImage

// blur function
func blur(radius:Double) -> Filter {
    return { image in
        let parameters = [
            kCIInputRadiusKey : radius,
            kCIInputImageKey  : image
        ]
        let filter = CIFilter(name: "CIGaussianBlur", withInputParameters: parameters)
        return filter!.outputImage!
    }
}


// color layer function
func colorGenerator(color:NSColor) -> Filter {
    return { image in
        let parameters = [kCIInputColorKey:CIColor(color: color)!]
        let filter = CIFilter(name: "CIConstantColorGenerator", withInputParameters: parameters)
        return filter!.outputImage!.imageByCroppingToRect(image.extent)
    }
}

// overlay function
func compositeSourceOver(overlay:CIImage) -> Filter {
    return { image in
        let parameters = [
            kCIInputBackgroundImageKey: image,
            kCIInputImageKey: overlay
        ]
        let filter = CIFilter(name: "CISourceOverCompositing", withInputParameters: parameters)
        return filter!.outputImage!.imageByCroppingToRect(image.extent)
    }
}

// color overlay function
func colorOverlay(color:NSColor) -> Filter {
    return { image in
        let overlay = colorGenerator(color)(image)
        return compositeSourceOver(overlay)(image)
    }
}

// 원본 이미지 읽기
let path = NSBundle.mainBundle().pathForResource("16.jpg", ofType: nil)!
let image = CIImage(contentsOfURL: NSURL(fileURLWithPath: path))!

// apply blur
let blurRadius = 5.0
let blurredImage = blur(blurRadius)(image)

// apply color overlay
let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2)
let overlaidImage =  colorOverlay(overlayColor)(blurredImage)


// define composition function
func composeFilters(filter1 filter1:Filter, filter2:Filter) -> Filter {
    return { image in filter2(filter1(image)) }
}

let myFilter1 = composeFilters(filter1: blur(blurRadius), filter2: colorOverlay(overlayColor))
let result1 = myFilter1(image)


// define composition operator
infix operator >>> { associativity left }

func >>> (filter1:Filter, filter2:Filter) -> Filter {
    return { img in filter2(filter1(img)) }
}

let myFilter2 = blur(blurRadius) >>> colorOverlay(overlayColor)
let result2 = myFilter2(image)





'Swift' 카테고리의 다른 글

[swift2] 키워드로 보는 Swift  (0) 2015.11.30
[swift2] Currying 함수  (0) 2015.11.25
[swift2] Core Image Filter 예제  (0) 2015.11.10
[swift2.1] Xcode7.1 Playground 변경사항  (0) 2015.10.27
[swift2] Functor 와 Monad  (0) 2015.10.25
[swift2] 타입변환 연산자 (is, as, as?, as!)  (0) 2015.10.18

[swift2] Swift Guard

Swift 2015. 9. 11. 21:33

Guard 문을 이해하기 쉽게 설명하고 있는 글을 발견하여 소개합니다. 원문을 조금 의역하고 예제를 수정했습니다.

원문: Swift's Guard

Swift2.0에 추가된 guard 구문은 멋집니다. 실 세계에서 가드가 하는 것과 같은 유사한 역할을 하지요. 학교 경비는 학교에 소속된 학생들만 교문을 입장하도록 허용합니다. 소속되지 않는 학생이 들어오려고 하면, 저지하고 되돌려 보냅니다. guard 문은 뒤따르는 조건이 충족되지 않을 경우, 로직실행을 멈추고 함수를 반환합니다.

guard 문의 사용법에 대해 몇가지 알아보겠습니다.


1. 함수안에서 Guard

  • 함수 안에서 guard 문은 올바른 입력에 대해서만 함수의 실행을 허용합니다.
func guardInFunction(string: String?) {
    guard string != nil else { return }
    print(string!, "second param", separator: "  ", terminator: "\n")
}

guardInFunction("test")

guardInFunction(nil)

위 함수는 nil 입력을 가드합니다. 만일 입력이 nil이면 guard문은 else 절을 수행하고 다른 구문이 수행되기 전에 함수를 return합니다.


2. 옵셔널 바인딩(Optional Binding)

옵셔널 바인딩을 guard문과 조합하여 사용하면, 이전의 코드를 다음과 같이 재작성 할 수 있습니다.

func guardWithOptionalBinding(string: String?) {
    guard let letString = string else { return }
    print(letString, "second param", separator: "  ", terminator: "\n")
}

guardWithOptionalBinding("test")

인자 string이 nil이면 else 절을 수행하고 그렇지 않으면 함수는 정상흐름을 계속하고, string을 출력합니다. if-let 문법을 사용한 옵셔널 바인딩을 사용해봤다면 무엇인가 다르다는 것을 느낄수 있을 것입니다. 옵셔널 바인딩된 상수를 조건문 범위 바깥에서도 사용할 수 있습니다. if-let의 경우에는 if문 조건을 만족하는 경우 if 블럭 내부에서만 사용할 수 있습니다.

guard 문의 경우에 조건이 true이면, 상수의 사용범위는 함수의 나머지 영역이 됩니다. guard문 이후 어디에서라도 사용할수 있습니다.


3. 복합 옵셔널 바인딩(Compound Optional Binding)

다음 예제처럼 복합해서 옵셔널 바인딩을 사용할 수 있습니다. 모든 파라미터가 nil이 아닐경우에 조건을 충족하고 출력문을 수행할 수 있습니다.

func guardWithCompoundOptionalBinding(firstName: String?, lastName:String?, dateOfBirth:String?, country:String? ) {
    guard let fname = firstName, let lname = lastName, let dob = dateOfBirth, let cnt = country else { return }

    print("\(fname) \(lname) \(dob) \(cnt)", separator: "", terminator: "\n")
}

guardWithCompoundOptionalBinding("sungbae", lastName: "kim", dateOfBirth: "", country: "korea")


4. 제어흐름 이동(Transferring Control)

위 예제에서 주목할 만한 것은, 조건이 충족되지 않으면 함수로부터 반환되는 것입니다. 이것은 guard 문이 조건을 만족하지 않으면 제어흐름의 이동시키는 구문을 가져야 하기 때문입니다. 위 예제에서 우리는 return문을 사용했습니다. 루프의 경우에는 continue와 break를 사용할 수 있습니다. 또한 우리는 error를 throw 할 수 있습니다. 예제를 보겠습니다.

func guardTransferringControl() {
    var strings:Array<String> = ["Safe", "Coding", "why", "using", "guard", "statement"]

    for string in strings {
        guard string != "why" else {
            continue
        }
        print("string", separator: "", terminator: "\n")
    }
}

guardTransferringControl()

조건문을 만족하지 않으면, else 구문에서 제어흐름을 이동시키기 위해 continue를 사용했습니다.

이것이 guard 문이 하는 일입니다.

Swift 글 목록

Swift/글 목록 2015. 9. 3. 13:55


Xcode7.3 Release Notes


Swift 소개


Swift의 기초


Playground


String


Error Handling


Protocol Oriented Programming


Performance


Functional Programming


Swift로 앱만들기



Xcode7 beta Release Notes

'Swift > 글 목록' 카테고리의 다른 글

Swift 글 목록  (0) 2015.09.03

[swift2] Swift2에서의 문자열(strings)

Swift 2015. 9. 2. 22:47

다음 포스트는 애플 공식 Swift 블로그의 Strings in Swift2 포스트를 번역했습니다.


Swift는 표준 라이브러리의 일부분으로, 성능이 뛰어나고 유니코드와 호환되는 String 구현을 제공합니다. Swift2에서, String 타입은 더이상 CollectionType 프로토콜을 구현하지 않습니다. (이전에는 String은 배열과 유사한 Character 값의 컬렉션이었습니다.) 이제 String은 character 컬렉션을 제공하는 characters 프로퍼티를 제공합니다.

무엇이 바뀌었을까요?
문자들의 컬렉션이 자연스러운 모델이지만, String 타입은 Array, Set, Ditionary와 같은 컬렉션 타입과 꽤 다르게 동작합니다. Swift2에서 프로토콜 extension이 추가되면서 몇몇 변경사항이 생겼습니다.



부분의 합과 다릅니다.

컬렉션에 엘리먼트를 추가하면, 컬렉션은 그 엘리먼트를 포함할 것이라 기대할수 있습니다. 즉 배열에 값을 추가하면, 배열은 그 값을 포함합니다. 동일한 규칙이 dictionary와 Set에도 적용됩니다. 그러나 string에 결합마크 문자를 추가하면 string 자체의 내용이 변합니다.

4개의 문자를 갖는 문자열 cafe를 예를 들어보겠습니다.

var letters: [Character] = ["c", "a", "f", "e"]
var string: String = String(letters)

print(letters.count) // 4
print(string) // cafe
print(string.characters.count) // 4

만일 악센트 문자 U+0301 ´를 결합하면, 문자열은 그대로 4글자 이지만 마지막 문자는 이제 é 입니다.

let acuteAccent: Character = "\u{0301}" // ´ COMBINING ACUTE ACCENT' (U+0301)

string.append(acuteAccent)
print(string.characters.count) // 4
print(string.characters.last!) // é 

string의 characters 속성은 오리지널 소문자 e를 포함하지도 않고, 이전에 추가됐던 악센트 문자 ´도 포함하지 않습니다. string은 이제 악센트가 붙은 소문자 "e" 즉 é를 포함합니다.

string.characters.contains("e") // false
string.characters.contains("´") // false
string.characters.contains("é") // true

만일 다른 컬렉션처럼 string을 다룬다면 결과는 Set에 UIColor.redColor() 과 UIColor.greenColor()를 추가하면 set은 UIColor.yellowColor() 포함한다고 리포트하는 것만큼 놀라울 것입니다.



문자의 내용에 따라 결정됩니다.

string과 collection의 또 다른 차이점은 동등성을 판단하는 방법입니다.

  • 두 배열은 엘리먼트의 개수가 같고, 대응하는 인덱스에 각 엘리먼트 쌍이 동일하면 동등합니다.
  • 두 집합은 엘리먼트의 개수가 같고, 첫 번째 집합에 포함된 각 엘리먼트가 두 번째 집합에도 포함되어 있을때 동등합니다.
  • 두 사전은 같은 키,값 쌍을 가지고 있을때 동일합니다.

그러나 String은 canonically equivalent 에 기초하여 동등성이 결정됩니다. 문자는 같은 언어적 의미와 외형을 가진다면 다른 유니코드 스칼라로 구성되어 있다하더라도 cannonically 동등합니다.

canonically equivalent: 유니코드 문자 인코딩 표준에 정의된 스펙. 코드포인트 순서집합이 출력되었을 때, 같은 외형과 의미를 갖을때 canonically equivalent 하다고 표현합니다.

자음과 모음을 나타내는 24개의 문자로 구성된 한국어 표기법을 예로 들어보겠습니다. 표기할때 각 문자들은 각 음절이 결합하여 글자를 이룹니다. 예를 들어 글자 '가'는 문자 'ㄱ' 과 'ㅏ' 로 구성됩니다. Swift에서는 String은 조합형인지 완성형인지에 상관없이 동등성을 판단합니다.

let decomposed = "\u{1100}\u{1161}" // ᄀ + ᅡ
let precomposed = "\u{AC00}" // 가

decomposed == precomposed // true

즉 String은 다른 Swift 컬렉션 타입의 어떤것들과도 완전 다르게 동작합니다. 🐟과 🍚 값으로 구성된 배열이 🍣과 동등하다고 여겨진다면 매우 놀라울 것입니다.



당신의 관점에 달려있습니다.

string은 collection이 아닙니다. 대신 string은 CollectionType 프로토콜을 따르는 뷰들을 제공합니다.

  • characters는 Character 값 또는 extended grapheme cluster의 컬렉션입니다.
  • unicodeScalars 는 유니코드 스칼라 값들의 컬렉션입니다.
  • utf8는 UTF-8 코드 유닛의 컬렉션입니다.
  • utf16은 UTF-16 코드 유닛의 컬렉션입니다.

extended grapheme cluster: 유니코드 스칼라 묶음이 조합되었을 때, 사람이 읽을 수 있는 문자가 되는 구성.

이전이 예제에서 단어 café 는 [ c, a, f, e ] 와 [ ´ ] 로 구성되어 있으며, string의 다양한 뷰들은 아래와 같습니다.



  • characers 속성은 사용자가 인식하는 문자인 extended grapheme clusters로 분리합니다. string은 문자열 내부에서 문자 경계를 결정하기 위해 코드 포인트라고 하는 각 위치를 순회해야 하기 때문에, 이 속성에 대한 접근은 O(n) 시간이 걸립니다. 인간이 읽을수 있는 텍스트를 포함하는 문자열을 처리할 때, localizedStandardCompare(_:) 메서드나 localizedLowercaseString 속성에서 사용되는 하이레벨 locale-sensitive 유니코드 알고리즘들에서 문자단위 처리가 자주 사용됩니다.

  • unicodeScalars 속성은 문자열에 저장된 내부의 스칼라 값을 노출합니다. 만일 원래 문자열이 조합형 e + ´ 로 구성되지 않고 완성형 é로 생성되었다면, 이런 구성이 유니코드 스칼라뷰에 반영됩니다. 문자 데이터의 low-level 조작을 수행할때 이 API를 사용하세요.

  • utf8과 utf16 속성들은 UTF-8과 UTF-16 표현에 대한 코드포인트를 제공합니다. 이 값들은 특정 인코딩으로부터 혹은 인코딩으로 변환할때 파일에 쓰여지는 실제 바이트에 대응합니다. UTF-8 코드 유닛은 많은 POSIX 문자열 처리 API에서 사용되며, UTF-16 코드 유닛은 문자열의 길이와 오프셋을 표현하기 위해 Cocoa & Cocoa Touch에서 전반적으로 사용됩니다.

[swift2] Xcode7 beta6 Swift 언어 변경사항

Swift 2015. 8. 27. 01:48


Swift Language Enhancements and Changes


1. try? 키워드가 추가되었습니다.

  • try? 구문은 에러를 던질수도 있는 연산을 수행합니다.
  • 연산이 성공하면 결과는 optional로 랩핑되어 반환되고, 에러가 throw 되면 결과는 nil를 반환하고 error는 버려집니다.
  • try?는 if let과 guard 문과 함께 사용될 때 특히 유용합니다.
    func produceGizmoUsingTechnology() throws -> Gizmo { … }
    func produceGizmoUsingMagic() throws -> Gizmo { … }

    if let result = try? produceGizmoUsingTechnology() { return result }
    if let result = try? produceGizmoUsingMagic() { return result }
    print("warning: failed to produce a Gizmo in any way")
    return nil
  • try?에 의해 평가되는 표현식의 결과타입에 항상 optional이 추가된다는 것을 주목하세요.
  • throwing 함수의 반환타입이 Int?이면, try?와 함께 함수를 호출한 결과는 Int?? 또는 'Optional<Optional>' 이 됩니다.


2. Xcode는 . 문법을 사용할 때, 문맥을 인식한 enum 엘리먼트와 option sets의 코드완성 기능을 제공합니다.


3. 프로토콜 extension Static 계산속성을 정의할 수 있습니다.


4. 함수 또는 생성자 파라미터 리스트 어떤 위치에도 가변인자가 올 수 있습니다.

func doSomethingToValues(values: Int..., options: MyOptions = [], fn: (Int) ->
Void) { 
    // function body 
}


5. Objective-C와 호환되지 않는 타입을 포함하는 컬렉션은 더이상 Objective-C 호환타입으로 간주되지 않습니다.

  • 이전에는 Array 구문은 @objc가 마킹된 프로퍼티로 허용되었지만, 그러한 케이스는 더이상 허용되지 않습니다.


6. C typdefs으로 선언된 block은 이제 Swift 클로져 typealias로 임포트됩니다.

  • BOOL 타입 파라미터가 사용된 typedefs 블럭은 Bool 타입 파라미터를 가진 클로져로 임포트됩니다.(이전 베타5에서는 ObjCBool 파리미터로 되었음.)
  • 이것은 블럭 파라미터의 행동과 임포트된 Objective-C 메서드를 매칭시킵니다.


7. 타입체커가 생성한 에러메시지는 구체적이고 유용하게 개선되고 있습니다.

  • 진단시에, type alias를 사용했다면 별칭(aka)을 출력합니다.


Swift Standard Library Enhancements and Changes


1. print()와 debugPrint()가 개선되었습니다.

  • 가변인자 지원하여, 한번의 호출로 여러개의 아이템을 출력할수 있습니다.
  • separator: String = " " 가 추가되어, 어떻게 아이템이 구분되는지 제어할 수 있습니다.
  • appendNewline:bool = true 는 terminator:String = "\n"로 교체되었습니다.
  • output stream을 인자로 받는 print 함수에는 stream 인자에 toStream 레이블을 추가했습니다.


2. RangeReplaceableCollectionType.extend() 메소드가 appendContentsOf()로 이름이 변경되었습니다.

  • splice() 메서드는 insertContentsOf()로 이름이 변경되었습니다.


3. 클로져와 @autoclosure 인자를 갖는 대부분의 표준라이브러리 API은 이제 rethrows를 사용합니다.

  • map과 filter 같은 메서드의 클로져 파라미터들이 에러를 throw할 수 있게 허용합니다.
  • 에러를 발생시킬 수 있는 표현식과 &&, ||, ?? 같은 short-circuiting 연산자를 함께 사용할수 있습니다.


4. 모든 CollectionType은 이제 sliceable 합니다.

  • 다음 sequence spllitting/slicing 함수들은 SequenceType 프로토콜 요구사항에 맞게 삭제되거나 프로토콜 extension 디폴트 구현으로 대체되었습니다.
/// Returns the first `maxLength` elements of `self`,
/// or all the elements if `self` has fewer than `maxLength` elements.
prefix(maxLength: Int) -> SubSequence

/// Returns the last `maxLength` elements of `self`,
/// or all the elements if `self` has fewer than `maxLength` elements.
suffix(maxLength: Int) -> SubSequence

/// Returns all but the first `n` elements of `self`.
dropFirst(n: Int) -> SubSequence

/// Returns all but the last `n` elements of `self`.
dropLast(n: Int) -> SubSequence

/// Returns the maximal `SubSequence`s of `self`, in order, that
/// don't contain elements satisfying the predicate `isSeparator`.
split(maxSplits maxSplits: Int, allowEmptySlices: Bool, @noescape isSeparator:
(Generator.Element) -> Bool) -> [SubSequence]
  • split를 위해 다음 convenience extension을 제공합니다.
split(separator: Generator.Element, maxSplit: Int, allowEmptySlices: Bool) ->
[SubSequence]  
  • 또한 새로운 프로토콜 요구사항과 컬렉션 타입의 디폴트 구현이 사용가능합니다.
/// Returns `self[startIndex..<end]`
prefixUpTo(end: Index) -> SubSequence

/// Returns `self[start..<endIndex]`
suffixFrom(start: Index) -> SubSequence

/// Returns `prefixUpTo(position.successor())`
prefixThrough(position: Index) -> SubSequence





[swift2] Xcode7 beta5 Swift 언어 변경사항

Swift 2015. 8. 14. 02:14


Linker

  • bitcode를 활성화하면 이제 weak frameworks와 weak libraries가 지원됩니다. linker는 더이상 -weak_framework, -weak-l 또는 -weak_librarys가 -bitcode_bundle 과 함께 사용될수 없다고 경고하지 않습니다.


Swift Language Changes

1. 구조체, 클래스도 이제 ErrorType 프로토콜을 구현할수 있습니다.


2. 실패가능한 initializer에 위임하거나 체이닝할 때 !를 사용하여 force-unwrap 할 수 있습니다.

  • self.init(...) 또는 super.init(...)가 실패가능한 경우 아래코드와 같이 force-unwrap 할 수 있습니다.
extension UIImage {
    enum AssetIdentifier: String {
        case Isabella
        case William
        case Olivia
    }
    convenience init(assetIdentifier: AssetIdentifier) {
        self.init(named: assetIdentifier.rawValue)!
    }
} 
  • 위와같이 실패가능한 initializer에 체이닝한 non-optional initializer 정의가 가능해집니다.


3. 성능향샹을 위한 -Onone 빌드 옵션

  • -Onone 옵션 (debug) 빌드를 사용하면 표준라이브러리 제네릭 타입에 특화된 인스턴스를 사용하여 빌드성능을 향상시킬 수 있습니다.
  • 많은 경우에 컴파일 타임에 영향없이 debug 빌드에서 상당히 빠른 실행파일을 생성합니다.


4. Objective-C 클래스의 제네릭 서브클래스와 이것을 상속하는 non-제네릭 클래스들은 runtime metadata 인스턴스화가 요구됩니다.

  • Objective-C 코드에서 직접적으로 네이밍을 지정할수 없습니다.
  • Beta4에 Objective-C 클래스들의 제네릭 서브클래스 정의가 가능해졌지만, 생성된 Objective-C 브리징 헤더에는 그러한 클래스들을 에러항목으로 리스팅합니다. 이로인해 잘못된 런타임 행동과 컴파일 타임에러를 발생시키는 오류를 수정하였습니다.
  • 클래스에 대한 @objc 속성의 행동이 좀더 명확해졌습니다.
  • 브리징헤더에 나타날 수 없는 클래스에 @objc를 적용하면 이제 에러입니다.
  • 클래스의 어떤 수퍼클래스라도 @objc 클래스라면, 그 클래스의 멤버들은 암묵적으로@objc 이며, 모든 @objc 클래스들은 NSObject 를 상속해야함. 따라서 유효한 코드에 대한 어떤 변경을 발생시키지는 않습니다.


5. MacTypes.h 에 정의된 Boolean은 Swift와 Objective-C 타입 브리징 상황에서 Bool로 임포트됩니다.


6. NSManaged 속성을 CoreData가 자동생성한 키밸류코딩 호환 to-many 접근자에 접근하기 위한 메서드와 프로퍼티에 사용할 수 있습니다.

@NSManaged var employees: NSSet

@NSManaged func addEmployeesObject(employee: Employee)
@NSManaged func removeEmployeesObject(employee: Employee)
@NSManaged func addEmployees(employees: NSSet)
@NSManaged func removeEmployees(employees: NSSet)
  • 이것들은 NSManagedObject 서브클래스에 선언되어야 합니다.


7. 타입 이름들과 enum cases들은 이제 디폴트로 자격없이(프로토콜 구현없이) print하고 String으로 변환할수 있습니다. debugPrint와 String(refelcting:)은 fully-qualified 이름을 얻기 위해 사용할 수 있습니다.

enum Fruit { case Apple, Banana, Strawberry }
print(Fruit.Apple)           // "Apple"
debugPrint(Fruit.Apple)      // "Fruit.Apple"


8. print()와 Mirrors의한 reflection은 current case와 멀티 payload 타입을 갖는 모든 enums payload를 report 할수 있습니다.

  • 유일하게 reflection이 지원되지 않고 남아있는 enum 타입들은 @objc enums와 C에서 임포트된 enums 입니다.


Swift Standard Library Enhancements and Changes

1. SequenceType 프로토콜 구현체에 .forEach 구문을 사용할 수 있습니다.

(0..<10).forEach {
    print($0)
}    

위 구문은 아래구문과 유사합니다.


for x in 0..<10 {
    print(x)
}

하지만 다음과 같은 차이점이 있습니다.

  • for-in 루프문과 다르게 break, continute를 사용하여 현재 클로져 몸체 호출을 종료하거나 일련의 호출을 건너뛸수
    없습니다.

  • 클로져 몸체에 return문을 사용하면 외부 스콥이 아닌 현재 호출만 종료할수 있으며, 이어지는 일련의 호출을 건너뛸수 없습니다.

  • 이러한 제약때문에 forEach 멤버는 함수형 알고리즘에 일련의 체이닝을 적용하고, 몸체가 적을때만 사용하도록 권장합니다.

foo.map {...}.filter {...}.forEach {...}
  • 다른 경우에는 for..in 문 사용을 권장합니다.


3. SIMD: simd 모듈의 정수형 벡터타입은 &+, &-, & 연산자를 사용하여 감싸진 unchecked 산술식만 지원합니다. +,-, 연산자들은 정수형 벡터에는 사용 불가능하고 Xcode가 자동으로 wrapping 연산자로 교체하라고 제안할 것입니다.


4. SIMD: smid 모듈의 정수형 벡터타입 코드생성은 벡터하드웨어를 더 잘 사용할 수 있도록 개선되어 대부분의 경우에 극적으로 성능이 향상되었습니다.


5. Dictionary의 removeAtIndex(_:) 메서드는 key,value 쌍을 제거하여 두 엘리먼트를 튜플로 반환합니다.

  • 유사하게 Set의 removeAtIndex(_:)은 삭제된 엘리멘트를 반환합니다.


6. Word와 UWord 타입은 표준라이브러리에서 삭제되었습니다. 대신 UInt를 사용하세요.




[swift2] Xcode7 beta4 Swift 언어 변경사항

Swift 2015. 7. 30. 00:12

드디어 Xcode7 beta4가 공개되었네요! beta4 에서 Swift 언어 변경사항에 대해 알아볼까요?

xcode7_beta4_title


Objective-C Language Changes

1. 더블 언더스코어 nullability 한정자들 이름이 변경되었습니다.

  • as is: __nullable, __nonnull, __null_unspecified
  • to be: _Nullable, _Nonnull, _Null_unspecified
  • 소스 호환성을 위해 컴파일러가더블 언더스코어 이름을 새로운 이름으로 맵핑하는 매크로를 정의해줍니다.(predefine)


Swift Language Changes

1. Enum과 Case에 indirect 키워드를 사용할 수 있습니다.

  • indirect 키워드를 사용해서 재귀적인 데이터 구조를 정의할 수 있음.
  • 재귀적인 데이터 구조 정의시 enum의 연관값은 간접적으로 저장됨
enum List<T> {
    case Nil
    indirect case Cons(head: T, tail: List<T>)
}  

indirect enum Tree<T> {
    case Leaf<T>
    case Branch(left: Tree<T>, right: Tree<T>)
}


2. class 객체를 참조하는 AnyObject, NSObject 변수를 class Object 타입으로 복원이 가능합니다.

 let x:AnyObject = NSObject.self // NSObject 클래스 객체를 AnyObject로 캐스팅
 let y = x as! NSObject.Type     // AnyObject를 NSObject 클래스 객체로 복원 


3. class 객체를 포함하는 Array,Dictionary,Set 을 NSArray,NSDictionary,NSSet로 브리징할 수 있습니다.

  • [NSURLSessionConfiguration protocolClasses]과 같이 NSArray * 객체를 반환하는 Objective-C API도 이제 Swift에서 정상동작합니다.


4. 클래스에 적용한 @objc 속성은 Objective-C 헤더에 생성된 클래스의 컴파일타임 이름 뿐만 아니라 런타임 이름도 변경합니다.

  • 프로토콜에도 적용됨
// Swift
@objc(MyAppDelegate)
class AppDelegate : NSObject, UIApplicationDelegate {
    //...
}  
// Objective-C
@interface MyAppDelegate : NSObject <UIApplcationDelegate>
// ...
@end


5. 스칼라값의 0 엘리먼트를 참조하는 기능은 제거되었습니다.


6. 타입검사 제약시스템이 생성한 에러 진단메시지가 beta4에서 개선되었습니다. 이후 베타버전에서 더 많은 부분들이 개선될 예정입니다.


7. SinkType 프로토콜과 SinkOf 구조체가 표준 라이브러리에서 제거되고 대신 (T) -> () 클로져를 사용합니다.


8. ExtensibleCollectionType 부모프로토콜이 서브 프로토콜 RangeReplaceableCollectionType로 통함되었습니다.

  • RangeReplaceableCollectionType 은 subRange를 다른 컬렉션의 엘리먼트들로 교체를 지원하는 컬렉션 타입.
  • 기본구현을 위한 메소드들이 추가됨.
  • 이 프로토콜들과 연관된 (free) Swift 모듈 함수들 대신 이 메서드들을 사용할 수 있습니다.


9. Unmanaged를 사용한 속성과 메서드들도 Objective-C에 노출가능합니다.

  • Unmanaged는 unmananged 객체 레퍼런스를 전달하기 위한 타입.
  • 이 타입을 사용하면 객체가 살아있도록 유지할 책임이 있음.


10. performSelector 류의 API 들 Swift 코드에서 사용가능합니다.


11. __unsafe_unretained로 마크된 C 구조체 필드는 Swift에 Unmanaged 로 표시됩니다.

  • Swift 컴파일러는 이 참조들이 실제로 strong(+1) 또는 unretained(+0)로 의도되었는지는 알수 없음.


12. Swift 문서 주석들은 최상위 목록아이템을 인식합니다.

  • Throws: ... 구문은 어떤 에러가 왜 던져질수 있는지 문서화하기 위해 사용합니다.
  • Xcode QuickHelp의 파라미터 및 반환값 설명 옆에 표시됩니다.


13. 타입은 그 타입보다 더 낮은 버전에서 가용한 프로토콜을 구현할 수 있습니다.

@available (iOS 9.0, *)
protocol P { ... }

@available (iOS 7.0, *)
class MyController : UIViewController, P {
    ...   
}


[swift2] Xcode7 beta2 Swift 언어 변경사항

Swift 2015. 6. 24. 21:56



Swift Language Changes


  • payload를 가진 Enum case들은 함수처럼 사용될수 있습니다.
 enum Either<T, U> { 
    case Left(T), Right(U) 
 }
 let lefts: [Either<Int, String>] = [1, 2, 3].map(Either.Left)
 let rights: [Either<Int, String>] = [“one”, “two”, “three”].map(Either.Right) 
 // => {{Left 1}, {Left 2}, {Left 3}}

  • 구조체, enum, 프로토콜의 non-mutationg 메소드들에 부분적으로 self 파라미터가 적용됩니다.
let a: Set<Int> = [1, 2, 3]
let b: [Set<Int>] = [[1], [4]]
b.map(a.union)   
// => [[1, 2, 3], [1, 2, 3, 4]]

  • 정적타입 참조 또는 타입객체에 static 메소드처럼 .init를 사용하여 생성자를 초기화함수에 접근할 수 있습니다.
let x = String.init(5)
let stringType = String.self
let y = stringType.init(5)
let oneTwoThree = [1, 2, 3].map(String.init).reduce(“”, combine: +)  
// => "123"

  • String(5) 처럼 정적타입을 사용해서 생성할때도 여전히 .init은 암묵적으로 호출됩니다. .init은 동적타입 객체를 사용할 때, 함수인자로 initializer를 참조할 때 사용됩니다.


  • 거대한 구조체 또는 enum 타입의 코드를 생성할 때 코드 크기를 감소시키도록 개선되었습니다.


  • 이제 Swift는 assertion이 활성화 된채 빌드됩니다. 실용적인 crash report를 생성하는데 도움이 될것입니다.


  • 실행결과