Control Flow Swift


제어 흐름

스위프트는 다양한 제어흐름 문법을 제공한다. 여러번 실행하기 위한 while, 주어진 조건에 따라서 코드의 분리를 실행할 if, guard, switch, 코드내에서 실행 흐름을 변경할 break, continue 와 같은 것들이다.

스위프트는 또한 for-in 루프를 제공해 배열, 딕셔너리, 범위, 문자열, 그리고 다른 시퀀스를 쉽게 이테레이트 할 수 있게 해준다.

스위프트의 switch 문은 C형식 언어에 대응되지만 훨씬 강력하다. case는 다양한 패턴, 범위 매치, 튜플, 그리고 특정 형식으로의 캐스트까지 지원한다. switch 에서의 매치된 값은 임시적 상수나 변수에 바운드할 수 있고, 케이스의 바디내에서 사용하고 복잡한 매칭 컨디션은 각 케이스에 대응해 where 로 표현될 수 있다.

for-in 루프

포 인 루프는 시퀀스에 대한 루프로서 배열, 숫자의 범위, 문자열의 캐릭터 등에 대응한다.

This example uses a for-in loop to iterate over the items in an array:

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
You can also iterate over a dictionary to access its key-value pairs. Each item in the dictionary is returned as a (key, value) tuple when the dictionary is iterated, and you can decompose the (key, value) tuple’s members as explicitly named constants for use within the body of the for-in loop. In the code example below, the dictionary’s keys are decomposed into a constant called animalName, and the dictionary’s values are decomposed into a constant called legCount.

let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
// ants have 6 legs
// cats have 4 legs
// spiders have 8 legs
The contents of a Dictionary are inherently unordered, and iterating over them does not guarantee the order in which they will be retrieved. In particular, the order you insert items into a Dictionary doesn’t define the order they are iterated. For more about arrays and dictionaries, see Collection Types.

You can also use for-in loops with numeric ranges. This example prints the first few entries in a five-times table:

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
The sequence being iterated over is a range of numbers from 1 to 5, inclusive, as indicated by the use of the closed range operator (...). The value of index is set to the first number in the range (1), and the statements inside the loop are executed. In this case, the loop contains only one statement, which prints an entry from the five-times table for the current value of index. After the statement is executed, the value of index is updated to contain the second value in the range (2), and the print(_:separator:terminator:) function is called again. This process continues until the end of the range is reached.

In the example above, index is a constant whose value is automatically set at the start of each iteration of the loop. As such, index does not have to be declared before it is used. It is implicitly declared simply by its inclusion in the loop declaration, without the need for a let declaration keyword.

If you don’t need each value from a sequence, you can ignore the values by using an underscore in place of a variable name.

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"
The example above calculates the value of one number to the power of another (in this case, 3 to the power of 10). It multiplies a starting value of 1 (that is, 3 to the power of 0) by 3, ten times, using a closed range that starts with 1 and ends with 10. For this calculation, the individual counter values each time through the loop are unnecessary—the code simply executes the loop the correct number of times. The underscore character (_) used in place of a loop variable causes the individual values to be ignored and does not provide access to the current value during each iteration of the loop.

In some situations, you might not want to use closed ranges, which include both endpoints. Consider drawing the tick marks for every minute on a watch face. You want to draw 60 tick marks, starting with the 0 minute. Use the half-open range operator (..<) to include the lower bound but not the upper bound. For more about ranges, see Range Operators.

let minutes = 60
for tickMark in 0..<minutes {
    // render the tick mark each minute (60 times)
}
Some users might want fewer tick marks in their UI. They could prefer one mark every 5 minutes instead. Use the stride(from:to:by:) function to skip the unwanted marks.

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)
}
Closed ranges are also available, by using stride(from:through:by:) instead:

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 루프

A while loop performs a set of statements until a condition becomes false. These kinds of loops are best used when the number of iterations is not known before the first iteration begins. Swift provides two kinds of while loops:
- while 은 루프를 통한 흐름의 시작접에서 조건을 실행한다.
- repeat-while 은 루프 흐름의 끝 점에서 조건을 실행한다.

