본문 바로가기

iOS/Swift

Swift 공식문서 5. Control Flow

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/controlflow

 

Documentation

 

docs.swift.org

 

Swift 공식 문서 보면서 내 맘대로 정리


 

Swift는 다양한 제어흐름 문장을 제공한다.

  • 반복을 위한 while
  • 조건 분기를 위한 if, guard, switch
  • 코드 흐름을 다른 지점으로 전환하는 break, continue
  • 배열, 딕셔너리, Range, 문자열 등 시퀀스 반복을 돕는 for-in
  • 현재 스코프를 벗어날 때 실행하는 defer

For-In Loops

  • Sequence를 반복하여 처리하고 싶을 때 for-in 루프를 사용
    • items in an array
    • ranges of numbers
    • characters in a string
    • key-value pairs in a dictionary

[key-value pairs in a dictionary]

  • for-in 루프에서 tuple 형태로 (key, value)를 제공
  • 딕셔너리 내에서 반환할 때 특정 순서를 보장하지 않는다.
  • 보장된 순서를 사용하고 싶다면 직접 구현 또는 swift-collections의 OrderedDictionary 사용을 고려
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]

for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
// cats have 4 legs
// ants have 6 legs
// spiders have 8 legs

 

[ranges of numbers]

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

 

[stride]

  • for - in 을 사용할 때 stride(from:to:by), stride(from: through: by:) 를 사용하면 반복을 몇 단계씩 건너뛰고 수행할 수 있다.
    • to는 마지막 값 미포함
    • through는 마지막 값 포함
    • Strideable 을 채택한 타입에 대하여 사용 가능
let minutes = 60
let minuteInterval = 5
for tickMark in stride(from:0, to: minutes, by: minuteInterval){
  // 0, 5, 10, ... , 55
}

let hours = 12
let hourInterval = 3
for tickMark in stride(from:3 : through : hours, by : hourInterval){
  // 3, 6, 9, 12
}

While Loops

  • 조건이 거짓이 될 때까지 반복적으로 명령을 실행하는 루프 구조
    • 반복 횟수가 미리 정해져 있지 않은 경우에 적합
  • 한 번은
while <#condition#> {
   <#statements#>
}
  • 조건 없이 명령을 수행하고 그다음부터 조건을 확인하는 repeat-while 문
repeat {
   <#statements#>
} while <#condition#>

If

[if문을 통해 상수에 값 할당하기]

  • 최소 한번 초기화되지 않는다면 사용 시점에 컴파일 에러가 발생한다
let temperatureInCelsius = 25
let weatherAdvice: String


if temperatureInCelsius <= 0 {
    weatherAdvice = "It's very cold. Consider wearing a scarf."
} else if temperatureInCelsius >= 30 {
    weatherAdvice = "It's really warm. Don't forget to wear sunscreen."
} else {
    weatherAdvice = "It's not that cold. Wear a T-shirt."
}


print(weatherAdvice)
// Prints "It's not that cold. Wear a T-shirt."

 

 

[더 간결하게 값 반환을 표현하기]

let temperatureInCelsius = 25

let weatherAdvice = if temperatureInCelsius <= 0 {
    "It's very cold. Consider wearing a scarf."
} else if temperatureInCelsius >= 30 {
    "It's really warm. Don't forget to wear sunscreen."
} else {
    "It's not that cold. Wear a T-shirt."
}

print(weatherAdvice)

 

  • 만약 반환 타입이 옵셔널이라면, Swift는 타입 추론하지 못한다.
  • 왜냐면, nil은 Swift의 모든 옵셔널 타입에서 사용할 수 있다.
  • 명시적으로 타입을 nil 값 또는 상수 선언부에 제공
let temperatureInCelsius = 25

let weatherAdvice: String? = if temperatureInCelsius <= 0 {
    "It's very cold. Consider wearing a scarf."
} else if temperatureInCelsius >= 30 {
    "It's really warm. Don't forget to wear sunscreen."
} else {
    nil
}

