오늘은 초기화 메소드에 대한 포스팅이다. Objective-C로 개발을 하다보면 가장 많이 구현하는 부분이 초기화 메소드일 것이다. 일반적으로 아래와 같이 초기화 메소드를 사용하게 된다.


-(instancetype)init

{

        self = [super init];

        if (self) {

            //impelment here

        }


        return self;

}


    이중에서 우리가 얘기할 부분은 위에 하이라이팅된 부분이다. 굳이 메모리에 할당되어 있는 self 객체를 슈퍼클래스에서 초기화한 객체로 다시 assign하는 것일까? 주변 다른 개발자 분으로부터 그 해답을 얻었다.


    우선 해답은 super class의 초기화 과정을 거치면서 할당된 메모리의 주소가 변경될 수 있기 때문이다. 메모리 주소가 초기화 과정에서 변경되는 이유는 필자가 Swift의 let (literal)을 통해 이미 공부했던 'String Intern Pool'과 관련되어 있었다.


    객체 지향 언어에서 객체를 할당할 때, 내부적으로는 상수로 관리한다. 상수를 할당하고 각 객체는 그 메모리 주소를 가르키고 있는 것이다. 이렇게 관리하는 이유는 실제로 2개 이상의 객체가 같은 값의 value를 소유하고 있을 때, 개수만큼 메모리를 할당하고 객체를 생성하는 것이 아니라 Pool 안의 같은 상수를 바라보게 함으로써 메모리의 낭비를 줄이기 위함이다. 이해를 돕기 위해 Swift에서 Literal을 공부할 때 사용했던 슬아이드 이미지 를 아래 첨부 하겠다.





    위의 그림처럼 실제로 NSString 객체는 4개이지만 value의 종류가 2개이기 때문에 메모리에 각각 4개의 NSString이 할당되는 것이 아니라 2개의 NSString만 할당되어 메모리를 절약할 수 있는 것이다. 그리고 이를 이용한 이점이 하나 더 있다.


    아래의 이미지를 보면 알 수 있다. NSString이나 NSNumber와 같이 value의 일치를 확인하는 logic을 수행할 때, NSString의 경우 각 문자에 접근하여 비교를 할 경우 아래 2번째 이미지처럼 O(n)의 시간 복잡도를 수행하게 된다. 하지만 Pool과 상수를 이용할 경우 주소값만 비교하면 되기 때문에 비교 연산 시, O(1)의 시간 복잡도로 단순화 된다. 이번에도 이해를 돕기 위해 Swift에서 Literal을 공부 할 때 사용한 슬라이드 이미지를 첨부하겠다.







    애플에서 Swift를 오픈소스화 한다는 공약을 실천했다. 현재 레파지토리는 Github를 이용하고 있으며 담당 사이트로 Swift.org를 개설했다. 이슈 트래킹은 Jira를 이용한다. Mailing-list를 통하여 Swift의 변동사항과 개발 이슈/동향 등을 reporting 받거나 다른 개발자들과 소통 창구로 이용할 수 있다. 아래 Swift 관련 URL을 모아 놨다. 역시 애플에서 관리하는 프로젝트라서 Contribution section에 있는 가이드를 꼼꼼히 읽고 접근하는 것이 좋을 것 같다.



Apple in Github : https://github.com/apple

Swift Repository : https://github.com/apple/swift


Swift.org : https://swift.org

Mailing-Lists : https://swift.org/community/#mailing-lists

Bugs in Swift with Jira : https://bugs.swift.org/secure/Dashboard.jspa

Map

map은 각 element를 순회하면서 element를 수정할 때 사용하기 좋은 함수이다. 예를 들어 우리는 각 String에 "box"라는 String을 덧붙이기 위하여 for-in을 떠올리기 쉽다.


ex) 

for string in strArray {

string += "box"

}


대신에 map 함수를 사용하면 한줄로 간단하게 작성할 수 있다.


ex)

var resultArray = strArray.map({$0+"box"})


참고로 map의 param 함수 타입은 '(T) -> U'이다. map 함수는 Array의 각 element를 순회하면서 개발자가 정의한 동작을 수행하게 된다.


Filter