while

A while loop starts by evaluating a single condition. If the condition is true, a set of statements is repeated until the condition becomes false.

Here’s the general form of a while loop:

while condition {
    statements
}
This example plays a simple game of Snakes and Ladders (also known as Chutes and Ladders):

../_images/snakesAndLadders_2x.png

The rules of the game are as follows:

The board has 25 squares, and the aim is to land on or beyond square 25.
The player’s starting square is “square zero”, which is just off the bottom-left corner of the board.
Each turn, you roll a six-sided dice and move by that number of squares, following the horizontal path indicated by the dotted arrow above.
If your turn ends at the bottom of a ladder, you move up that ladder.
If your turn ends at the head of a snake, you move down that snake.
The game board is represented by an array of Int values. Its size is based on a constant called finalSquare, which is used to initialize the array and also to check for a win condition later in the example. Because the players start off the board, on “square zero”, the board is initialized with 26 zero Int values, not 25.

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
Some squares are then set to have more specific values for the snakes and ladders. Squares with a ladder base have a positive number to move you up the board, whereas squares with a snake head have a negative number to move you back down the board.

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
Square 3 contains the bottom of a ladder that moves you up to square 11. To represent this, board[03] is equal to +08, which is equivalent to an integer value of 8 (the difference between 3 and 11). To align the values and statements, the unary plus operator (+i) is explicitly used with the unary minus operator (-i) and numbers lower than 10 are padded with zeros. (Neither stylistic technique is strictly necessary, but they lead to neater code.)

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!")
The example above uses a very simple approach to dice rolling. Instead of generating a random number, it starts with a diceRoll value of 0. Each time through the while loop, diceRoll is incremented by one and is then checked to see whether it has become too large. Whenever this return value equals 7, the dice roll has become too large and is reset to a value of 1. The result is a sequence of diceRoll values that is always 1, 2, 3, 4, 5, 6, 1, 2 and so on.

After rolling the dice, the player moves forward by diceRoll squares. It’s possible that the dice roll may have moved the player beyond square 25, in which case the game is over. To cope with this scenario, the code checks that square is less than the board array’s count property. If square is valid, the value stored in board[square] is added to the current square value to move the player up or down any ladders or snakes.

NOTE

If this check is not performed, board[square] might try to access a value outside the bounds of the board array, which would trigger a runtime error.

The current while loop execution then ends, and the loop’s condition is checked to see if the loop should be executed again. If the player has moved on or beyond square number 25, the loop’s condition evaluates to false and the game ends.

A while loop is appropriate in this case, because the length of the game is not clear at the start of the while loop. Instead, the loop is executed until a particular condition is satisfied.

repeat-while

The other variation of the while loop, known as the repeat-while loop, performs a single pass through the loop block first, before considering the loop’s condition. It then continues to repeat the loop until the condition is false.

NOTE

The repeat-while loop in Swift is analogous to a do-while loop in other languages.

Here’s the general form of a repeat-while loop:

repeat {
    statements
} while condition
Here’s the Snakes and Ladders example again, written as a repeat-while loop rather than a while loop. The values of finalSquare, board, square, and diceRoll are initialized in exactly the same way as with a while loop.

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
In this version of the game, the first action in the loop is to check for a ladder or a snake. No ladder on the board takes the player straight to square 25, and so it isn’t possible to win the game by moving up a ladder. Therefore, it’s safe to check for a snake or a ladder as the first action in the loop.

At the start of the game, the player is on “square zero”. board[0] always equals 0 and has no effect.

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!")
After the code checks for snakes and ladders, the dice is rolled and the player is moved forward by diceRoll squares. The current loop execution then ends.