// 또는

let weatherAdvice = if temperatureInCelsius <= 0 {
    "It's very cold. Consider wearing a scarf."
} else if temperatureInCelsius >= 30 {
    "It's really warm. Don't forget to wear sunscreen."
} else {
    nil as String?
}

print(weatherAdvice)

 

Switch

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#>
}

 

[No Implicit Fallthrough]

  • Swift는 C언어와 Obj-C와 다르게 어떤 경우에 만족할 경우 아랫부분을 확인하지 않는다.
  • 또한 Switch 문에서 break 문을 요구하지 않는다.
let anotherCharacter = "a"
//잘못된 구문
switch anotherCharacter {
case "a":
case "A":
  print("The letter A")
default :
  print("Not the letter A")
}

//올바른 구문
switch anotherCharacter {
case "a","A" :
  print("The letter A")
default :
  print("Not the letter A")
}

 

[Interval Matching]

  • Switch는 어떠한 간격에 대한 조건을 줄 수 있다.
let approximateCount = 62
var naturalCount : String

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

 

[Tuples]

  • 여러 값들을 같은 switch 구문에서 사용하고 싶다면 tuple을 사용
  • 튜플의 각각의 원소는 다른 조건에 의해 테스트될 수 있음
  • (0, 0)은 아래 모든 케이스문에 매칭되는데, Swift에서는 가장 첫 번째 매칭 case 구문을 타고 나머지는 무시된다.
let point = (1,1)
switch point {
case (0,0):
  print("origin")
case (_,0):
  print("x-axis")
case (0,_):
  print("y-axis")
case (-2...2,-2...2):
  print("inside the box")
default :
  print("outside the box")
}

 

[Value Bindings]

  • switch의 case에서 일시적인 상수나 변수를 선언해서 사용할 수 있다.
let anotherPoint = (2,0)
switch anotherPoint {
case (let x,0):
  print("x-axis : (\(x),0)")
case (0, let y):
  print("y-axis : (0,\(y)")
case let (x,y):
  print("somewhere (\(x),\(y))")
}
  • 위 코드에서 let x, let y와 같이 일시적으로 변수를 선언하여 사용할 수 있다.
  • 위의 코드에서 let (x,y)는 default와 같은 역할을 수행한다.

[Where]

  • switch 구문에서 where 절을 활용하여 추가 조건을 확인할 수 있다
let yetAntoherPoint = (1,-1)

switch letAntoherPoint {
case let (x,y) where x == y:
  print("point is on the line y = x")
case let (x,y) where x == -y:
  print("point is on the line y = -x")
default:
  print("some arbitrary point")
}

 

Control Transfer Statements

Swift에는 코드 흐름을 제어하여 특정 코드로 이동이나 반복 종료 등 동작을 수행하는 제어 전달 구문이 5가지가 있다.

  • continue
  • break
  • fallthrough
  • return
  • throw

여기서는 continue, break, fallthrough를 살펴보고 return, throw는 각각 Functions, Error Handling 챕터에서 살펴본다.

 

Continue

  • 현재 반복을 중단하고 다음 반복을 시작
  • 루프 안에서 조건을 만족하는 경우, 루프를 빠져나가는 것이 아니라 다음 루프를 건너뛰고 실행되도록 할 때 유용
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]
for character in puzzleInput {
    if charactersToRemove.contains(character) {
        continue
    }
    puzzleOutput.append(character)
}
print(puzzleOutput)
// Prints "grtmndsthnklk"

Break

  • 실행 중인 제어 흐름문의 전체 실행을 즉시 종료
  • switch 문이나 반복문에서 사용

FallThrough

  • switch 문에서 한 케이스를 만족하더라도 다음 케이스의 구문도 수행하도록 하는 예약어
  • fallthrough는 case 조건을 확인하지 않고 그냥 다음 case를 실행하게 만든다.
let integerToDescribe = 5
var description = "The number \(integerToDescirbe) is"

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

