'IOS/Swift'에 해당되는 글 16건

  1. 2017.04.01 [Swift 4.0] Functions
  2. 2017.04.01 [Swift 4.0] Control Flow
  3. 2017.02.03 [Swift 4.0] Collection Types
  4. 2017.01.22 [Swift 4.0] Strings and Characters
  5. 2017.01.16 [Swift 4.0] Basic Operators
  6. 2017.01.13 [Swift 4.0] The Basics

[Swift 4.0] Functions

IOS/Swift 2017. 4. 1. 23:15

Defining and Calling Functions
: Swift 에서는 Function도 타입을 가질 수 있다. 타입을 구별하는 요소는 Function의 parameter type과 return type이다. 

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
        
    return greeting
}

print(greet(person: "Anna"))
// Prints "Hello, Anna!"
print(greet(person: "Brian"))
// Prints "Hello, Brian!"


Function Parameters and Return Values
: Swift Function의 parameter와 return value의 형태는 매우 다양하다. 이제 하나하나씩 알아볼 것이다.

Functions Without Parameters

return 값만 있고 parameter는 존재하지 않는 함수다.

func sayHellosWorld() -> String {
    reutrn "hello, world"
}

print(sayHelloWorld())

Functions With Multiple Parameters

여러개의 parameter와 return 값이 있는 function 이다.

func greet(person: String, alreadyGreeted: Bool) -> String {
    if alresyGreeted {
        return greetAgain(person: person)
    } else {
        return greet(person: person)
    }
}

print(greet(person: "Tim", alreadyGreeted: true))

이 함수는 우리가 위에서 정의한 greet(person:) 함수와 이름이 똑같다. 하지만 parameter의 타입이 다르기 때문에 서로 다른 타입으로 구별된다.

Functions Without Return Values

return Value를 갖지 않는 Function 이다.

func greet(person: String) {
    print("Hello, \(person)!")
}

greet(person: "Dave")

엄밀히 말하면, 위 함수는 값을 전혀 return하지 않는것이 아니고, Void type을 return한다. 이것은 empty tuple '()'로 표현될 수 있다.

Functions with Multiple Return Values
: return type으로 tuple을 지정할 수 있다.

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]

    for value in array[1 ..< array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }  
    
    return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])

print("min is \(bounds.min) and max is \(bounds.max)")
// Prints "min is -6 and max is 109"

Optional Tuple Return Types
: return type이 tuple인데 nil이 될 가능성이 있는경우 뒤에 '?'을 붙여서 optional type으로 만들어야 한다. 
단, (Int, Int)? 와 (Int?, Int?)는 구분해야 한다. 전자는 튜플 자체가 optional type이고 후자는 튜플안에 있는 각각의 타입이 optional type인 것이다.

func minMax(array: [Int]) -> (min: Int, max:Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }

    return (currentMin, currentMax)
}

if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}
// Prints "mins is -6 and max is 109"


Function Argument Labels and Parameter Names
: Function의 구성요소로 Argument Label과 Parameter Name이 있다.
Argument Label은 function을 call 할때 각각의 argument 앞에 붙여서 사용하고 parameter name은 함수 내부에서 넘어온 argument를 사용할 때 사용한다.
기본적으로 argument Label을 따로 지정해 주지 않으면 parameter name을 argument Label로 사용한다.

func someFunction(firstParameterName: Int, secondParameterName: Int)
{
    // 따로 argument Label을 지정해주지 않았으므로
    // firstParameterName, secondParameterName은 parameter name이 됨과 동시에 argument Label이 된다.
}

specifying Argument Labels
: Argument Label을 지정하는 경우를 살펴보자.

func someFunction(argumentLabel parameterName: Int) 
{
    // argument label이 지정된 경우이다. 함수 내부에서는 parameter name을 써야하고
    // 함수를 호출할 때에는 argument label을 사용해야 한다.
}

여러개의 parameter가 존재할 경우 argumentLabel을 일부 생략할 수도 있다.

func greet(person: String, from hometown: String) -> String
{
    return "Hello \(person)! Glad you could visit from \(hometown)."
}

print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill! Glad you could visit from Cupertino"

Omitting Argument Labels
: argument label을 사용하고 싶지 않다면 '_'을 추가해서 생략할 수 있다.

func someFunction(_ firstParameterName: Int, secondParameterName: Int)
{
    // body...
}

someFunction(1, secondParameterName: 2)

Default Parameter Values
: parameter에 Default value를 지정하는 방법을 알아보도록 하자.

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12)
{
    // body...
}

someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6)
someFunction(parameterWithoutDefault: 4) // 이 경우 두번째 parameter에 12가 자동 지정되어 함수가 호출된다.

Variadic Parameters
: 가변 갯수 파라미터를 뜻한다. 
가변 갯수 파라미터로 넘어오면 constant array로 취급된다.
함수는 최대 하나의 variadic parameter를 가질 수 있다.

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)
arithmeticMean(3, 8.25, 18.75)

In-Out Parameters
: 전산 전공자라면, 학부때 call-by-value, call-by-reference 개념을 배웠을 것이다.
swift는 함수의 parameter가 default로 상수(let)로 취급되고, 기본 타입(Int, Double . . . )이 구조체이기 때문에 call-by-value로 동작한다.
swift는 이러한 제약을 탈피하고자 In-Out Parameter라는 문법을 제공한다. 

func swapTwoInts(_ a: inout Int, _b: inout Int) {
    let temporaryA = a
    
    a = b
    b = temporaryA
}

in-out parameter로 상수, literal 값을 넘길 수 없다. 오직 변수(var)만 가능하다. 
또한, in-out parameter는 default 값을 가질 수 없고 variadic parameter는 in-out parameter가 될 수 없다. in-out parameter로 변수를 넘길 때 앞에 '&'을 붙여서 값이 변경될 것이라는 것을 표시한다. 

var someInt = 3
var anotherInt = 107

swapTwoInts(&someInt, &anotherInt)