The loop’s condition (while square < finalSquare) is the same as before, but this time it’s not evaluated until the end of the first run through the loop. The structure of the repeat-while loop is better suited to this game than the while loop in the previous example. In the repeat-while loop above, square += board[square] is always executed immediately after the loop’s while condition confirms that square is still on the board. This behavior removes the need for the array bounds check seen in the while loop version of the game described earlier.

조건문

It is often useful to execute different pieces of code based on certain conditions. You might want to run an extra piece of code when an error occurs, or to display a message when a value becomes too high or too low. To do this, you make parts of your code conditional.

Swift provides two ways to add conditional branches to your code: the if statement and the switch statement. Typically, you use the if statement to evaluate simple conditions with only a few possible outcomes. The switch statement is better suited to more complex conditions with multiple possible permutations and is useful in situations where pattern matching can help select an appropriate code branch to execute.

if

In its simplest form, the if statement has a single if condition. It executes a set of statements only if that condition is true.

var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
    print("It's very cold. Consider wearing a scarf.")
}
// Prints "It's very cold. Consider wearing a scarf."
The example above checks whether the temperature is less than or equal to 32 degrees Fahrenheit (the freezing point of water). If it is, a message is printed. Otherwise, no message is printed, and code execution continues after the if statement’s closing brace.

The if statement can provide an alternative set of statements, known as an else clause, for situations when the if condition is false. These statements are indicated by the else keyword.

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."
One of these two branches is always executed. Because the temperature has increased to 40 degrees Fahrenheit, it is no longer cold enough to advise wearing a scarf and so the else branch is triggered instead.

You can chain multiple if statements together to consider additional clauses.

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."
Here, an additional if statement was added to respond to particularly warm temperatures. The final else clause remains, and it prints a response for any temperatures that are neither too warm nor too cold.

The final else clause is optional, however, and can be excluded if the set of conditions does not need to be complete.

temperatureInFahrenheit = 72
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.")
}
Because the temperature is neither too cold nor too warm to trigger the if or else if conditions, no message is printed.

switch

스위치 문은 값을 특정한 매칭 패턴에 비교한다. 적절한 코드블럭을 처음으로 일치하는 패턴을 발견한 곳에서 수행한다. 스위치는 다중의 가능한 상태에 반응하기 위한 if 문의 대체 문이라 할 수 있다.

가장 단순한 형태에서 switch 문은 같은 형식의 1개 이상의 값과 비교하는 것이다.

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
}

모든 스위치 문은 다중 케이스로 구성되는 것으로 각각은 case 키워드로 시작한다. 특정값과 비교하는 것에 더해 스위프트는 더 복잡한 매칭 패턴에 대응할 수 있는 방법을 제공한다. 이들 옵션은 이 챕터 아래에서 보여진다.

if 문의 바디와 같이 각 케이스는 코드 실행의 분리된 브랜치이다. 스위치 문은 어떤 브랜치가 선택되어야 하는지 가늠한다. This procedure is known as switching on the value that is being considered.

Every switch statement must be exhaustive. That is, every possible value of the type being considered must be matched by one of the switch cases. If it’s not appropriate to provide a case for every possible value, you can define a default case to cover any values that are not addressed explicitly. This default case is indicated by the default keyword, and must always appear last.

This example uses a switch statement to consider a single lowercase character called someCharacter:

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"
The switch statement’s first case matches the first letter of the English alphabet, a, and its second case matches the last letter, z. Because the switch must have a case for every possible character, not just every alphabetic character, this switch statement uses a default case to match all characters other than a and z. This provision ensures that the switch statement is exhaustive.

No Implicit Fallthrough

In contrast with switch statements in C and Objective-C, switch statements in Swift do not fall through the bottom of each case and into the next one by default. Instead, the entire switch statement finishes its execution as soon as the first matching switch case is completed, without requiring an explicit break statement. This makes the switch statement safer and easier to use than the one in C and avoids executing more than one switch case by mistake.

NOTE

Although break is not required in Swift, you can use a break statement to match and ignore a particular case or to break out of a matched case before that case has completed its execution. For details, see Break in a Switch Statement.

