Programming/Mac & iOS

[Swift] Swift Study 1주차 요약 (Alias, Tuple, Optional, Range operator ... etc)

MB Brad KWON 2014. 8. 16. 15:38
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.’