print("someInt is now \(someInt), and anotherInt is now (anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"


Function Types
: 모든 function은 type을 가지고 있고, type은 parameter type과 return type에 의해 구별된다.

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

func multiplyTwoInts(_ a: Int, _ b: Int) -> Int
{
    return a * b
}

두 함수의 타입은 (Int, Int) -> Int로 서로 같다.

Using Function Types
: function type은 다른 type들과 다를 것이 없다. 예를들어, function type의 변수, 상수를 만드는 것이 가능하다. 

var mathFunction: (Int, Int) -> Int = addTwoInts

print("Result: \(mathFunction(2, 3))")
// Prints "Result : 5"

mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"
// multiplyTwoInts는 addTwoInts와 같은 타입이니 같은 변수에 대입할 수 있다.

let anotherMathFunction = addTwoInts
// anotherMathFunction은 (Int, Int) -> Int 으로 타입추론 된다.

Function Types as Parameter Types
: function type을 parameter type으로 사용하여 function도 parameter로 넘길 수 있다.

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

printMathResult(addTowInts, 3, 5)
// Prints "Result: 8"

Function Types as Return Types
: function을 함수의 return type으로 사용할 수 있다.

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

func stepBackward(_ input: Int) -> Int 
{
    return - 1
}

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

var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero 는 stepBackward()를 가리키게 된다.

print("Counting to zero:")

while currentValue != 0
{
    print("\(currentValue)...")
    currentValue = moveNearerToZero(currentValue)
}

print("zero!")
// 3...
// 2...
// 1...
// zero!


Nested Functions
: Nested Function은 function안에 존재하는 function이다.
위 chooseStepFunction(backward:) 예제를 Nested Function을 통해 구현할 수 있다.

func chooseStepFunction(backward: Bool) -> (Int) -> Int 
{
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    
    return backward ? stepBackward: stepForward
}

var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero는 stepForward()를 가리킨다.

while currentValue != 0 
{
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}

print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!


'IOS > Swift' 카테고리의 다른 글

[Swift 4.0] Enumerations  (0) 2017.04.01
[Swift 4.0] Closures  (0) 2017.04.01
[Swift 4.0] Control Flow  (0) 2017.04.01
[Swift 4.0] Collection Types  (0) 2017.02.03
[Swift 4.0] Strings and Characters  (0) 2017.01.22
Posted by 홍성곤
,

Introduction 
: Swift는 우리들에게 이미 익숙한 여러가지 control flow statement를 제공한다.
while, for, for-in, if, guard, switch, continue, break 등등..
특히, Swift의 switch는 다른 언어에 비해 상당히 powerful한 기능들을 가지고 있다. 이 부분은 뒤에서 차차 알아가도록 하자.


for-In Loops
: 'for-in'을 사용해서 array, dictionary, ranges of numbers 등을 iterating할 수 있다.

let names = ["Anna", "Alex", "Brian", "Jack"]

for name in names {
    print("Hello, \(name)!")

// array iterate

또한 dictionary를 key-value tuple로 iterate할 수 있다.

let numberOfLegs = ["spider : 8", "ant" : 6, "cat" : 4]

for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
// dictionary iterate

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// ranges of numbers iterate
// index값이 필요 없다면 '_'로 대체할 수 있다.

let minutes = 60

for tickMark in 0..<minutes {
    // render the tick mark each minute (60 times)
}
// half open range operator iterate

stride(from:to:by:)를 사용해서 iterate 간격을 설정할 수 있다.

let minuteInterval = 5

for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // 0, 5, 10, 15 ... 45, 50, 55
}

또한, stride(from:through:by)를 사용해서 'through' 값을 포함하면서 iterate할 수 있다.

let hours = 12
let hourInterval = 3

for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // render the tick mark every 3 hours (3, 6, 9, 12)
}


While Loops
: while, repeat-while이 있는데 while은 다른 언어의 while과 다를바 없고 repeat-while은 다른 언어의 do-while과 같다. 

repeat {
    "statements"
} while "condition"


If

var temperatureInFahrenheit = 90

if temperatureInFahrenheit <= 32 {
    print("It is very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
    print("It is really warm. Don't forget to wear sunscreen.")
} else {
    print("It is not that cold. Wear a t-shirt")
}


Switch

: 앞서 말했듯이, Swift의 Switch문은 다른 언어에 비해 powerful한 기능을 제공한다.
Swift의 Switch문은 모든 case를 포함해야 한다. 즉, 'case'가 비교할 값이 될수 있는 모든 경우를 나타내지 않으면 반드시 'default:'를 구현해야 한다. 반대로 'case'가 모든 경우를 나타내고 있으면 'default:'를 생략할 수 있다. 

let someCharacter: Character = "z"

switch someCharacter {
    case "a":
        print("The first letter of the alphabet")
    case "z"
        print("The last letter of the alphabet")
    default:
        print("Some other character")
}

No Implicit Fall through
: swift의 Switch문은  C, Objective-C와 달리 자동으로 fall through 하지 않는다. 
이는, 실수로 break문을 생략함으로서 일어나는 버그들을 예방해준다.
'break'를 쓰지 않아도 fall through 하지 않지만 각 case마다 'break'를 사용해도 된다. 

각 case마다 하나이상의 실행문을 구현해야 한다. 그렇지 않으면 compile error가 발생한다.

let anotherCharacter: Character = "a"

switch anotherCharacter {
    case "a": // Invalid, the case has an empty body
    case "A":
        print("The letter A")
    default:
        print("Not the letter A")
}
// C, Objective-C 스타일 코딩이다. compile error 발생.

let anotherCharacter: Character = "a"

switch anotherCharacter {
    case "a", "A":
        print("The letter A")
    default:
        print("Not the letter A")
}
// Swift 스타일 코딩.

Interval Matching
: 해당 값이 특정 간격안에 포함되어 있는지 체크할 수 있다.

let approximateCount =62
let countedThings = 'moons orbiting Saturn"
let naturalCount: String

switch approximateCount {
    case 0:
        naturalCount = "no"
    case 1..<5:
        naturalCount = "a few"
    case 5..<12:
        naturalCount = "several"
    case 12..<100:
        naturalCount = "dozens of"
    case 100..<1000:
        naturalCount = "hundreds of"
    default:
        naturalCount = "many"
}

print("There are \(naturalCount) \(countedThings).")

Tuples
: Switch문에서 Tuple을 비교하는 방법을 알아보겠다. 

let somePoint = (1, 1)

switch somePoint {
    case (0, 0):
        print("\(somePoint) is at the origin")
    case (_, 0):
        print("\(somePoint) is on the x-axis")
    case (0, _):
        print("\(somePoint) is on the y-axis")
    case (-2...2, -2...2):
        print("\(somePoint) is inside the box")
    default:
        print("\(somePoint) is outside of the box")
}
// Prints "(1, 1) is inside the box"
// '_'는 제약이 필요없는 경우일 때 사용한다.

Value Bindings
: 비교값이 특정 case에 matching될 때 case문 안에서 사용할 수 있도록 let 또는 var로 값을 binding할 수 있다.

let anotherPoint = (2, 0)

switch anotherPoint {
    case (let x, 0):
        print("on the x=axis with an x value of \(x)")
    case (0, let y):
        print("on the y-axis with a y value of \(y)")
    case let (x, y):
        print("somewhere else at (\(x), \(y))")
}
// Prints "on the x-axis with an x value of 2"
// binding 하기 위해서 '_'을 let 또는 var로 대체했다고 생각하면 된다.

Where
: Switch문 안에서 추가적이 condition을 체크하기 위해서 'where'문을 사용할 수 있다. 

let yetAnotherPoint = (1, 1)

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

Compound Cases 
: ','를 사용해서 하나의 case 여러 조건을 명시할 수 있다. 여러 조건중 하나에만 matching되면 해당 case를 만족하는 것으로 간주한다. 이를 'Compound Cases' 라고 한다.

let someCharacter: Character = "e"

switch someCharacter {
    case "a", "e", "i", "o", "u":
        print("\(someCharacter) is a vowel")
    case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
            "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
        print("\(someCharacter) is not a vowel or a consonant")
}
// Prints "e is a vowel"
// case문이 길 경우 여러줄로 명시해도 된다.

'Compound Cases'도 value binding을 사용할 수 있다. 

let stillAnotherPoint = (9, 0)

switch stillAnotherPoint {
    case (let distance, 0), (0, let distance):
        print("On an axis, \(distance) from the origin")
    default:
        print("Not on an axis")
}
// Prints "On an axis, 9 from the origin"


Control Transfer Statements
: code 실행의 흐름을 전환시킬 수 있는 여러가지 statement들을 제공한다.
: 'continue', 'break', 'fallthrough', 'return', 'throw'가 있다.
이 장에서는 'continue', 'break', 'fallthrough'만 설명하겠다. 'return'은 "Function"에서, 'throw'는 "Propagating Errors Using Throwing Functions"에서 설명하겠다.

continue
: 현재 iteration 수행을 멈추고 다음 iteration을 실행하라는 뜻이다.

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "e", "u", " "]

for character in puzzleInput.characters {
    if charactersToRemove.contains(character) {
        continue
    } else {
        puzzleOutput.append(character)
    }
}

print(puzzleOutput)
// Prints "grtmndsthnklk"

Break
: control flow statement의 실행을 멈춘다. if문에서 사용시 labeled statement를 사용해야 한다.
: 반복문 에서도 사용 가능하다.