The body of each case must contain at least one executable statement. It is not valid to write the following code, because the first case is empty:

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.
Unlike a switch statement in C, this switch statement does not match both "a" and "A". Rather, it reports a compile-time error that case "a": does not contain any executable statements. This approach avoids accidental fallthrough from one case to another and makes for safer code that is clearer in its intent.

To make a switch with a single case that matches both "a" and "A", combine the two values into a compound case, separating the values with commas.

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// Prints "The letter A"

가독성을 위해, 복합 케이스는 다중 라인으로 작성될 수 있다. For more information about compound cases, see Compound Cases.
일러두기)
명시적으로 특정 스위치 케이스의 끝까지 폴 스루 하려면 fallthrough 키워드를 사용한다.  as described in Fallthrough.

인터벌 매칭

Values in switch cases can be checked for their inclusion in an interval. This example uses number intervals to provide a natural-language count for numbers of any size:

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."
In the above example, approximateCount is evaluated in a switch statement. Each case compares that value to a number or interval. Because the value of approximateCount falls between 12 and 100, naturalCount is assigned the value "dozens of", and execution is transferred out of the switch statement.

튜플

튜플을 같은 스위치 문에서 다중 값을 테스트하는데 사용할 수 있다. 튜플의 각 요소는 값의 인터벌이나 다른 값에 대응해 테스트 될 수 있다. 대체적으로, _ 를 사용해 와일드 카드 패턴으로 알려진 모든 가능한 값에 대응할 수도 있다.

The example below takes an (x, y) point, expressed as a simple tuple of type (Int, Int), and categorizes it on the graph that follows the example.

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"

../_images/coordinateGraphSimple_2x.png

The switch statement determines whether the point is at the origin (0, 0), on the red x-axis, on the orange y-axis, inside the blue 4-by-4 box centered on the origin, or outside of the box.

Unlike C, Swift allows multiple switch cases to consider the same value or values. In fact, the point (0, 0) could match all four of the cases in this example. However, if multiple matches are possible, the first matching case is always used. The point (0, 0) would match case (0, 0) first, and so all other matching cases would be ignored.

값 바인딩

스위치 케이스는 값 또는 값을 임시 상수나 변수에 매칭하여 케이스의 본문에서 사용되게 할 수 있다. 이 작용은 밸류 바인딩이라하는데 값이 임시 변수나 상수에 케이스 바디내에서 바인드되기 때문이다.

아래의 예시는 (x,y) 포인트를 받아서 (Int, Int) 형식의 튜플로 표현되고 이 것을 그래프에 카테고라이즈 하고 있다.

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"

../_images/coordinateGraphMedium_2x.png
스위치 문은 점이 붉은 엑스 축에 있는지 오랜지 와이 축에 있는지 아니면 다른 곳에 있는지를 확인한다.

세 스위치 케이스는 상수 x와 y의 플레이스 홀더를 정의하고 다른 anotherPoint 로부터 하나 또는 그 이상의 값을 임시로 얻는다. 첫 번째 케이스에서 case (let x, 0) 은 0값이 0인 모든 점에서 임시 상수 x 에 값을 넣는다는 의미이다. Similarly, the second case, case (0, let y), matches any point with an x value of 0 and assigns the point’s y value to the temporary constant y. (즉 와일드 카드인데 값을 임시 상수/변수에 할당)

After the temporary constants are declared, they can be used within the case’s code block. Here, they are used to print the categorization of the point.

스위치 문은 디폴트 케이스가 없다, 마지막 케이스인 case let(x,y) 는 두 플레이스 홀더 상수에 어떤 값이든 매치된다. 그래서 anotherPoint 는 항상 튜플의 두 값이된다. 이 케이스는 모든 남은 값에 대응된다. switch를 명확하게 만드는데 default 케이스는 필요치 않다.

where

스위치 케이스는 where 를 사용해 추가적인 조건을 확인할 수 있다.