filter는 말 그대로 특정 조건을 통과한 element들을 모아서 Array로 반환하는 함수 이다. filter의 param 함수 타입은 '(T) -> Bool'이다. 작성하면 아래와 같다.


ex)

var filteredArray = moneyArray.filter({$0<1_000})


Reduce

reduce는 element들을 하나의 value로 통합하는 함수이다. reduce의 함수 원형은 'func reduce<U>(initial: U, combine: (U, T) -> U) -> U'이다. 이를 이용하여 작성하면 아래와 같다.


ex)

var sum = moneyArray.reduce(0, combine: +)


moneyArray의 element는 Integer로 각 element의 합을 구하는 것이다. 혹은 아래와 같이 String을 reduce할 경우, element들을 하나의 String으로 통합할 수도 있다.


ex)

var aString = resultArray.reduce("", combine: {$0+$1 + "!! "})


Function parameters
함수의 param들은 기본적으로 다 상수다. 그러므로 수정이 불가능하다. 함수 내에서 param을 수정하기 위해선 inout param으로 선언하면 되는데, 함수 선언부에서 param앞에 '&'를 표시해주면 된다.

Function type
function type을 다른 function의  param의 타입으로 사용할 수 있다. 함수의 타입이 들어가는 부분에  '(Int, Int) -> Int'와 같이 일반 함수 선언 부에서 함수명의 뒷부분을 적어주면 된다. function type을 사용해서 함수를 param으로 사용할 수 있다.

ex)
func addTwoInts(a: Int, b: Int) -> Int {
    return a + b
}

func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
    println("Result: \(mathFunction(a, b))")
}

printMathResult(addTwoInts, 3, 5)
// prints "Result: 8’

또, function type을 return  type으로 또한 사용이 가능하다. 함수 선언부에 return을 적어 주는 부분에 function type을 적으면 된다.

ex)
func stepForward(input: Int) -> Int {
    return input + 1
}
func stepBackward(input: Int) -> Int {
    return input - 1
}

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    return backwards ? stepBackward : stepForward
}

var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function

Closures
closure는 코드 상에서 나뉘어 있고 사용되는 기능을 스스로 내포한 블록이다. C와 objective-C의 block과 다른 언어의 lambda와 비슷하다. 다만, block과 closure의 다른 점은 block는 단순한 instruction의 모음 단위이다. closure는 실제로 block을 value로 소유한 객체라고 생각하면 된다. 간단한 예로 value를 block에서 쓰려면 block에서 사용한다는 표시하기 위해 '__block' 키워드를 사용하지만 closure는 실체를 가진 reference이므로 정의할 때 당시의 주변 context의 value 자체에 대한 refence count를 증가시켜 capturing이 가능한 것이다.

다른 개발자의 표현을 빌리자면 'closure는 함수 포인터와 비슷하지만 함수 포인터는 함수 내부의 변수만 접근이 가능한 반면 closure는 정의할 당시의 주변의 context의 변수 들에 접근이 가능하다'라고 서술되어 있다.

전역 함수와 중첩 함수도 closure의 유형으로 아래와 같다.

    • 전역 함수는 함수명을 가지지만 value를 capture하지 않는다.
    • 중첩 함수는 함수명을 가지면서 자신의 함수 중첩 내의 value를 capture할 수 있다.
    • closure는 이름을 가지지 않으며 주변의 context의 value를 capture할 수 있다.


closure의 capturing value에 관해서는 뒤에서 자세히 설명할 것이다. 먼저, closure의 syntax는 아래와 같다.

ex)
{ (parameters) -> return type in
    statements
}

'in' 키워드는 closure에서 param과 return의 선언이 끝나고 body가 시작됨을 알려준다. closure의 body는 너무 짧아 한 줄로 끝날수도 있으므로 'in'이라는 키워드를 사용하여 명시적으로 표시한다.

Operator function
closure의 축약형이다. 애플의 swift 서적의 예로 설명하면 아래와 같다. 먼저, 아래와 같은 closure가 존재한다.

ex)
reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 })

위에서와 같이 실행 단위를 block으로 묶어서 사용된다. 단, 위의 경우는 전역함수나 중첩 함수와 다르게 anonymous 형태로 사용되었다. 위의 closure를 단순화 시켜 최종적으로 operator function의 형태로 만들것이다. 우선, Swift에서 제공하는 sort 함수는 두번 째 param의 (String, String) -> Bool 임을 예측한다. 그러므로 function type은 생략한다.