Break in Loop Statement
: 현재 속해있는 loop의 실행을 멈추고 loop를 빠져나온다. closing brace '}'를 기준으로 현재 loop를 판단한다. 

Break in a Switch Statement
: Switch문 안에서 'break'를 만나면 현재 실행을 중단 시키고 Switch문을 빠져나온다.
: 앞서 말했다시피, Swift의 Switch문은 모든 case를 명시해줘야 한다. 그리고 각 case는 하나 이상의 실행문을 가지고 있어야 한다. 그러면 어떠한 동작도 원하지 않는 case는 어떻게 처리할까? 그것은 해당 case문 안에 'break'만 명시해주면 된다.

let numberSymbol: Character = "1"
var possibleIntegerValue: Int?

switch numberSymbol {
    case "1"
        possibleInteger = 1
    case "2"
        possibleInteger = 2
    case "3"
        possibleInteger = 3
    default:
        break
}

if let integerValue = possibleIntegerValue {
    print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
    print("An integer value could not be found for \(numberSymbol).")
}

Fallthrough
: C, Objective C의 switch문과 달리 Swift의 switch문은 자동으로 case를 fall through 하지 않는다. 만약 case를 fall through 하고 싶다면 case문 안에서 'fallthrough' 키워드를 사용해야 한다.
: 'fallthrough'를 사용하면 다음 case에 대해 조건을 체크하지 않고 무조건 실행 시킨다.(C와 마찬가지로)

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."
}

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

Labeled Statements
: 간단히 말해서, break 또는 continue 하려고 하는 조건문, 반복문에 이름을 지어줘서 원하는 조건문, 반복문의 실행을 break, continue 하겠다는 것이다. 

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

var square = 0
var diceRoll = 0

gameLoop: while square != finalSquare {
    diceRoll += 1

    if diceRoll == 7 { diceRoll = 1 }
   
    switch square + diceRoll {
        case finalSquare:
            // diceRoll will move us to the final square, so the game is over
            break gameLoop
        case let newSquare where newSquare > finalSquare:
            // diceRoll will move us beyond the final square, so roll again
            continue gameLoop
        default:
            // this is a valid move, so find out its effect
            square += diceRoll
            square += board[square]
    }
}

print("Game over!")


Early Exit
: 'guard' statement는 'else' statement와 같이 쓰인다.
'guard' 뒤에 나오는 조건이 true가 아니면 else문이 실행 되는데 else문 안에서는 guard문이 존재하는 code block을 빠져 나오는 동작을 실행해야만 한다. 그렇지 않으면 compile error 발생. 
이러한 동작은 'return', 'break', 'continue', 'throw' statement를 사용하거나 return 하지 않는 method, function을 호출해서 실행할 수 있다. 여기서 return 하지 않는 method, function란 'fatalError(_:file:line:)과 같은 것들이다.


Checking API Availability
: Swift는 API의 Availability를 체크할수 있는 'availability condition'을 제공한다. 

if #available(iOS 10, macOS 10.12, *) {
    // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
    // Fall back to earlier iOS and macOS APIs
}
// iOS10, macOS 10.12 API를 지원하는지 체크한다. 맨 뒤 파라미터 '*'는 필수로 들어가야 되고, 이는 앞에 명시된 플랫폼 버전 이외에 다른 플랫폼 버전의 제약은 없다는 뜻이다. 


'IOS > Swift' 카테고리의 다른 글

[Swift 4.0] Closures  (0) 2017.04.01
[Swift 4.0] Functions  (0) 2017.04.01
[Swift 4.0] Collection Types  (0) 2017.02.03
[Swift 4.0] Strings and Characters  (0) 2017.01.22
[Swift 4.0] Basic Operators  (0) 2017.01.16
Posted by 홍성곤
,

Introduction
: 스위프트의 주요 Collection type은 Array, Set, Dictionary가 있다. 
Array는 ordered collection, Set은 Unordered, unique value collection이고 Dictionary는 Unordered, key-value 기반 collection이다.


Mutability Of Collections
: Mutability를 결정하는 기준은 collection을 var, let 중 무엇으로 선언했느냐에 따른다.
var로 선언하면 mutable이 되고, collection이 포함하고 있는 item들을 adding, removing, changing할 수 있다. 
let으로 선언하면 immutable이 되고, item들의 조작이 불가능하다.

*변경이 불가능한 collection에 대해 모두 immutable로 선언하는 것이 예상치 못한 side effect나 performance 측면에서도 유리하다.


Arrays
: 같은 type의 value들을 저장한다. 똑같은 값의 중복 저장이 가능하다. 
: Objective-C의 NSArray와 bridged(호환)된다. 

Creating an Empty Array

var someInts = [Int]()

print ("someInts is of type [Int] with \(someInts.count) items.")
// Prints "someInts is of type [Int] with 0 items."
// someInts는 [Int] 타입이 된다. Int 타입의 값들을 저장할 수 있는 배열이다.

Array가 이미 타입이 지정되었으면 타입을 명시할 필요없이 바로 'empty array literal'로 빈 배열을 생성할 수 있다. 

someInts.append(3)
// someInts는 1개의 Int 타입의 value를 저장하고 있다.
someInts = []
// someInts는 empty array가 된다. 하지만 여전히 [Int] 타입이다. 

Creating an Array with a Default Value
: Array 초기화시 size와 default값을 지정할 수 있다.

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 타입은 [Double]이고, [0.0, 0.0, 0.0] 값을 갖는다.

Creating an Array by Adding Two Arrays Together
: 기존 두개의 Array를 합쳐서 하나의 Array를 생성할 수 있다. 단, 기존 두개의 Array의 타입이 compatible해야 한다.(compatible이란 의미가, 꼭 같은 타입만을 의미하는것은 아닌것 같다. 상속 관계에 있는 클래스로 시도를 해봤으나 실패했다. Double, Int 타입도 실패했다. 하지만 same이 아닌 compatible이란 단어를 쓴것으로 보아 같은 타입이 아님에도 '+' 연산이 되는 type 메커니즘이 있는게 아닐까?)

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles는 [Double]타입이다.
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles는 [Double]타입으로 추론되고, [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] 값을 갖는다.

Creating an Array with an Array Literal
: Array literal로 Array선언이 가능하다.

var shoppingList: [String] = ["Eggs", "Milk"]

shoppintList는 [String] 타입으로 선언되었기 때문에 array literal 안에는 String 타입의 value들이 들어가야 한다.
단, 타입을 명시하지 않을 수도 있다.

var shoppingList = ["Eggs", "Milk"]

타입을 명시하지 않았지만 array literal에 들어있는 값들이 모두 String 타입이기 때문에, [String] 타입으로 추론된다.

Accessing and Modifying an Array
: array의 값을 접근하거나 변경하기 위해서 array의 메서드나 프로퍼티 또는 subscript syntax를 이용한다.

print("The shopping list contains \(shoppingList.count) items")

if (shoppingList.isEmpty) {
    print("The shopping list is empty")
} else {
    print("The shopping list is not empty")
}

append(_:) 메서드를 이용해 item을 array 끝에 추가할 수 있다.

shoppingList.append("Flour")

+= 연산자를 이용해 한개 이상의 item들을 추가할 수 있다.

shoppingList += ["Banking powder"]
shoppingList +=["Chocolate spread", "Cheese", "Butter"]

subscript syntax를 이용해서 값을 검색할 수 있다.

var firstItem = shoppingList[0]

또한 subscript syntax를 이용해 기존 index에 값을 변경할 수 있다.

shoppingList[0] = "Six eggs"

subscript syntax에 범위를 지정해서 값을 변경할 수 도 있다. 심지어 변경하려는 값들의 범위와 subscript syntax로 지정한 범위가 맞지 않아도 된다.