Labeled statements

<#label name#>: while <#condition#> {
   <#statements#>
}
  • 다른 루프와 조건문 안에 또 다른 조건문을 중첩하여 복잡한 제어흐름 구조를 만들 수 있다.
    • 이렇게 만든 루프와 조건문은 break로 조기 종료시킬 수 있다.
    • 이 때 종료할 루프와 조건문을 명시적으로 나타내 주는 게 효과적
    • 마찬가지로 continue 문도 어떤 루프에 적용되는 것인지 명시하는 것이 유용
var age = 20
var name = "Ick"

checkage: while age < 30 {
  if name == "Ick" {
    break checkage
  } else {
    age += 1
  }
}

 

Early Exit

  • guard문을 사용하여 조건이 참이어야만 guard 문 뒤의 코드가 실행되도록 요구할 수 있다.
func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }


    print("Hello \(name)!")


    guard let location = person["location"] else {
        print("I hope the weather is nice near you.")
        return
    }


    print("I hope the weather is nice in \(location).")
}


greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."

 

[guard else vs if return]

 

개인적으로 해당 코드의 컨텍스트 및 가독성 여부에 따라 guard else와 if return 사용을 선택하고 있음

 

guard문 사용을 고려하는 경우

  • 현재 스코프에서 반드시 true이어야 다음 코드 라인의 실행이 유의미한 경우
  • 옵셔널 바인딩, 초기 값 추출로 이후 코드에서 해당 값이 항상 필요한 경우

if문 사용을 고려하는 경우

  • 현재 스코프에서 이후 라인의 진행을 특정 조건에 따라 멈춰야 하는 경우
  • 이중 부정으로 조건부의 가독성을 해친다고 판단하는 경우에는 if return문을 선택하기도 함

Deferred Actions

 

  • defer 블록을 사용하면 현재 스코프가 끝날 때 실행될 코드를 작성할 수 있다.
  • 함수가 조기 종료되거나, 반복문의 break, 에러가 발생해 throw가 호출되는 경우에도 항상 defer 구문은 수행된다.
  • 따라서 메모리 할당 및 해제, 파일 디스크립터 열기 및 닫기, 데이터베이스 트랜잭션 시작 및 종료처럼 쌍을 이루는 작업을 보장하는데 유용
  • 두 작업을 코드에서 서로 붙여서 작성할 수 있다는 장점
var score = 1
if score < 10 {
    defer {
        print(score)
    }
    score += 5
}
// Prints "6"
  • defer문을 여러개 쓰면 역순으로 수행된다.
if score < 10 {
    defer {
        print(score)
    }
    defer {
        print("The score is:")
    }
    score += 5
}
// Prints "The score is:"
// Prints "6"

 

Checking API Availability

  • Swift는 API Availability 체크 기능을 내장하고 있다.
  • SDK에 맞지 않는 API를 사용하지 않도록 방지
    • 컴파일러는 SDK의 가용성 정보를 바탕으로, 프로젝트에 설정된 배포 대상에서 사용하는 API가 모두 가용한 지 검증
    • 사용할 수 없는 API를 사용하려 하면 컴파일 에러 발생
  • if, guard를 사용하여 조건부 실행이 가능
  • 마지막 인수 * 는 필수이고 지정한 최소 배포 버전 이상에서 실행 가능
if #available(iOS 10, macOS 10.12, *){
		
} else{

}

 

  • unavailable도 함께 사용하면 더 가독성 좋은 코드가 될듯
if #available(iOS 10, *) {
} else {
    // Fallback code
}


if #unavailable(iOS 10) {
    // Fallback code
}

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

Swift 공식문서 6. Functions  (0) 2024.09.20
Swift - In-Out 파라미터  (0) 2024.09.20
Swift - Strideable Protocol  (0) 2024.09.19
Swift 공식문서 4. Collection Types  (0) 2024.09.08
Swift 공식 문서 3. Strings and Characters (2)  (1) 2024.08.31