ex)
reversed = sort(names, { (s1, s2) in return s1 > s2 })

'in' 키워드 뒤에 statement가 1줄이기 때문에 return statement가 올 것이라는 것을 예측할 수 있기 때문에 'return' 키워드를 생략한다.

ex)
reversed = sort(names, { (s1, s2) in s1 > s2 })

swift는 위와 같은 inline closure에 default param name을 제공한다. 그러므로 param name을 $0, $1, $2 ... and so on으로 대체 가능하다. 그리고 default param name을 사용할 경우 param의 선언부도 생략한다.

ex)
reversed = sort(names, { $0 > $1 })

여기서 추가적으로 swift에서 제공하는 String에서는 '>' operator에 대해서 추가 구현이 되어 있으므로 아래와 같이 축약이 가능하다.

ex)
reversed = sort(names, >)

실제로 operator에 관한 내용은 더 뒤에서 설명이 나오지만 짐작하건데 C++에서의 operator overloading과 비슷할 것으로 생각된다.

func : map -> call closure once each value in array
swift의 array 객체는 map이라는 method를 가진다. map 메소드는 array의 각 value마다 사용자가 정의한 closure를 호출하는 역할을 한다. 사용법은 아래와 같다.

ex)
let strings = numbers.map {
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = digitNames[number % 10]! + output
        number /= 10
    }
    return output
}
// strings is inferred to be of type String[]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]


capturing values in closure
closure는 자신이 정의된 곳의 context의 constant와 value들을 capture할 수 있다. 심지어 context의 scope를 벗어났을 때도 capture가 가능하다. 일반적인 중첩 함수로 makeIncrementor(forIncrement:)를 예로 들어보자. 지역 변수 'runningTotal'은 makeIncrementor가 가지는 scope 안에서만 유효하다.

ex)
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0

    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }

    return incrementor
}

중첩 함수는 closure의 한 형태이다. 아래와 같이 makeIncrementor(forIncrement:)를 상수에 집어넣어 사용해보자. 비록 makeIncrementor(forIncrement:)가 실행되어 scope안에서만 'runningTotal'의 value가 유효하지만 closure이기 때문에 scope를 벗어나서도 'runningTotal'의 value는 유효하다. 이를 value를 capture했다고 한다.

ex)
let incrementByTen = makeIncrementor(forIncrement: 10)

incrementByTen()
// returns a value of 10

incrementByTen()
// returns a value of 20

incrementByTen()
// returns a value of 30

Enumeration
swift의 enum은 C와 obejctive-C와 달리 default로 integer값을 사용하지 않는다. integer 대신에 각 member를 구별할 수 있는 value를 사용한다.  아래의 syntax로 사용한다.

ex)
enum CompassPoint {
    case North
    case South
    case East
    case West
}

enum Planet {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

enum : Associated values
associated value는 연관되어 있는 값들을 서로 다른 타입(양식)으로표현할 때 사용한다. 사용자가 정의한 타입으로 enumeration을 할 수 있다. 아래와 같이 UPCA와 QRCode로 사용자가 직접 enumeration 안에서 타입을 정의할 수 있고 분기문에서 사용 가능하다. 사용된 enum의 associated value는 constant로 사용한다. 

ex)
enum Barcode {
    case UPCA(Int, Int, Int)
    case QRCode(String)
}

var productBarcode = .QRCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case let .UPCA(numberSystem, identifier, check):
    println("UPC-A with value of \(numberSystem), \(identifier), \(check).")

case let .QRCode(productCode):
    println("QR code with value of \(productCode).")
}
// prints "QR code with value of ABCDEFGHIJKLMNOP."

enum : Raw values
associated value는 서로 다른 타입의 값을 표할 때 사용된다. 하지만 raw value는 모두 같은 타입의 enum member들을 사용할 때 사용한다. raw value를 사용하려면 enum을 정의할 때, 구체적인 타입을 아래와 같이 정의해주면 된다.

ex)
enum ASCIIControlCharacter: Character {
    case Tab = "\t"
    case LineFeed = "\n"
    case CarriageReturn = "\r"
}