//현재 shoppingList에는 ["Six Eggs", "Milk", "Flour", "Banking powder", "Chocolate Spread", "Cheese", "Butter"] 7개 item들이 들어있다.

shoppingList[4..6] = ["Bananas", "Apples"]
// 4, 5, 6번째 index item인 "Chocolate Spread", "Cheese", "Butter"이 "Bananas", "Apples"로 바뀌면서 shoppingList의 갯수도 7개에서 6개로 줄어들었다. 

shoppingList.insert("Maple Syrup", at: 0)
// 첫번째 index item이 "Maple Syrup"이 되고 나머지 6개 item들은 하나씩 뒤로 밀린다. 총 갯수 7개.

let mapleSyrup = shoppingList.remove(at: 0)
// "Maple Syrup"이 제거 되고 다시 갯수는 6개가 된다. 

let apples = shoppingList.removeLast()
// "Apples"가 제거 된다. array의 마지막 item을 제거할 때는 remove(at:) 보다 removeLast()를 사용해라. index가 array의 범위를 벗어나면 runtime error가 발생한다.

Iterating Over an Array

for item ins shoppingList  {
    print(item)
}
// Six eggs, Mils, Flour, Baking Powder, Bananas가 출력된다.

for (index, value) in shoppingList.enumerated() {
    print("Item \(index + 1): \(value)
")
}
// index가 필요한 경우 Tuple을 사용한 enumerate를 한다.


Sets
: Set은 
Unordered, unique value collection이다. Objective-C의 NSSet과 bridged 된다.

Hash Values For Set Types
: Set에 저장되기 위해선 hashable type이어야 한다. 즉, hash value를 계산할 수 있는 type이어야 하고, a == b 이면, a.hashValue == b.hashValue 여야 한다.
Swift의 모든 basic type( String, Int, Double, Bool )들은 hashable 하다. Enumeration case value들도(associated value들 없이) 기본적으로 hashable 하다.  

* 자신이 만든 custom type이 dictionary key나 set value type으로 저장되길 원한다면, Hashable 프로토콜을 
conform해야 한다. hashable 프로토콜을 구현한다는 것은 hashValue를 property로 제공해야 한다는 것이다. 다만, hashValue는 꼭 프로그램내 또는 다른 프로그램에서 항상 같은 값을 가지고 있을 필요 없다.

Hashable 프로토콜은 Equatable 프로토콜을 conform하고 있으므로 equals operator(==)를 구현해야만 한다. '==' 구현은 다음 세가지 조건을 만족해야 한다.
1) a == a
2) a == b 는 b == a를 의미한다.
3) a == b 이고 b == c 이면 a == c 이다.

Set Types Syntax
: Set<Element> 일때, 'Element'는 해당 Set이 저장할 수 있는 Type 이라는 뜻이다.

Creating and Initializing an Empty Set

var letters = Set<Character>()

print("letters is of type Set<Character> with (letters.count) items.")

letters.insert("a")
letters = []
// letters는 empty set이 되지만, Set<Character> type을 유지한다.

Creating a Set with an Array Literal

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
// 이처럼 type을 생략할 수도 있다.

Accessing and Modifying a Set

print("I have \(favoriteGenres.count) favorite music genres.")
// Prints "I have 3 favorite music genres."

if favoriteGenres.isEmpty {
    print("As far as music goes, I'm not picky.")
} else {
    print("I have particular music preferences.")
}

favoriteGenres.insert("Jazz")
// 4 items을 포함하게 된다.

if let removedGenre = favoriteGenres.remove("Rock") {
    print("\(removedGenre)? I'm over it.")
} else {
     print("I never much cared for that.")
}
// Rock이 있기 때문에 Rock이 제거되고 "Rock? I'm over it."이 출력된다. 3 items을 포함하게 된다.

if favoriteGenres.contains("Funk") {
    print("I get up on the good foot.")
} else {
    print("It's too funky in here.")
}
// 해당 item이 포함되었는지 check하기 위해 contains() 메서드를 사용한다.

Iterating Over a Set

for genre in favoriteGenres {
    print("\(genre)")
}
// Jazz, Hip hop, Classical을 출력.

Set은 unordering collection이기 때문에 ordered iterating을 하기 위해서는 sorted() 메서드를 사용해야 한다. sorted() 메서드는 '<' operator 기준으로 정렬시킨 array를 반환한다.

for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
// Classical, Hip hop, Jazz 순으로 출력된다.


Performing Set Operations
: set operation들을 통해서 교집합, 합집합, 여집합 등을 체크할 수 있다.

Fundamental Set Operations


let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]

oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]

Set Membership and Equality

- '==' operator는 두 set이 모두 같은 value들을 포함하고 있는지 체크한다.
- isSubset(of:), isSuperset(of:)를 통해 포함관계(equal도 포함)를 파악할 수 있다.
- isStrictSubset(of:), isStrictSuperset(of:)은 equal은 해당되지 않는 포함관계를 파악할 수 있다.
- isDisjoint(with:)를 통해 두 set이 서로소 인지 체크할 수 있다.


Dictionaries
: <key, value> 이면서 unordering collection이다. Objective-C의 NSDictionary 타입과 bridged 된다.

Dictionary Type Shorthand Syntax
: dictionary의 keyType은 hashable 프로토콜을 conform해야 한다. dictionary 타입은 '[Key:Value]'로 표현할 수 있다.

Creating an Empty Dictionary

var namesOfIntegers = [Int: String]()

namesOfIntegers[16] = "sixteen"
namesOfIntegers = [:]
// namesOfIntegers는 empty dictionary로 초기화 되었더라도 기존 type인 '[Int: String]'을 유지한다.

Creating a Dictionary with a Dictionary Literal

var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// 타입을 명시하지 않고 Dictionary 초기화가 가능하다.

Accessing and Modifying a Dictionary

print("The airports dictionary contains \(airports.count) items.")
// "The airports dictionary contains 2 items" 출력 

if airports.isEmpty {
    print("The airports dictionary is empty.")
} else {
    print("The airports dictionary is not empty.")
}

airports["LHR"] = "London"
// airports는 이제 3 item을 포함한다.

airports["LHR"] = "London Heathrow"
// "London" 값을 "London Heathrow"로 대체된다. 

위 subscript를 사용해서 value를 set하는 것을 updateValue(_:forKey:) 메서드로 대체할 수 있다. 다만, 
updateValue(_:forKey:) 메서드는 oldValue가 존재하면 return 한다.

if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("The old value for DUB was \(oldValue).")
}
// "
DUB" key에 "Dublin" 값이 저장되어 있기 때문에 oldValue에 "Dublin"이 return 된다.
// "The old value for DUB was Dublin" 출력.

if let airportName = airports["DUB"] {
    print("The name of the airport is \(airportName).")
} else {
    print("That airport is not in the airports dictionary.")
}
// "The name of the airport is Dublin Airport" 출력

airports["APL"] = "Apple International"
airports["APL"] = nil
// nil 값을 대입하면 해당 Key, Value는 Dictionary에서 제거 된다. 

위 nil을 대입해서 해당 값을 제거하는것을 removeValue(forKey:) 메서드로 대체할 수 있다. 이 메서드는 해당 값이 있으면 제거하고 제거된 값을 return한다. 해당값이 없으면 nil을 return 한다. 