다음 예시는 그래프상의 (x, y) 점을 카테고라이즈 한다.

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"

../_images/coordinateGraphComplex_2x.png

The switch statement determines whether the point is on the green diagonal line where x == y, on the purple diagonal line where x == -y, or neither.

The three switch cases declare placeholder constants x and y, which temporarily take on the two tuple values from yetAnotherPoint. These constants are used as part of a where clause, to create a dynamic filter. The switch case matches the current value of point only if the where clause’s condition evaluates to true for that value.

As in the previous example, the final case matches all possible remaining values, and so a default case is not needed to make the switch statement exhaustive.

복합 케이스

Multiple switch cases that share the same body can be combined by writing several patterns after case, with a comma between each of the patterns. If any of the patterns match, then the case is considered to match. The patterns can be written over multiple lines if the list is long. For example:

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"
The switch statement’s first case matches all five lowercase vowels in the English language. Similarly, its second case matches all lowercase English consonants. Finally, the default case matches any other character.

복합 케이스는 밸류 바인딩도 포함할 수 있다. 복합 케이스의 모든 패턴은 밸류 바인딩의 같은 집합을 포함해야 하고 각 바인딩은 복합 케이스의 모든 패턴으로부터 같은 형식에 대한 값을 얻을 수 있어야 한다. This ensures that, no matter which part of the compound case matched, the code in the body of the case can always access a value for the bindings and that the value always has the same type.

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"
The case above has two patterns: (let distance, 0) matches points on the x-axis and (0, let distance) matches points on the y-axis. Both patterns include a binding for distance and distance is an integer in both patterns—which means that the code in the body of the case can always access a value for distance.

제어 전달 문

제어 전달 문은 코드 실행의 순서를 변화시키는 것으로서 코드의 한부분으로부터 제어를 다른 곳으로 전송한다. 스위프트는 5가지의 제어 전송 문을 가진다.
- continue
- break
- fallthrough
- return
- throw

continue, break, fallthrough 는 아래에서 설명한다. return 문은 함수에서 throw 는 Propagating Errors Using Throwing Functions 에서 설명한다

continue

The continue statement tells a loop to stop what it is doing and start again at the beginning of the next iteration through the loop. It says “I am done with the current loop iteration” without leaving the loop altogether.

The following example removes all vowels and spaces from a lowercase string to create a cryptic puzzle phrase:

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"
The code above calls the continue keyword whenever it matches a vowel or a space, causing the current iteration of the loop to end immediately and to jump straight to the start of the next iteration.

break

The break statement ends execution of an entire control flow statement immediately. The break statement can be used inside a switch or loop statement when you want to terminate the execution of the switch or loop statement earlier than would otherwise be the case.

루프문 에서 브레이크

When used inside a loop statement, break ends the loop’s execution immediately and transfers control to the code after the loop’s closing brace (}). No further code from the current iteration of the loop is executed, and no further iterations of the loop are started.

스위치문에서 브레이크

When used inside a switch statement, break causes the switch statement to end its execution immediately and to transfer control to the code after the switch statement’s closing brace (}).

이 작용은 스위치 문에서 매치하거나 하나이상의 케이스를 무시하는데 사용될 수 있다. 스위프트의 스위치는 명확해야 하고 빈 케이스를 허용하지 않으므로 상황에 따라 의도적으로 매치시키거나 특정 케이스를 무시하여 인텐션을 명료하게 한다. 무시하고 싶은 케이스의 전체 바디로 브리에크 문을 작성해 이를 수행할 수 있다. 이 케이스가 스위치 문에 매치되면 케이스내의 브레이크문은 스위치문의 실행을 즉시 종료한다.

일러두기)
A switch case that contains only a comment is reported as a compile-time error. Comments are not statements and do not cause a switch case to be ignored. Always use a break statement to ignore a switch case.

The following example switches on a Character value and determines whether it represents a number symbol in one of four languages. For brevity, multiple values are covered in a single switch case.

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