또한 기존의 C와 objective-C처럼 integer로 이루어진 enum 선언도 아래와 같이 할 수 있다. 'Mercury'를 '1'로 정의하고 뒤에는 자동으로 '2, 3...'으로 정의된다.

ex)
enum Planet: Int {
        case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}


Structures and Enum is value type
Classes and Closures is reference type

syntax)
struct SomeStructure {
    // structure definition goes here
}

class SomeClass {
    // class definition goes here
}

NSArray & Array
NSDictionary & Dictionary
NSArray & NSDictionary는 클래스로 구현되어 있다. 하지만 Array & Dictionary는 구조체로 이루어져 있어서 value type을 가진다. 여기서 특이점은 Dictionary는 value type의 특징 대로 무조건 copy된다. 하지만 Array의 경우는 length의 변화가 없을 경우, copy를 하더라도 공유된 sequence를 가지게 된다. length가 변할 때 실제로 물리적 copy를 수행하게 된다. 만약에 Array를 sequence를 공유하디 않고 애초에 물리적 copy를 하고 싶을 때는 아래와 같이 unshare() 혹은 copy()를 사용하면 된다.

ex)
var unsharedNames = names.unshare()
var copiedNames = names.copy()

unshare()는 호출 당시에는 물리적 copy를 수행하지 않지만 Array의 length 이외의 변화가 생길 때에도 즉시 물리적 copy를 수행한다. copy()는 호출과 동시에 물리적 copy를 수행하게 된다.


You can use (===, !==)
swift에서는 삼등연산자를 사용가능하다. 객체를 상대로 사용할 경우, 객체가 같은 클래스로 생성되었는지 아닌지를 구분할 수 있다. Array에 사용할 경우 Array 내부의 value들이 같은지 아닌지를 확인 할 수 있다.

Lazy property
lazy property는 실제로 property에 접근하여 read를 하기 전까지 불필요한 property의 초기화를 하지 않는다. 사용할 때는 아래와 같이 '@lazy'를 붙여주면 된다. 참고로 conatant property는 lazy property로 사용이 불가능하다.

ex)
class DataImporter {
    /*
    DataImporter is a class to import data from an external file.
    The class is assumed to take a non-trivial amount of time to initialize.
    */
    var fileName = "data.txt"
    // the DataImporter class would provide data importing functionality here
}
 
class DataManager {
    @lazy var importer = DataImporter()
    var data = String[]()
    // the DataManager class would provide data management functionality here
}
 
let manager = DataManager()
manager.data += "Some data"
manager.data += "Some more data"
// the DataImporter instance for the importer property has not yet been created

println(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// prints "data.txt"

위의 예시를 통해서 볼 경우, 제일 하단의 'println(manager.impoter.fileName)'이 호출되어 read가 일어나기 전까지 lazy property인 manager.importer의 초기화는 이루어지지 않는다.

Computed property
computed property는 실제 물리적인 값을 가지진 않고 오로지 getter와 setter를 가진 property를 말한다. 아래의 예시를 보면 이해가 바르다.

ex)
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
    get {
        let centerX = origin.x + (size.width / 2)
        let centerY = origin.y + (size.height / 2)
        return Point(x: centerX, y: centerY)
    }
    set(newCenter) {
        origin.x = newCenter.x - (size.width / 2)
        origin.y = newCenter.y - (size.height / 2)
    }
    }
}

위와 같이 property center는 실제로 물리적인 value를 가지고 있지 않지만 getter와 setter를 통한 접근이 가능하다. 이를 computed property라고 한다. setter를 정의하지 않을 경우, read only property로 사용되며 아래와 같이 getter라고 정의할 필요없이 간단하게 사용 가능하다.

ex)
struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
    return width * height * depth
    }
}

Property observer
objective-C의 KVO와 비슷한 기능을 가진 친구이다. 'willSet()'과 'didSet'을 통하여 property의 value에 set이 발생할 때마다 수행할 로직을 정의할 수 있다. 심지어 같은 값을 set해도 호출된다. overrinding한 property에도 사용할 수 있다. 사용방법은 아래와 같다.