if let removedValue = airports.removeValue(forKey: "DUB") {
    print("The removed airport's name is \(removedValue).
")
} else {
    print("The airports dictionary does not contain a value for DUB.")
}
// 
"DUB" key에 대한 "Dublin Airport"이 removedValue에 대입된다.
// "The removed airport's name is Dublin Airport." 출력

Iterating Over a Dictionary

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson, LHR: London Heathrow 출력.

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// key만 iterating이 가능하다.

for airportName in airports.values {
    print("Airport name: \(airportName)")
}
// value만 iterating이 가능하다.

Dictionary의 keys, values를 통해 array를 초기화 할 수 있다.

let airportCodes = [String](airports.keys)
// airportCodes는 ["YYZ", "LHR"]로 초기화 된다.

let airportNames = [String](airports.values)
// airportNames는 ["Toronto Pearson", "London Heathrow"]로 초기화 된다.

Dictionary는 unordering collection이다. ordering iterating을 하려면 keys 또는 values의 sorted() 메서드로 iterating을 해야한다. 

'IOS > Swift' 카테고리의 다른 글

[Swift 4.0] Functions  (0) 2017.04.01
[Swift 4.0] Control Flow  (0) 2017.04.01
[Swift 4.0] Strings and Characters  (0) 2017.01.22
[Swift 4.0] Basic Operators  (0) 2017.01.16
[Swift 4.0] The Basics  (0) 2017.01.13
Posted by 홍성곤
,

Introduction
: 스위프트의 String 타입은 Foundation 프레임워크의 NSString 클래스의 bridged 타입이다.
Foundation 프레임워크는 NSString 클래스의 public 메서드를 String 클래스에서도 그대로 사용할 수 있게 만들었다.(스위프트 3.0.1 부터 가능)


String Literals
: "" 로 둘러쌓여 있는 문자는 String Literal로 간주한다.

let someString = "Some string literal value"


Initializing an Empty String
: variable에 empty string을 만드는 방법은 두가지가 있다.

var emptyString = ""
var anotherEmptyString = String()

if (emptyString.isEmpty) {
    print("Nothing to see here")
}
// 위 두 변수 모두 empty string 으로 간주되며, 'isEmpty'를 통해 empty 체크된다.


String Mutability
: String의 mutability는 var, let 중 어느것으로 선언되었느냐에 따라 달라진다.

var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"

let constantString = "Highlander"
constantString += " and another highlander"
// 컴파일 에러 발생, let으로 선언된 String은 수정이 불가능 하다.


Strings Are Value Types
: String 타입은 구조체이다. 즉, reference 타입이 아니라 value 타입이다. 그래서 String이 다른 함수나 메서드에 넘겨지거나 다른 변수, 상수에 할당될 때 값이 복사된다. 
: 스위프트 컴파일러는 이러한 복사 동작을 최적화 해서 꼭 복사가 필요한 경우에만 복사한다.

let a = "abc"
var b = a 
// 아직 복사가 이루어 지지 않는다. a, b 모두 같은 String 구조체를 가리킨다.

b += "def"
// 이때 복사가 이루어 진다. 즉, 기존 문자 리터럴이 변경되지 않는것을 보장하지 못할 경우에만 복사가 이루어 진다.


Working with Characters
: String의 characters property를 통해 각 character를 iterating할 수 있다.

for character in "Dog!".characters {
    print(character)
}

character 타입의 상수, 변수를 선언할 수 있다.

let exclamationMark: Character = "!"
// let exclamationMark = "!" 처럼 자료형을 명시해주지 않으면 String으로 타입추론됨.

String은 Character의 Array로 만들어질 수 있다.

let catCharacters: [Character] = ["C", "a", "t", "!"]
let catString = String(catCharacters)
print(catString)


Concatenating Strings and Characters

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome now equals "hello there"

var instruction = "look over"
instruction += string2
// instruction now equals "look over there"

Character를 덧붙이는 것도 가능하다.

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
//welcome now equals "hello there!"


String Interpolation

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"


Unicode
: Unicode는 text의 인코딩, 표현, 처리에 관한 국제 표준이다. 스위프트의 String, Character는 fully Unicode-compliant 타입이다.

Unicode Scalars
: 스위프트의 String 타입은 Unicode scalar value들로 이루어 진다. Unicode scalar는 21 bit의 unique number 값을 갖는다. 

ex) U+0061은 "a"(LATIN SMALL LETTER A)를 나타낸다.

Special Characters in String Literals
: String literals은 special characters를 포함할 수 있다.

- The escaped special characters
\0 (null character), \\ (backslash), \t (horizontal tab), \n(line feed), \r (carriage return), 
\" (double quote), \' (single quote)
- An arbitrary Unicode scalar
\u{n} n은 16진수다. 

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein

let dollarSign = "\u{24}"
// $, Unicode scalar U+0024
let blackHeart = "\u{2665}" 
// ♥, Unicode scalar U+2665
let sparklingHeart  = "\u{1F496}"
// 💖, Unicode scalar U+1F496


Extended Grapheme Clusters
: Grapheme은 문자소를 뜻한다. 문자소란 의미를 나타내는 최소문자 단위이다.(ex. 'ㄴ', 'ㄹ', 'ㅏ') 
: Extended Grapheme Clusters란 하나이상의 문자소가 합쳐져서 만들어진 human-readable character정도로 이해하면 되겠다. Character 타입은 Extended Grapheme Clusters를 표현하는 타입이라고 생각하면 된다.

let precomposed: Character = "\u{D55C}" 
// '한'
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"
// 'ㅎ' 'ㅏ' 'ㄴ' 이 합쳐져서 '한'이 된다.
// (precomposed == decomposed) 비교하면 true가 return 된다.

Counting Characters

: String의 프로퍼티인 characters의 프로퍼티인 count로 Characters의 갯수를 얻을 수 있다.
: string concatenation, modification이 string의 count의 항상 영향을 미치치 않는다. extended grapheme clusters를 통해서 여러개의 Unicode Scalars가 더해져서 하나의 Character를 표현할 수 있기 때문이다. 아래 예제를 보자.

var word = "cafe"
print("the number of characters in \(word) is \(word.characters.count)")
// Prints "the number of characters in cafe is 4"

word += "\u{301}"
// '`', COMBINING ACUTE ACCENT, U+0301

print("the number of characters in \(word) is \(word.characters.count)")
// Prints "the number of characters in café" is 4"

*Extended grapheme Clusters는 하나 이사의 Unicode scalars로 이루어져 있다. 그러므로 스위프트의 Character 타입은 똑같은 문자를 표현하고 있다하더라도 저장 메모리 공간은 다를수 있다. 이것은 String의 갯수를 측정할 때, 단순의 메모리의 크기로 측정할 수 없다는것을 뜻한다. 즉, String의  count를 구하기 위해서는 String의 프로퍼티인 characters를 Extended grapheme Clusters 단위로 iterating 해서 구해야 한다. 반대로 Objective-C의 NSString은 UTF-16(16비트 기준)로 문자를 표현하기 때문에 메모리 크기로 NSString의 length를 계산할 수 있다. 
즉, 이러한 이유로 똑같은 문자열을 표현하더라도 스위프트의 String.characters.count와 NSString의 length 프로퍼티의 값이 다를 수 있다. 


Accessing and Modifying a String
: 스위프트에서는 method, 프로퍼티들 그리고 subscript를 통해서 String을 접근 및 변경할 수 있다.

String Indices
: String은 index type을 갖는데, 이는 String 안에 있는 각 Character의 Position과 연관된다.
위에도 언급했듯이 Character는 같은 문자라 하더라도 다른 메모리 공간을 사용할 수 있어서 String은 integer 값으로 indexing 하지 않는다.

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a

범위 밖에 있는 index를 접근하려고 하면 runtime error가 발생한다.

greeting[greeting.index] //Error
greeting.index(after: greeting.endIndex) //Error

characters의 indices 프로퍼티를 접근해서 String 안에 있는 모든 index에 접근할 수 있다.

