for-in
반복문을 배열, 숫자의 범위, 문자열과 같은 시퀀스를 순회하는 데 사용할 수 있다.
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
딕셔너리 또한 (key, value)
형태의 튜플로 순회할 수 있으며, 튜플의 요소를 블록 내에서 사용하기 위해 명시적인 이름의 상수로 만들어 줄 수 있다.
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
딕셔너리는 순서가 없기 때문에 순회를 할 때 순서대로 검색이 될 것이라는 보장을 하지 못한다. 삽입한 순서가 순회에서의 순서를 정의하지 않는다.
숫자의 범위를 지정해 반복문을 사용할 수 있다.
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
이 예제에서 index
는 순회가 시작할 때 자동으로 세팅이 되며, 사용하기 전 따로 선언을 해 줄 필요가 없다.
만약 시퀀스로부터의 값이 필요 없다면, 언더스코어(_)를 사용하여 무시할 수 있다.
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// Prints "3 to the power of 10 is 59049"
반 열린 범위 연산자를 사용하여 lower bound
는 포함하지만 upper bound
는 포함하지 않는 범위를 지정할 수 있다.
let minutes = 60
for tickMark in 0..<minutes {
// render the tick mark each minute (60 times)
}
반 열린 범위 연산자에 간격을 설정하고 싶을 경우 stride(from:to:by:)
메소드를 사용한다.
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}
닫힌 범위 연산자에도 stride(from:through:by:)
메소드를 사용하여 적용이 가능하다.
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
반복문은 조건이 거짓이 될 때까지 구문을 실행한다. Swift는 두 가지 형태의 while
문을 제공한다.
while
: 각 반복을 시작할 때 조건을 판단한다.repeat-while
: 각 반복이 끝날 때 조건을 판단한다.while
문은 하나의 조건 판단함으로써 시작한다. 만약 조건이 참이라면, 조건이 거짓이 될 때까지 조건이 반복된다.
while
문은 다음과 같이 작성한다.
while condition {
statements
}
1부터 25까지의 숫자와 몇 가지 함정으로 이루어진 보드 게임이 있다고 가정하자.
// 게임판 초기화
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
while square < finalSquare {
// roll the dice
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
if square < board.count {
// if we're still on the board, move up or down for a snake or a ladder
square += board[square]
}
}
print("Game over!")
repeat-while
문은 처음 한 번 구문을 실행한 뒤 조건을 검사한다. 이후 조건이 거짓이 될 때까지 반복문을 실행한다.
다른 언어의
do-while
문과 같은 역할을 한다.
repeat-while
문의 일반적인 형태는 다음과 같다.
repeat {
statements
} while condition
repeat {
// move up or down for a snake or ladder
square += board[square]
// roll the dice
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
} while square < finalSquare
print("Game over!")
조건문이 끝에서 평가되기 때문에, square
가 범위를 초과하는지 확인할 필요가 없다.
Swift는 두 가지 형태의 조건문을 제공한다.
if
: 몇 가지 가능한 결과에 대한 간단한 조건을 판단하는 데 사용한다. switch
: 보다 복잡한 조건에 적합하다. 또한 패턴 매칭이 실행할 코드 분기를 선택하는 데 도움이 될 수 있는 경우 유용하다.가장 간단하게, if
조건 하나만으로 표현할 수 있다. 조건이 참일 경우 실행된다.
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
}
// Prints "It's very cold. Consider wearing a scarf."
조건이 거짓일 때 실행되어야 하는 구문이 존재할 경우 else
키워드를 사용한다.
temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// Prints "It's not that cold. Wear a t-shirt."
다수의 조건이 있다면 else if
를 사용하여 분기한다.
temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// Prints "It's really warm. Don't forget to wear sunscreen."
switch
구문은 몇 가지 매칭이 가능한 패턴과 대조하여 값을 판단한다. 처음으로 패턴과 매칭이 되는 곳에서 코드가 실행된다.
다음과 같은 형태에서, 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
}
switch
문에서 모든 가능한 값들은 반드시 케이스 중 하나와 매칭이 되어야 한다. 만약 모든 값에 대해 케이스를 제공하지 못할 경우, 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")
}
// Prints "The last letter of the alphabet"
C 또는 Objective-C와 반대로, Swift의 switch
구문은 암시적인 진행을 하지 않는다. C나 Objective-C에서는 기본적으로 모든 케이스를 순회하여 default
를 만날 때까지 진행된다. 이를 위해 break
키워드를 명시해야 했다. Swift에서는 break
를 적지 않아도 특정 케이스가 완료되면 자동으로 switch
구문을 빠져 나오게 된다. 이런 사용법으로 인해 실수로 break
를 빠뜨려 의도하지 않은 케이스가 실행되는 것을 방지해 준다.
break
가 Swift에서 필수적이지는 않지만, 케이스 안의 특정 지점에서 멈추도록 하기 위해break
를 사용할 수 있다.
각각의 케이스 안에는 반드시 실행 가능한 구문이 있어야 한다. 다음의 코드는 첫 번째 케이스가 비어 있기 때문에 컴파일 에러를 발생시킨다.
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")
}
// This will report a compile-time error.
C와는 다르게 Swift는 'a'와 'A'에 모두 매칭되지 않으며, 컴파일 시간에 에러를 일으킨다.
콤마(,)를 사용해 동일한 구문을 실행하는 여러가지 케이스를 결합할 수 있다.
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// Prints "The letter A"
가독성 때문에 혼합해 사용하는 경우를 여러 코드라인에 나눠서 적을 수도 있다.
명시적으로 switch-case 문의 특정 지점의 끝까지 실행하고 싶다면
fallthrough
키워드를 사용할 수 있다.
숫자의 특정 범위를 조건으로 지정할 수 있다.
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).")
// Prints "There are dozens of moons orbiting Saturn."
동일한 구문을 실행하는 다양한 값을 테스트하기 위해 튜플을 사용할 수 있다. 튜플 내의 각각의 원소가 다른 값 또는 범위와 맞는지 검사된다. 언더스코어(_)를 사용할 수 있는데, 이는 가능한 모든 값에 매치되는 와일드카드 역할을 한다.
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"
switch
케이스는 케이스 안에서 사용할 임시적인 상수, 변수에 매치되는 값 또는 값들을 명명할 수 있다.
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"
위 예제의 첫 번째 케이스에서, x
에 매치되는 값(2)이 임시 상수인 x
에 할당된다. 이렇게 임시 상수가 선언된 후에 케이스의 코드 블록 안에서 사용할 수 있게 된다.
default
케이스가 존재하지 않는데, 마지막 케이스인 case let (x, y)
는 가능한 모든 값과 매치가 되기 때문이다.
추가적인 조건을 확인하기 위해 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"
위의 예시에서 세 가지 케이스는 yetAnotherPoint
로부터 두 튜플 값들을 임시적으로 가져올 placeholder 상수 x와 y를 선언한다. 이 상수들은 동적 필터로 생성되어 where
구문의 일부분으로써 사용된다.
동일한 실행부를 공유하는 여러 케이스들을 콤마로 구분하여 작성할 수 있다. 하나라도 패턴에 매치되면 그 케이스가 실행된다. 목록이 길 경우 여러 줄에 나눠서 작성할 수 있다.
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 a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
// Prints "e is a vowel"
혼합 케이스는 값 바인딩을 포함할 수 있다. 혼합 케이스의 모든 패턴은 반드시 값 바인딩의 셋이 동일해야 한다. 혼합 케이스의 모든 패턴으로부터 동일한 타입의 값을 얻을 수 있어야 한다. 케이스의 값 바인딩 중 하나만 일치해도 구문이 실행된다.
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)
는 제어권을 코드의 한 부분에서 다른 부분으로 넘김으로써 실행 중인 코드의 순서를 바꾼다. Swift는 다섯 가지 제어 전송 구문을 갖고 있다.
continue
break
fallthrough
return
throw
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
문의 실행을 멈추고 싶거나 반복문을 보다 일찍 끝내고 싶을 경우 사용한다.
반복문 안에서 break
구문을 사용했을 때, 반복문의 실행은 즉시 종료되고 반복문 블록 바깥으로 제어를 넘긴다. 현재 순회 이전, 이후 모두 실행되지 않는다.
switch
문 안에서 사용한다면 switch
문을 종료하고 제어를 블록 바깥으로 넘긴다.
하나 또는 그 이상의 케이스를 무시하고자 할 때 종종 사용된다. switch
문은 빈 케이스를 허용하지 않기 때문이다. 케이스 구문의 break
는 switch
문의 실행을 즉시 종료한다.
주석만 있는 케이스는 컴파일 에러를 발생시킨다. 케이스를 무시하고 싶다면
break
를 사용해야 한다.
let numberSymbol: Character = "三" // Chinese symbol for the number 3
var possibleIntegerValue: Int?
switch numberSymbol {
case "1", "١", "一", "๑":
possibleIntegerValue = 1
case "2", "٢", "二", "๒":
possibleIntegerValue = 2
case "3", "٣", "三", "๓":
possibleIntegerValue = 3
case "4", "٤", "四", "๔":
possibleIntegerValue = 4
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).")
}
// Prints "The integer value of 三 is 3."
Swift에서는 switch
문이 매치되는 케이스의 아래로 지나가지 않는다. 처음으로 매치되는 케이스가 완료되는 대로 switch
문이 끝나는 것이다. break
가 없다면 매치된 이후의 모든 케이스를 실행하는 C와는 상반된다. fallthrough
키워드는 이후의 case
에 대해서도 실행하게 만든다. 자동으로 break
가 사용되는 것을 막는 효과를 가져온다.
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."
fallthrough
키워드는 케이스 조건을 확인하지 않고 다음 케이스를 실행하게 한다.
Swift에서는 복잡한 제어문 구조를 만들기 위해 조건문이나 반복문 안에 다른 조건문, 반복문을 중첩할 수 있다. 이러한 제어문들은 그들의 실행을 조기에 끝내기 위해 break
문을 사용할 수 있다. 때문에 어떤 제어문을 끝내고 싶은지 명시하는 것이 유용할 때가 있다. continue
문을 사용할 때 역시 마찬가지다.
이를 위해 조건문 또는 반복문에 라벨을 붙일 수 있다. 라벨이 붙은 구문의 실행을 끝내기 위해 break
문 또는 continue
문을 라벨과 함께 사용한다.
라벨 구문은 콜론(:)과 함께 제어문 키워드와 같은 라인에 위치한다.
label name: while condition {
statements
}
위에서 보았던 게임 예시에서, while
문에 라벨을 설정하여 switch
문과 함께 사용할 수 있다.
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!")
guard
문을 이용해 특정 조건을 만족하지 않으면 이후의 코드를 실행하지 않도록 방어코드를 작성할 수 있다. if
문과는 달리 guard
문은 항상 else
구문을 갖고 있다. 만약 조건이 거짓이라면 코드 블록 내부의 else
구문이 실행된다.
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
문의 조건이 충족된다면, guard
문의 닫는 중괄호 이후로 코드 실행이 계속된다. 옵셔널 바인딩을 사용하여 할당받은 상수/변수는 나머지 코드 블록에서 사용 가능하다.
조건을 만족하지 않았다면, else
문 안의 코드가 실행된다. return
, break
, continue
, throw
와 같은 제어 전송 구문이나 리턴값이 없는 함수/메소드를 사용할 수 있다.
guard
문의 사용은 if
문과 비교했을 때 가독성이 높다.
Swift에서는 기본으로 특정 플랫폼 (iOS, macOS, tvOS, watchOS)과 특정 버전을 확인하는 구문을 제공해 준다. 이 구문을 활용해 특정 플랫폼과 버전을 사용하는 기기에 대한 처리를 따로 할 수 있다.
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
}