[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