This example checks numberSymbol to determine whether it is a Latin, Arabic, Chinese, or Thai symbol for the numbers 1 to 4. If a match is found, one of the switch statement’s cases sets an optional Int? variable called possibleIntegerValue to an appropriate integer value.

After the switch statement completes its execution, the example uses optional binding to determine whether a value was found. The possibleIntegerValue variable has an implicit initial value of nil by virtue of being an optional type, and so the optional binding will succeed only if possibleIntegerValue was set to an actual value by one of the switch statement’s first four cases.

위의 예시의 가능한 문자 값을 전부 리스트하지 않았으므로 디폴트 케이스는 매치되지 않는 캐릭터를 다룬다. 이 디폴트 케이스는 어떤 액션을 수행하는 것도 원하지 않으므로 이 바디에는 브레이크만 있다. 디폴트 케이스가 매치되고 브레이크 문이 스위치문의 실행을 종료시킨다. 코드는 if let 문으로 계속된다.

Fallthrough

스위프트에서 스위치 문은 각 케이스의 아래에서 다음 것으로 폴스루하지 않는다. 그래서 전체 스위치문은 첫 매칭 케이스가 완료되면 끝난다. 대조적으로 C는 명시적 break를 넣어야 이런 폴스루를 방지할 수 있다. 기본 폴스루를 방지하는 것은 스위프트 스위치가 더 명확하고 예측가능하다는 것을 의미한다. 그래서 실수에 의한 실행을 방지한다.

만약 C스타일 폴스루가 필요하다면 fallthrough 키워드를 사용할 수 있다. The example below uses fallthrough to create a textual description of a number.

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

This example declares a new String variable called description and assigns it an initial value. The function then considers the value of integerToDescribe using a switch statement. If the value of integerToDescribe is one of the prime numbers in the list, the function appends text to the end of description, to note that the number is prime. It then uses the fallthrough keyword to “fall into” the default case as well. The default case adds some extra text to the end of the description, and the switch statement is complete.

Unless the value of integerToDescribe is in the list of known prime numbers, it is not matched by the first switch case at all. Because there are no other specific cases, integerToDescribe is matched by the default case.

After the switch statement has finished executing, the number’s description is printed using the print(_:separator:terminator:) function. In this example, the number 5 is correctly identified as a prime number.

NOTE

The fallthrough keyword does not check the case conditions for the switch case that it causes execution to fall into. The fallthrough keyword simply causes code execution to move directly to the statements inside the next case (or default case) block, as in C’s standard switch statement behavior.

레이블드문

In Swift, you can nest loops and conditional statements inside other loops and conditional statements to create complex control flow structures. However, loops and conditional statements can both use the break statement to end their execution prematurely. Therefore, it is sometimes useful to be explicit about which loop or conditional statement you want a break statement to terminate. Similarly, if you have multiple nested loops, it can be useful to be explicit about which loop the continue statement should affect.

To achieve these aims, you can mark a loop statement or conditional statement with a statement label. With a conditional statement, you can use a statement label with the break statement to end the execution of the labeled statement. With a loop statement, you can use a statement label with the break or continue statement to end or continue the execution of the labeled statement.

A labeled statement is indicated by placing a label on the same line as the statement’s introducer keyword, followed by a colon. Here’s an example of this syntax for a while loop, although the principle is the same for all loops and switch statements:

  1. label name: while condition {
  2. statements
  3. }


The following example uses the break and continue statements with a labeled while loop for an adapted version of the Snakes and Ladders game that you saw earlier in this chapter. This time around, the game has an extra rule:

To win, you must land exactly on square 25.
If a particular dice roll would take you beyond square 25, you must roll again until you roll the exact number needed to land on square 25.

The game board is the same as before.

../_images/snakesAndLadders_2x.png


The values of finalSquare, board, square, and diceRoll are initialized in the same way as before:

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
This version of the game uses a while loop and a switch statement to implement the game’s logic. The while loop has a statement label called gameLoop to indicate that it is the main game loop for the Snakes and Ladders game.