for index in greeting.characters.indices {
    print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n T a g ! "

* 위에 소개된 startIndex, endIndex 프로퍼티와 index(before:), index(after:), index(_:offSetBy:) 메서드는 Collection 프로토콜을 따르는 타입은 모두 사용할 수 있다. 즉, String 이외에 Array, Dictionary 그리고 Set 에서도 사용 가능하다.

Inserting and Removing
: insert(_: at:), insert(contentsOf:at:) 메서드를 통해 문자나 문자열을 삽입할 수 있다.
: remove(at:), removeSubrange(_:) 메서드를 통해 문자나 문자열을 제거할 수 있다.

var welcome = "hello"

welcome.insert("!", at: welcome.endIndex)
// welcome = "hello!"

welcome.insert(contentsOf:" there".characters, at:welcome.index(before: welcome.endIndex))
// welcome = "hello there!"

welcome.remove(at: welcome.index(before: welcome.endIndex))
//welcome now equals "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6) ..< welcome.endIndex

welcome.removeSubrange(range)
// welcome now equals "hello"

* 위에 소개된 insert(_: at:), insert(contentsOf:at:), remove(at:), removeSubrange(_:) 메서드는 RangeReplaceableCollection 프로토콜을 따르는 타입은 모두 사용할 수 있다. 즉, String 이외에 Array, Dictionary, Set 에서도 사용 가능하다.


Comparing Strings
: 스위프트는 string, character의 비교연산을 하기위한 3가지 방법을 지원한다. 

String and Character Equality

let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."

if quotation == sameQuotation {
    print("These two strings are considered equal")
}

String은 linguistic meaning과 appearance이 같으면 둘은 같다고 인식한다. 즉, 다른 Unicode scalars로 이루어져있다 하더라도 의미와 나타내는 모양이 같으면, 둘을 같은 문자열로 취급하는 것이다. 

let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
// 두 상수 모두 "Voulez-vous un café"를 나타낸다.

if eAcuteQuestion == combinedEAcuteQuestion {
    print("These two strings are considered equal")
}
// 두 문자열을 구성하는 Unicode scalars가 다르더라도 의미와 모양이 같기 때문에 같은 문자열로 취급한다.

let latinCapitalLetterA: Character = "\u{41}"
let cyrillicCapitalLetterA: Character = "\u{0410}"
// 하나는 영어 'A'이고, 다른 하나는 러시아어의 'A'이다.

if laticCapitalLetterA != cyrillicCatitalLetterA {
    print("These two characters are not equivalent.")
}
// 둘은 모양은 같지만 linguistic meaning 즉, 의미가 다르기 때문에 다른 문자로 취급된다.

Prefix and Suffix Equality
: 두 문자열이 같은 prefix 또는 suffix를 가지고 있는지 확인하려면 hasPrefix(_:), hasSuffix(_:) 메서드를 사용해야 한다. 

let romeoAndJuliet = [
    "Act 1 Scene 1 : A street outside Capulet's mansion",
    "Act 1 Scene 2 : The Great Hall in Capulet's mansion",
    "Act 2 Scene 1: Outside Capulet's mansion",
    "Act 2 Scene 2: Outside Friar Lawrence's cell" 


var act1SceneCount = 0

for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        act1SceneCount += 1
    }
}
// act1SceneCount = 2

var mansionCount = 0
var cellCount = 0

for scene in romeoAndJuliet {
    if scene.hasSuffix("Capulet's mansion") {
        mansionCount += 1
    } else if scene.hasSuffix("Friar Lawrence' cell") {
        cellCount += 1
    }
}
// mansionCount = 3, cellCount = 1


Unicode Representations of Strings
: Unicode 문자열이 text file이나 다른 저장소에 쓰여질때 encoding 될수 있는 여러개의 encoding form이 있다. 각 encoding form은 문자열을 small chunk들, 즉 code unit들로 쪼개서 encoding한다. 
- UTF-8 : 8bit의 code units로 encoding (ASCII encoding과 representation이 동일하다.)
- UTF-16 : 16bit의 code units로 encoding
- UTF-32 : 32bit의 code units로 encoding (Unicode의 scalar가 21bit로 이루어져 있기 때문에 Unicode scalar 단위로 쪼개서 encoding 하려면 UTF-32를 이용해야 한다.)

let dogString = "Dog!!🐶"
// !!(Unicode scalar U+203C) , 🐶(Unicode scalar U+1F436)

위 dogString을 각각의 encoding form으로 다뤄보겠다.

UTF-8 Representation

String의 utf8 프로퍼티를 통해 각 code unit에 접근할 수 있다. utf8 프로퍼티는 String.UTF8View 타입이며, 이는 UInt8 값들의 collection이다.

for codeUnit in dogString.utf8 {
    print("\(codeUnit) ", terminator: "")
}
// 68 111 103 226 128 188 240 159 144 182

UTF-16 Representation

String의 utf16 프로퍼티를 통해 각 code unit에 접근할 수 있다. utf16 프로퍼티는 String.UTF16View 타입이며, 이는 UInt16 값들의 collection이다.

for codeUnit in dogString.utf16 {
    print("\(codeUnit) ", terminator: "")
}
// 68 111 103 8252 55357 56347

Unicode Scalar Representation

String의 unicodeScalars 프로퍼티를 통해 각 code unit에 접근할 수 있다. unicodeScalars 프로퍼티는 UnicodeScalarView 타입이며, 이는 UnicodeScalar의 collection이다.
각 UnicodeScalar는 21bit의 scalar 값을 가지고 있으며 value 프로퍼티로 접근할 수 있다. 이는 UInt32 타입이다.

for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
// 68 111 103 8252 128054

for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D o g !! 
🐶

'IOS > Swift' 카테고리의 다른 글

[Swift 4.0] Functions  (0) 2017.04.01
[Swift 4.0] Control Flow  (0) 2017.04.01
[Swift 4.0] Collection Types  (0) 2017.02.03
[Swift 4.0] Basic Operators  (0) 2017.01.16
[Swift 4.0] The Basics  (0) 2017.01.13
Posted by 홍성곤
,

Introduction
: 스위프트는 대부분의 standard C operators를 지원한다. 게다가 빈번히 일어나는 실수를 줄이기 위한 동작들도 추가되었다. 예를들어, '=' operator는 값을 return 하지 않는다.(우리는 종종 if문 안에서 '==' 대신 '='를 입력한다. C에서는 '='는 값을 return하기 때문에 compile time에 해당오류를 잡기 힘들고, 심지어는 runtime 에서조차 잡기 힘들다.)
또한 '+', '-', '*', '/', '%'는 overflow를 허용하지 않는다. 그리고 'a..<b', 'a...b' 같은 다양한 operator를 제공하면서 프로그래머에게 편의를 제공한다. 


Assignment Operator

let b = 10
let a = 5
a = b

let (x, y) = (1, 2)


Arithmetic Operators
: Addition (+), Substraction(-), Multiplication(*), Division(/)
: 여느 언어와 다르게 overflow를 허용하지 않는다. 
: Addition operator는 String concatenation을 지원한다. 

"hello, " + "world"
// "hello, world"


Remainder Operator
: C와 동일

9 % 4
// equals 1

-9 % 4
// equals -1


Unary Minus Opeartor
: C와 동일

let three = 3
let minusThree = -three
let plusThree = -minusThree


Unary Plus Operator
: C와 동일

let minusSix = -6
let alsoMinusSix = +minusSix
// equals -6


Compound Assignment Operators
: C와 동일

var a = 1
a += 2
// a = 3