ex)
class StepCounter {
    var totalSteps: Int = 0 {
    willSet(newTotalSteps) {
        println("About to set totalSteps to \(newTotalSteps)")
    }
    didSet {
        if totalSteps > oldValue  {
            println("Added \(totalSteps - oldValue) steps")
        }
    }
    }
}

Type property & method
type property와 type method는 Java에서 static property와 method를 말한다. 실제의 객체가 없어도 사용 가능한 property와 method이다. 사용 법은 아래와 같다. struct와 enum에서 사용될 때는 'static'키워드를 사용하고 class에서 사용될 때는 'class'라는 키워드를 사용해주면 된다.

ex)
struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
    // return an Int value here
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
    // return an Int value here
    }
}
class SomeClass {
    class var computedTypeProperty: Int {
    // return an Int value here
    }
}

Mutating
구조체와 enum은 value type을 가지기 때문에 instance method를 통한(type method 말고) 내부 value의 변경이 불가능하다. 하지만 method 앞에 'mutating'이라는 키워드를 사용할 경우, 내부 value의 변경이 가능한 mutating method를 정의할 수 있다. 또한 내부의 value뿐만 아니라 self를 사용하여 구조체와 enum의 자신의 개체 마저도 변경이 가능하다. 

ex)
‘struct Point {
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

enum TriStateSwitch {
        case Off, Low, High
        mutating func next() {
            switch self {
            case Off:
                self = Low
            case Low:
                self = High
            case High:
                self = Off
            }
    }
}

Subscripts
subscript는 class, struct, enum에서 정의 가능하다. subscript는 array나 dictionary와 같이 index나 key를 가지고 value를 querying하는 것을 가르킨다. subscript를 이용해서 class, struct, enum에서도 index를 통한 value의 querying이 가능하다. syntax는 computed property와 비슷하다. 2개의 예제 중 아래는 read-only subscript이다.

ex)
subscript(index: Int) -> Int {
    get {
        // return an appropriate subscript value here
    }
    set(newValue) {
        // perform a suitable setting action here
    }
}

subscript(index: Int) -> Int {
        // return an appropriate subscript value here
}

아래와 같이 실제 사용의 예를 보면 class, struct, enum도 array, dictionary와 같이 index를 통한 querying을 사용할 수 있다. 또한 index로 사용할 param을 variable param 혹은 variadic param으로도 사용 가능하나 in-out이나 defalut param으로는 사용 불가능하다.(기억이 안난다면 1주차 요약 참고) input param과 return type에는 제한이 없다.

ex)
struct TimesTable {
        let multiplier: Int
        subscript(index: Int) -> Int {
            return multiplier * index
        }
}

let threeTimesTable = TimesTable(multiplier: 3)
println("six times three is \(threeTimesTable[6])")
// prints "six times three is 18"


Preventing overrides
상속을 할 때, 상속을 허용하지 않을 property 혹은 method는 앞에 '@final'이라는 키워드를 사용하면 된다. Java를 사용해본 개발자라면 표현이 익숙할 것이다.

Initializer (init)
Initializer에서 모든 property를 초기화해야만 컴파일 가능하다. Initailizer내에서의 method 호출은 모든 property(Optional 제외, 엄밀히 말하면 optional은 nil로 초기화됨)를 초기화하고 나서 가능하다.

Designated & convenience initializer
Designated initializer는 모든 property 초기화하고 모든 클래스가 최소 1개 이상을 가진다. 반면, Convenience initializer는 보조 initializer이기 때문에 굳이 필요하지 않으면 만들지 않는 것이 좋다. 사용할 때는 'convenience' 키워드 사용한다. syntax는 아래와 같다.

ex)
Designated initializer
init(parameters) {
    statements
}

Convenience initializer
 convenience init(parameters) {
    statements
}

Initailizer chaining 
Initializer의 작은 규칙 몇 가지가 따른다. 구현에 앞서 이를 숙지하면 아~~주 좋다.
  1. Designated만 상위클래스의 Designated를 호출
  2. Convenience만 같은 클래스 내의 다른 생성자를 호출
  3. Convenience는 최종적으로 Designated를 호출하며 끝날 것