The while loop’s condition is while square != finalSquare, to reflect that you must land exactly on square 25.

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!")
The dice is rolled at the start of each loop. Rather than moving the player immediately, the loop uses a switch statement to consider the result of the move and to determine whether the move is allowed:

If the dice roll will move the player onto the final square, the game is over. The break gameLoop statement transfers control to the first line of code outside of the while loop, which ends the game.
If the dice roll will move the player beyond the final square, the move is invalid and the player needs to roll again. The continue gameLoop statement ends the current while loop iteration and begins the next iteration of the loop.
In all other cases, the dice roll is a valid move. The player moves forward by diceRoll squares, and the game logic checks for any snakes and ladders. The loop then ends, and control returns to the while condition to decide whether another turn is required.
NOTE

If the break statement above did not use the gameLoop label, it would break out of the switch statement, not the while statement. Using the gameLoop label makes it clear which control statement should be terminated.

It is not strictly necessary to use the gameLoop label when calling continue gameLoop to jump to the next iteration of the loop. There is only one loop in the game, and therefore no ambiguity as to which loop the continue statement will affect. However, there is no harm in using the gameLoop label with the continue statement. Doing so is consistent with the label’s use alongside the break statement and helps make the game’s logic clearer to read and understand.

얼리 엑시트

guard 문은 if 문과 같이 표현의 불린 값에 기반해 문장을 실행한다. guard 문을 조건에 넣음으로서 가드문 이후 코드를 실행하려면 반드시 조건이 참이어야 함을 요구한다. if문과 달리 가드 문은 항상 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."

만약 가드 문이 조건에 맞으면 코드는 가드 문의 이후의 코드가 실행된다. Any variables or constants that were assigned values using an optional binding as part of the condition are available for the rest of the code block that the guard statement appears in.

만약 컨디션이 맞지 않다면 else내의 코드가 실행된다. 이 브랜치는 반드시 가드문이 보여지는 곳 내의 코드 블록을 종료해야한다. 제어 전송문인 return, break, continue, 또는 throw 또는 함수를 호출하거나 또는 반환하지 않는 함수인 fatalError(_:file:line:) 과 같은 함수를 호출할 수 있다.

요구사항을 위한 가드문은 if문과 비교할 수 있어 코드의 가독성을 증가시킨다. else블록에서 래핑하지 않고 실행하는 코드를 작성할 수 있게하며 다음 요구사항에서 위반된 요구사항을 다루는 코드를 유지하게 한다.

API 가용성 체킹

스위프트는 API가용여부를 확인하기 위한 빌트인 서포트를 제공한다. 이를 통해 주어진 배포타겟에서 가용하지 않는 API를 사용할 수 없게 해준다.

컴파일러는 SDK내의 가용 정보를 사용해 코드에서 사용하는 API가 배포 대사에서 가용한지를 확인한다. 스위프트는 가용하지 않다면 컴파일 타임에 에러를 보여준다.

if또는 guard 문에 가용성 조건을 사용하여 코드의 블록을 실행할 수 있다. 컴파일러는 코드의 블록내의 API가 가용한지를 확인할 때 아용 조건으로부터의 정보를 사용한다.

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
}

위의 조건은 iOS 10 이후에서만 맥에서는 10.12이후에서만, 그리고 마지막 매개변수 *는 다른 어떤 플랫폼에서든 if 는 타겟으로 지정된 최소 배포 타겟에 맞춰 실행한다.

In its general form, the availability condition takes a list of platform names and versions. You use platform names such as iOS, macOS, watchOS, and tvOS—for the full list, see Declaration Attributes. In addition to specifying major version numbers like iOS 8 or macOS 10.10, you can specify minor versions numbers like iOS 11.2.6 and macOS 10.13.3.

if #available(platform name version, ..., *) {
    statements to execute if the APIs are available
} else {
    fallback statements to execute if the APIs are unavailable
}



덧글

댓글 입력 영역