Comparison Operators
: C와동일 ( '==', '!=', '>', '<', '>='. '<=' )
: '===', '!==' reference type 비교, 두 object가 같은 instance를 참조하고 있는지 체크한다.
: tuple 또한 비교가 가능하다. 단 tuple의 value들의 갯수가 같아야 하며, 각 value들이 비교가 가능한 type이어야 한다. 예를 들어 tuple의 value가 (Int, String)이면, 두 type 모두 비교가 가능한 type이기 때문에 비교 연산자를 사용할 수 있지만 value중 Bool이 있으면 비교가 불가능하다.
: 왼쪽에서부터 오른쪽으로 비교한다. 밑에 예제를 보고 어떻게 동작 하는지 알아보자.

(1, "zebra") < (2, "apple") 
// true because 1 is less than 2; "zebra" and "apple" are not compared

(3, "apple") < (3, "bird") 
// true because 3 is equal to 3, and "apple" is less than "bird"

(4, "dog")  == (4, "dog")
// true because 4 is equal to 4, and "dog" is equal to "dog"

(3, "zebra") > (4, "dog")
// false because 3 is less than 4, "zebra" and "dog" are not compared

* 스위프트에서 기본으로 제공하는 tuple에 대한 comparison operator는 tuple의 value들의 갯수를 6개까지로 한정한다. 7개 이상의 value들을 가지는 tuple을 비교하려면 스스로 operator를 만들어야 한다. 


Ternary Conditional Operator
: 삼항 연산자, C와 동일
: question ? answer1 : answer2

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight is equal to 90


Nil-Coalescing Operator
:(a ?? b), a는 optional type이다. a가 value를 가지고 있으면 a를 unwrap하고 a가 nil이면 b를 return한다.
: a와 b는 같은 type 이어야 한다.

let defaultColorName = "red"
var userDefinedColorName: String?
var colorNameToUse = userDefinedColorName ?? defaultColorName
// colorNameToUse = "red"


Range Operators
: 스위프트는 2개의 range Operators를 제공한다.

Closed Range Operator
: 'a...b' 는 a와 b를 포함하는 범위를 뜻한다. a는 b보다 크지 않아야 한다.

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}

Half-Open Range Operator
: 'a..<b' a는 포함하되, b는 포함하지 않는 범위를 뜻한다. a는 b보다 크지 않아야 한다.
이 operator는 array같은 zero-based 리스트를 다룰때 유용하다. 

let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
    print("Person \(i + 1) is called" \(names[i])")
}


Logical Operators
: 세개의 Logical Operators를 제공한다. C와 동일.
: Logical NOT (!a)
: Logical AND (a && b)
: Logical OR (a || b)


'IOS > Swift' 카테고리의 다른 글

[Swift 4.0] Functions  (0) 2017.04.01
[Swift 4.0] Control Flow  (0) 2017.04.01
[Swift 4.0] Collection Types  (0) 2017.02.03
[Swift 4.0] Strings and Characters  (0) 2017.01.22
[Swift 4.0] The Basics  (0) 2017.01.13
Posted by 홍성곤
,

[Swift 4.0] The Basics

IOS/Swift 2017. 1. 13. 19:08

Introduction
: 스위프트는 우리에게 이미 익숙한 Int, Double, Float, Bool, String, Array, Set, Dictionary등의 타입을 제공하고, 조금은 생소한 Tuple, optional type이라는 개념을 제공한다.
Tuple은 값들의 grouping 이라는 개념으로 이해하면 된다. 여러개의 값 뭉치를 하나의 타입으로 만들 수 있다는 의미이다.
optional Type은 원어로 "there is a value, and it equals x" or "there isn't a value at all"로 표현한다. 자세한 의미는 나중에 알아보기로 하고, 중요한 점은 스위프트의 powerful한 feature들의 구현 중심에는 optional type이 존재한다는 것이다.
스위프트는 type-safe 언어이다. 이것은 우리가 앱개발 도중에 가능한한 빨리 에러를 발견하고 고칠수 있게 만들어준다.


Constants and Variables
: 말 그대로 Constants는 상수, 한번 값을 set하면 변경할 수 없다. Variable은 값을 set 했더라도 변경할 수 있다.

Declaring Constants and Variables
: 변수는 var, 상수는 let 키워드를 사용해서 선언한다.

let maximumNumberOfLoginAttempts = 10;
var currentLoginAttempt = 0;

Type Annotations
: 변수, 상수의 타입을 명시한다.

var welcomeMessage: String
var red, green, blue: Double

Naming Constants and Variables
: 변수, 상수는 거의 모든 문자를 담을 수 있다.(Unicode 문자들 포함)

Printing Constants and Variables
: 변수, 상수를 Print 할 수 있다.
: print(_: separator: terminator:)
: separator, terminator 파라미터는 default값을 가지고 있다. print() 호출시 생략한다면 terminator는 빈 문자열이 들어가고 terminator에는 line break가 들어간다.

let friendlyWelcome = "Bonjour!"
print("The Current value of friendlyWelcome is \(friendlyWelcome)")


Integers
: 스위프트는 singed, unsigned interger를 8, 16, 32, 64 bit 형태로 제공한다.
: UInt8, UInt16,  UInt32, UInt64, Int8, Int16, Int32, Int64

Int 
: 대부분의 경우에는 Integer의 특정크기를 지정해줄 필요가 없다. 그래서 보통 Int 자료형을 사용한다.
: Int는 32bit 플랫폼에서는 32bit, 64bit 플랫폼에서는 64bit크기를 갖는다.

UInt
: UInt도 Int와 마찬가지로 32bit 플랫폼에서는 32bit, 64bit 플랫폼에서는 64bit크기를 갖는다.

Floating-Point Numbers
- Double : 64비트 실수형 타입
- Float : 32비트 실수형 타입


Type Safety and Type Inference
: 스위프트는 type safe 언어이기 때문에 type check를 컴파일 타입에 실행한다. 그러나 모든 상수, 변수에 타입을 명시할 필요는 없다. 타입을 명시하지 않아도 컴파일러가 타입을 추론하기 때문이다.

let meaningOfLife = 42;
// meaningOfLife상수에 타입을 명시하지 않아도 컴파일러는 Int형 타입으로 추론하여 컴파일 한다.

let pi = 3.14159
// Double형으로 추론
let anotherPi = 3 + 0.14159
// Double형으로 추론, 3에 대한 명시적인 타입선언이 없기때문에 두 변수의 합에 대한 타입은 Double이 더 적절하다.


Numeric Literals

let decimalInteger = 17
let binaryInteger = 0b10001
let octalInteger = 0o21
let hexadecimalInteger = 0x11
// 위 내게 상수 모두 17을 뜻한다.

실수는 십진수, 또는 16진수로 표현할 수 있다. 십진수 실수는 exponent 값을 포함할 수 있으며, 16진수는 exponent값을 포함해야 한다.

십진수 exponent
- 1.25e2 : 1.25 * 10^2 -> 125.0
- 1.25e-2 : 1.25 * 10^-2 -> 0.0125

16진수 exponent
- 0xFp2 : 15 * 2^2 -> 60
- 0xFp-2 : 15 * 2^-2 -> 3.75
- 0xFp0 : 15 * 2^0 -> 15

Numeric literal은 가독성을 높이기 위한 포맷팅이 존재한다.
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1


Numeric Type Conversion
:코드에서 정수를 다룰때 특별한 경우를 제외하고는 Int 타입을 쓰기 바란다. 그 이유는 CPU가 정수 계산을 할 때 피연산자 들을 모두 Int형으로 변환한 후 계산하기 때문이다. 그리고 우리가 명시적인 약속으로 Int형을 Default 정수형 타입이라고 정해놓으면 불필요한 type casting이 줄어들게 될것이다. 

Integer Conversion

let twoThousand: UInt16 = 2000
let one: UInt8  = 1
let twoThousandAndOne = twoThousand + UInt16(one)

SomeType(ofInitialValue)은 SwiftType으로 type casting하는 가장 일반적인 방법이다.