위의 이미지와 같이 designated는 오로지 슈퍼 클래스의 designated만을 호출할 수 있다. convenience만 다른 생성자들을 호출이 가능하다. 객체가 실제로 물리적 초기화가 되는 시점은 designated가 호출되는 시점이다. initial logic이 중첩되는 다양한 customized initializer를 사용할 경우엔 convenience를 이용하자. 굳이 필요하지 않으면 안 쓰는 것이 좋다.



참고 : Apple Inc. ‘The Swift Programming Language.’


Swift에 입문하기에 앞서 Swift의 전반적인 syntax에 먼저 익숙해지자.

/*************************************************************************/

Type alias
기존의 타입들을 대체하는 키워드를 정의할 수 있다. 팀내에서 정의된 타입을 사용할 때는 편리할 수 있으나 무분별한 type alias의 사용은 code의 readability를 저해할 것 같다.

ex)
typealias AudioSample = UInt16

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0’

Tuple
다수의 value들을 하나의 compound value로 사용할 수 있도록 해준다. 

ex)
let http404Error = (404, "Not Found")
let (statusCode, statusMessage) = http404Error

println("The status code is \(statusCode)")
// prints "The status code is 404"

println("The status message is \(statusMessage)")
// prints "The status message is Not Found"

Optionals (?)
value의 부재가 예상되는 곳에 optional을 사용한다. swift에서는 initializer에서 모든 property를 초기화 해야만 실행이 가능하다. 초기화가 완전히 이루어 지지 않으면 컴파일할 때 에러를 뱉는다. 하지만 초기화가 되지 않은 value가 있거나 method의 return이 정상적으로 이루어지지 않거나 부재일 것이 예상되는 부분에 사용하여 컴파일 혹은 런타임에서 에러를 피할 수 있다. 하지만 value가 부재이기 때문에 런타임에서 에러를 발생할 소지가 생기기에 사용을 할 때 신중해야 한다. value checking을 하여 런타임에서의 에러를 방지한다. 선언할 때는 property를 선언한 다음 끝에 '?'를 표시해준다.

ex)
let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
// convertedNumber is inferred to be of type "Int?", or "optional Int"

Forced unwrapping
optional value를 사용하기 위해서 '!'를 붙여준다. '!'는 'value가 존재하기 때문에 사용 가능하다'라는 것을 컴파일러에게 명시적으로 알려주는 방법이다. 부재중인 value의 접근 가능성이 있기 때문에 아래와 같이 'if'문을 사용하여 value checking을 해준다.

ex)
if convertedNumber {
                    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
                    println("\(possibleNumber) could not be converted to an integer")
}
// prints "123 has an integer value of 123"

Optional binding
optional binding은  'if'외에 사용할 수 있는 optional을 사용한 value의 부재에 의한 에러를 방지하기 위한 방법이다. 아래의 예제와 같이 optional value의 직접적인 엑세스를 하지 않고 임시변수를 만들 때 사용한다.

ex)
if let actualNumber = possibleNumber.toInt() {
    println("\(possibleNumber) has an integer value of \(actualNumber)")

} else {
    println("\(possibleNumber) could not be converted to an integer")
}
// prints "123 has an integer value of 123"

Implicitly unwrapped optionals (!)
소프트웨어의 구조 혹은 로직 상으로 value의 부재가 불가능할 경우, '이 property는 value를 항상 가지고 있다'라는 것을 표시해주는 방법이다. optional은 선언할 때 '?'를 사용하지만 Implicitly unwrapped optional은 선언하고 끝에 '!'를 사용해주면 된다.

ex)
let possibleString: String? = "An optional string."
println(possibleString!)
// requires an exclamation mark to access its value
// prints "An optional string."
 
let assumedString: String! = "An implicitly unwrapped optional string."
println(assumedString)
// no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."

Assertion
debugging에 도움이 될만한 도구이다. value의 부재나 부적절한 value의 체크에 유용하다. 잠재적으로 value에 의한 코드의 정상적 실행이 불가능할 경우 value를 확인하기 위해 사용한다. 아래는 Apple에서 권고하는 Assertion의 사용 시기이다.

    • Integer index에 의한 custom collection의 사용 시, index의 값이 너무 적거나 커서 접근에 이상이 예상 될때

    • 함수의 param이 함수의 정상적 실행이 불가능하게 하는 값일 경우
    • optional value가 nil이어서 부분적 실행이 정상적이지 못할 경우