Integer and Floating-Point Conversion
: 정수와 실수간의 type casting은 명시적으로 이루어져야 된다.

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi = 3.14159

let integerPi = Int(pi)
let minusValue = -3.9
let minusInt = Int(minusValue)
// integerPi = 3
// minusInt = -3


Type Aliases

typealias AudioSample = Uint16
var maxAmplitudeFound = AudioSample.min


Booleans

C의 Bool과 달리 0이 아닌값으로 Booleans 값을 대체할 수 없다.

let i = 1

if (i)
{


// 컴파일 에러 발생.


Tuples
: Tuple은 여러개의 값을 grouping한 자료형이다.
: 두개의 자료형이 같을 필요없다.

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

print("The status code is \(statusCode)")
print(The status message is \(statusMessage)")
// Prints "The status code is 404"
// Prints "The status message is Not Found"

let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"

print("The status code is \(http404Error.0)")
//Prints "The status code is 404"
print("The status message is \(http404Error.1)")
//Prints "The status message is Not Found"

let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
print("The status message is \(http200Status.description)")
// Prints "The status code is 200"
// Prints "The status message is OK"


Optionals
: Optional은 상수, 변수의 상태라고 보면 된다. 두가지 상태 중 하나에 속해있는것인데, 하나는 값을 가지고 있고 그것은 unwrap할 수 있는 상태이고, 나머지 하나는 값이 없는상태이다.

: Optional은 C, C++에는 존재하지 않는 개념이다. 그나마 가까운 개념은 Objective-C의 nil이다. nil의 의미는 object의 invalid한 상태를 뜻한다. 그러나 nil은 object에만 적용되는 개념이다. Objective-C는 structure, basic C type, enum 타입 등이 invalid한 상태이면 NSNotFound 같은 special value를 return 한다. 이를 제대로 처리하기 위해서는 method 호출자가 NSNotFound와 같은 special value에 대해 대응이 가능한 상태여야 한다. 하지만 스위프트에서는 걱정할 필요 없다. Optional은 object뿐 아니라 다른 타입들 모두에게 적용되는 개념이기 때문이다.

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

Int(String) 생성자에 넘겨지는 문자열이 "abc"였다면 Int형 타입으로 올바르게 변환이 되지 않을 것이다. 그래서 Int(String)은 항상 optional Int(Int?)를 반환한다. 이것이 뜻하는 바는 올바른 Int값이 들어있을 수도 있고, valid한 값을 안가지고 있을수도 있다는 것이다.

nil
: optional type 변수에 값이 없는상태를 표현하기 위해서 nil을 대입할 수 있다.
: optional type 변수 선언시 initial value를 대입하지 않으면 자동으로 nil이 대입된다.

* Objective-C의 nil과 스위프트의 nil은 다르다. Objective-C의 nil은 객체의 포인터 타입으로 포인터가 아무것도 가리키는 것이 없는 상태를 나타내는 것이고, 스위프트의 nil은 포인터 타입이 아니고 모든 객체에 적용되는 개념으로, 해당 변수가 값을 가지고 있지 않은 상태를 나타내는 것이다.

If Statements and Forced Unwrapping
: 스위프트에서는 If문을 optional type 변수가 nil인지 valid값을 가지고 있는지 판별하는 용도로 사용할 수 있다.

if convertedNumber != nil {
    print("convertedNumber contains some integer value")
}

위 if문 안으로 들어갔다는 것은 optional type 변수 convertedNumber가 valid한 값을 가지고 있다는 것이므로 해당 변수뒤에 "!"을 붙여서 optional이라는 껍질을 벗겨냄으로서 정상적으로 해당 변수를 사용할 수 있게 만든다. 이를 unwrap이라고 한다.

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}

* 사실 "!"을 사용하여 unwrap하는 것은 사용하지 않는것이 좋다. optional type이 nil인 상태에서 "!"으로 unwrap을 시도하려고 하면 runtime error가 발생한다. "!"을 사용해서 unwrap 한다는 것은 optional type 변수가 valid한 값을 가지고 있을거라고 확신한다는 것인데, 프로그래밍에 있어서 100프로 확신하는 것은 굉장히 위험한 발상이다.

Optional Binding
: Optional Biding이란, "!"으로 optional type 변수를 강제 unwrap 하지 않고 안전하게 optional type변수를 unwrap 하기위한 기법이다. 
: if, while 구문을 사용하여 Optional Binding할 수 있다.

if let actualNumber = Int(possibleNumber) {
    print("\(possibleNumber) has an integer value of \(actualNumber)")
} else {
    print("\(possibleNumber) could not be converted to an integer")
}

possibleNumber가 올바르게 type casting이 된다면 Int(String)이 return 하는 Int?을 unwrapping하여 actualNumber 상수의 값을 대입한 후 if문에 진입하고, 그렇지 않으면 else문을 실행하게 된다.

단일 if문 안에 복수개의 optional binding과 Boolean condition 체크를 할 수 있다. 복수개의 optional binding들 중 어느 하나라도 nil을 가지고 있거나 Boolean condition이 어느 하나라도 false일 경우에는 if statement는 false가 된다. 

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// if문의 모든 조건을 만족하므로 if문에 진입.


Error Handling
: 다른 언어의 try, catch문을 통한 Error handling이랑 크게 다를바가 없다.
: error가 발생되면 현재 error가 발생한 scope 한단계 밖으로 error가 전파되고 handling 메서드를 찾지 못하면 계속 한단계 밖 scope로 전파된다. Top scope까지 전파되었는데 handling 메서드를 찾지 못하면 runtime error가 발생한다. 

func makeASandwich() throws {
    //....
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

makeASandwich() 실행도중 SandwichError.outOfCleanDishes 에러가 발생하면 washDishes()를 실행시키고 SandwichError.missingIngredients 에러가 발생하면 buyGroceries(let ingredients)를 실행시킨다.


Assertions and Preconditions
: 코드 실행 중 특정 조건이 만족 안되면 더 이상 코드 실행을 할 수 없을 때가 있다. 이는 8~90프로 버그 상황인데 이 버그상황을 디버그 환경에서 조금 더 효율적으로 체크할 수 있는 방법이 있다. 
첫번째는 Assertions이고 두번째는 Preconditions이다. 사용방법은 비슷하고 해당 조건을 만족하지 못했을 경우 앱이 종료된다. 다만, 차이점이 하나 있다. Assertions은 debug 빌드일 경우에만 동작하고, precondition은 debug, production 빌드 둘다 동작한다.

* Assertions, precondition 모두 optimization 레벨 None 으로compile되면 작동되지 않는다.(이 Option은 app target이 Release configuration이면 자동으로 YES 세팅된다.) 다만, fatalError(_:file:line:)의 경우는 optimization 레벨의 상관없이 항상 동작한다.


Debugging with Assertions

let age = -3
assert(age, "A person's age cannot be less than zero")

위 코드가 debug 환경에서 실행중이라면 assert문에서 assert 에러 메시지가 출력되고 앱을 종료될 것이다. 에러 메시지는 생략할 수 있다. 

if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age > 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

이 처럼 실패의 경우만 체크하기 위해  'assertionFailure'를 사용할 수 있다.

Enforcing Preconditions

// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")

위 코드는 subscript 안에서의 사용예를 보인것이다. index가 0보다 작거나 같으면 앱이 종료될 것이다.


'IOS > Swift' 카테고리의 다른 글

[Swift 4.0] Functions  (0) 2017.04.01
[Swift 4.0] Control Flow  (0) 2017.04.01
[Swift 4.0] Collection Types  (0) 2017.02.03
[Swift 4.0] Strings and Characters  (0) 2017.01.22
[Swift 4.0] Basic Operators  (0) 2017.01.16
Posted by 홍성곤
,