ex)
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// this causes the assertion to trigger, because age is not >= 0"

Range operator
값의 범위를 축약적으로 나타낼 때, 사용한다. 어려운 개념이 아니기에 아래의 예문을 참고하여 쓰도록 한다.

ex)
Closed range
'for index in 1...5' mean '1 <= index <= 5'

Half-Closed range
'for index in 0..count' mean '0 <= index < count'

Func : countElements(String)
Swift에서 사용하는 String의 길이를 받아오는 함수, NSString의 length property는 unicode-16의 길이를 재는 것이기 때문에 countElements(String)의 값과 상이할 수 있다. NSString의 leng와 같은 값을 받아오기 위해서 String에서는 utf16count를 사용하면 된다.

Func : enumerate(ARRAY) -> (index, value)
enumerate 함수를 이용하여 index, value 형태의 tuple로 collection의 접근이 가능하다.

ex)
for (index, value) in enumerate(shoppingList) {
    println("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas

for-in Dictionary -> (key, value)
dictionary를 for-in으로 순회하게 되면 key, value 형태의 tuple로 접근이 가능하다.

ex)
for (airportCode, airportName) in airports {
    println("\(airportCode): \(airportName)")
}
// TYO: Tokyo
// LHR: London Heathrow

Switch-case
switch의 기본  syntax는 아래와 같다. swift에서는 statement가 없는 case문은 컴파일할 때 에러가 발생한다. 그러므로 아래와 같이 case문에 겹쳐서 statement를 사용하도록 한다.

ex)
switch 'some value to consider' {
case value 1:
    respond to value 1

case value 2, value 3:
    respond to value 2 or 3

default:
    otherwise, do something else
}

swift에선 조건에 부합하면서 처음 만나는 case문을 실행하면 자동으로 switch문을 벗어나기 때문에 명시적으로 'break'를 선언할 필요가 없다. 대신에 break없이 다음 케이스문까지 실행하고 싶으면 'fallthrough'라는 키워드를 사용하면 된다.

ex)
let integerToDescribe = 5

var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " a prime number, and also"
    fallthrough
default:
    description += " an integer."
}

println(description)
// prints "The number 5 is a prime number, and also an integer."

각 case문에는 tuple, range operator 등의 compound value를 사용 가능하다. 또한 추가적인 조건문의 사용이 필요하면 where를 활용할 수 있다.

ex)
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    println("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    println("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    println("(\(x), \(y)) is just some arbitrary point")
}
// prints "(1, -1) is on the line x == -y"


#이하 내용은 기존의 언어에서도 많이 사용하는 방법이기 때문에 빠르게 읽고 숙지하는 편이 정신건강에 좋다.

Mutiple return value using tuple
tuple을 사용하여 아래와 같이 compund value를 return할 수 있다.

ex)
func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {
    var vowels = 0, consonants = 0, others = 0
    for character in string {
        switch String(character).lowercaseString {
        case "a", "e", "i", "o", "u":
            ++vowels
        case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
        "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
            ++consonants
        default:
            ++others
        }
    }
    return (vowels, consonants, others)
}

External parameter name
함수를 호출할 때 아래와 같이 external name을 정의할 수 있다.

ex)
/*************************************************************************/
func someFunction(externalParameterName localParameterName: Int) {
    // function body goes here, and can use localParameterName
    // to refer to the argument value for that parameter
}
/*************************************************************************/

func join(string s1: String, toString s2: String, withJoiner joiner: String)
    -> String {
        return s1 + joiner + s2
}

join(string: "hello", toString: "world", withJoiner: ", ")
// returns "hello, world"

internal name과 external name을 동일하게 쓸 경우, 아래와 같이 약식으로 '#'을 사용하면 편리하게 사용할 수 있다.

ex)
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}

let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v"

Variadic parameter
개수를 예측할 수 없는 다수의 param을 사용할 경우엔 아래와 같은 방식으로 함수를 사용할 수 있다.

ex)
func arithmeticMean(numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8, 19)
// returns 10.0, which is the arithmetic mean of these three numbers


참고 : Apple Inc. ‘The Swift Programming Language.’


+ Recent posts