※ 본 포스팅은 Swift 공식문서 The Swift Programming Language 중 A Swift Tour 내용을 기반으로 제가 직접 정리한 스위프트 문법에 관한 글입니다.
Swift 언어에 대해 아예 모르는 상태에서 적은 글입니다. 다른 프로그래밍 언어를 다룬 적은 있지만 Swift는 처음이라 공식문서를 보고 차근차근해보는 중입니다. 제가 쉽다고 생각하는 부분은 생략될 수도 있으며 공식문서에서 모르는 부분은 추가해서 적을 수도 있습니다.
A Swift Tour는 공식문서 가장 초반부에 위치해 이후 공식문서에서 다루는 내용을 간결하게 소개하는 챕터라고 볼 수 있습니다. 그렇기에 직접 읽어본 바로는 상당히 불친절하게 적혀있습니다. 저는 공식문서를 다 읽고 이 부분을 보는 게 아니기 때문에 알아낸 정보만 적혀있다는 사실을 알려드립니다.
< 서론 >
print("Hello, world!")
Swift 언어는 입출력 "Hello, World" 라는 문장을 print 하기까지 한 줄이면 된다는 문장으로 글이 시작됩니다.
그러면서 Swift의 특징이면서 장점을 나열합니다.
1. input/output이나 string을 다루는 라이브러리를 굳이 import할 필요가 없다. (C언어를 저격하는 듯 싶다.)
2. 전역 범위에서 쓰인 코드는 프로그램 전체에 사용되기 때문에 C처럼 main() 함수를 쓰지 않아도 된다.
3. No semicolons -> 일일이 세미콜론을 문장 끝에 안 붙여도 됨.
이렇게 다른 언어를 까면서 스위프트의 장점을 말하고, 이 Tour는 소개이기 때문에 전체를 이해할 필요는 없다는 이야기를 해줍니다.
< 1. Simple Values >
a. Constants and Variables
var myVariable = 42
myVariable = 50
let myConstant = 42
1. let 은 상수(constant), var 은 변수(variable)를 만들기 위해 사용된다.
2. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once. This means you can use constants to name a value that you determine once but use in many places.
→ 본문에서 그대로 발췌한 두 문장입니다. 저는 여기서부터 이해가 잘 안 갔습니다. 그래서 제가 이해한 내용을 적어보겠습니다.
- 상수는 값이 한 번 할당되면 변경할 수 없는 변수를 의미한다.
- 첫 문장에서 번역하면 상수의 값이 컴파일 시간에 결정될 필요가 없다는 뜻이다. → 이는 상수의 값이 런타임에서 결정될 수 있다는 뜻이다.
let pi: Double = 3.14159265359
- 위와 같이 pi 의 값은 컴파일러가 상수를 선언할 때 알고 있지만, 상수의 값은 변경되지 않기에 컴파일러가 필요한 경우 런타임에 값을 계산할 수 있다.
- 그렇기에 (두 번째 문장) 상수를 사용하면 값을 한 번 결정하고 필요할 때마다 사용할 수 있다.
- 여기서 주의해야 할 것은 값을 할당하는 것과 컴파일 시점에 값을 알아야 하는 것은 서로 다른 개념이라는 거다. 따라서 런타임에 값을 할당하더라도 컴파일러는 이를 인식할 수 있다.
- 아래는 상수가 런타임에 값을 할당하는 경우이다.
let currentTime = Date()
- 이와 같이 현재 시간을 나타내는 상수를 선언할 때는 컴파일 시간에는 현재 시간을 알 수 없으므로 런타임에 값을 할당하게 된다.
- 이 경우 let 을 사용하여 상수를 선언하였지만, 런타임에 현재 시간이 할당되므로 이후에는 값을 변경할 수 없다.
3. 상수 또는 변수는 할당하려는 값과 반드시 동일한 타입이어야 한다. 하지만 항상 타입을 명시해야 하는 것은 아니다.
- 상수나 변수를 만들 때 값을 제공하면 컴파일러가 그것의 타입을 추론한다. 하지만 초기값이 충분한 정보를 제공하지 않는다던가, 초기값이 없다면 타입을 명시해야 한다.
- 변수나 상수 뒤에 : 을 붙여서 데이터 타입 명시
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
- 위 코드를 보면 알 수 있듯이, 세 번째 explicitDouble을 Double이라고 명시하지 않았다면 컴파일러가 Integer로 착각할 수 있다. 이럴 경우 초기값이 충분한 정보를 제공하지 않은 것이므로 데이터 타입을 명시할 필요가 있다.
b. Data types
1. 값의 타입을 다른 타입으로 암시적으로 변환하지 않는다(implict conversion X), 만약 값의 타입을 바꿀 필요가 있다면, 명시적으로 원하는 타입의 인스턴스를 만들어야 한다. (explicit conversion O)
- 여기서 implicit conversion은 컴파일러가 자동으로 타입을 변환해 주는 것이다. 예를 들어 Swift에서 정수와 실수는 다른 타입으로 취급되기 때문에, 정수와 실수를 연산하면 컴파일러는 자동으로 정수를 실수로 변환해 준다.
- explicit conversion은 코드 작성자가 직접 타입을 변환해 주는 것이다.
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
- 여기서 String(width)는 Int 타입인 width 변수를 String 타입으로 명시적으로 변환한 것이다.
- 이렇게 생성된 String 인스턴스가 widthLabel 상수에 할당된다.
- 위 예시에서 String을 없애고 그냥 label과 width를 더하려고 하면 이런 오류가 발생한다.
Binary operator '+' cannot be applied to operands of type 'String' and 'Int'
Overloads for '+' exist with these partially matching parameter lists: (Int, Int), (String, String)
- 다른 타입의 두 개를 컴파일러 내에서 변환시켜 계산할 수 없음을 오류를 통해 알 수 있다.
2. String 타입으로 만드는 더 간단한 방법은 \( ) 을 사용하는 것이다.
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
3. (""") three double quotation marks -> multiple lines를 String으로 쓸 수 있게 해 준다.
let quotation = """
I said "I have \(apples) apples."
And then I said "I have \(apples + oranges) pieces of fruit."
"""
4. array 랑 dictionary는 [ ] (brackets)을 사용해서 만들 수 있다.
- [ ] 안에 index나 key 를 적어서 각각의 요소(element)에 접근할 수 있음
- 마지막 요소 뒤에 ,(comma)를 붙여도 상관없다.
var fruits = ["strawberries", "limes", "tangerines"]
fruits[1] = "grapes" // 대괄호 안에 1 인데스 넣어서 grapes 를 추가함. -> 1번자리에 추가
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations" // 대괄호 안에 key를 적고 "Public Relartions"라는 요소를 추가함.
5. Array(배열)은 자동적으로 사용자가 요소(element)를 추가하는 만큼 추가가 된다.
fruits.append("blueberries")
print(fruits)
// Prints "["strawberries", "grapes", "tangerines", "blueberries"]"
- append 메소드를 사용하면 array의 맨 마지막에 요소가 추가되는 것 같다.
6. 대괄호만 써서 빈 array( [ ] ) 나 dictionary ( [ : ] )도 표현이 가능하다.
let emptyArray: [String] = []
let emptyDictionary: [String: Float] = [:]
- 만약 네가 빈 배열이나 사전에 새로운 값을 할당하려고 하거나, 또는 다른 장소에 타입 정보가 없는 경우 타입을 정해주어야 한다.
< 2. Control Flow >
조건문을 만들 때는 if 나 switch를 사용하고, 반복문을 만들 때는 for-in, while, repeat-while을 사용한다.
조건이나 루프 변수를 ( ) 가 둘러싸는 건 option이지만, 본문 { }는 필수적이다.
a. If
1. if문에서 조건부는 반드시 Boolean expression이어야 한다. (true or false)
var score: Int = 40
if score {
...
}
// 이런 조건문은 있을 수 없다
// score이 Integer 형태이기 때문에 Boolean expression이 아니다.
2. if 와 let을 함께 사용하여 누락될 수 있는 값에 대해 사용할 수 있다. 이 값들은 optionals로 표기된다.
(이 부분도 이해가 안 되는 부분이었는데 찾아보니 Optional Binding이라는 파트였습니다. 조금이라도 찾은 내용을 밑에 적어보겠습니다.)
- optional value란? 값을 포함하거나 값이 없음(nil)을 포함한다.
→ 값이 없을 가능성이 존재해 오류가 날 수 있는 값이라는 의미인 것 같다. - optional value라는 것을 표시하기 위해 value의 타입 뒤에 물음표(?)를 작성한다.
- 그럼 값이 없는 상태인 nil 이란? 예를 들어 String 타입의 변수에 문자열을 할당하는 경우, 해당 변수는 값이 있다.
그러나 처음부터 nil 값을 할당하거나, 초기화하지 않은 경우 해당 변수는 값이 없는 상태가 된다.
→ 변수나 상수를 선언하고 초기화하지 않은 경우, 해당 변수나 상수는 값을 가지지 않는 상태이다. 이를 uninitialized 상태 또는 uninitialized 변수 또는 상수라고도 한다. (초기화라는 단어가 번역이라서 헷갈릴 만한 부분) - optional binding이란? if 와 let 을 같이 사용해서 Optional value가 값이 있는지 없는지 여부를 확인하고 안전하게 처리하는 방식.
var optionalString: String? = "Hello"
print(optionalString == nil)
// Prints "false"
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)" // name 변수가 nil이 아닌 경우 실행됨
} else {
greeting = "Hello, anonymous" // name 변수가 nil인 경우 실행됨
}
- 위 코드에서 String? 은 이 변수를 Optional String 타입으로 선언한 것이다.
- 동시에 "Hello"라는 String값을 할당했기에 두 번째 줄인 nil과 동일한지 여부는 false라고 나온다.
- 위 코드가 수정되지 않는 한 greeting 은 "Hello John Appleseed"가 나올 것이다.
if let constantName = optionalValue {
// optionalValue가 nil이 아닌 경우 실행되는 코드 블록
// constantName 상수에 optionalValue의 값을 할당받아 사용할 수 있음
} else {
// optionalValue가 nil인 경우 실행되는 코드 블록
}
- 헷갈릴까 봐 다시 한번 코드로 정리했다.
3. Optional Value를 다루는 다른 방법은 ?? operator를 사용해서 Default Value를 제공하는 것이다.
만약 Optional Value 값이 없다면 (즉, nil 상태라면), default value가 대신 사용된다.
let nickname: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickname ?? fullName)"
// 이경우 nickname(optional value)이 nil이기 때문에
// informalGreeting 은 Hi John Applessed 가 된다
// (default 값이 대신 쓰임)
- ?? 연산자는 optional value가 nil인 경우에 default valu를 사용하도록 지시한다.
- ?? 연산자를 사용하여 default value를 지정하면 optional value가 nil일 때도 안전하게 코드를 실행할 수 있다.
b. Switch
1. Switch문은 모든 종류의 데이터와 다양한 비교작업을 지원한다. Switch는 정수 및 동등성 비교로 제한되지 않는다.
- 정수와 같은 특정한 데이터 유형에 국한되지 않으며, 동등성 테스트뿐만 아니라 다양한 비교 연산을 지원한다는 뜻
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
// Prints "Is it a spicy red pepper?"
2. Swift문에서 default case를 제거하면 생기는 오류
Switch must be exhaustive
- Switch문에서 default case를 제거하면 Switch문이 처리할 수 없는 값이 들어올 경우 어떤 case에도 해당하지 않는 상황이 발생한다.
- 이러한 상황에서는 컴파일 오류를 발생시키며 위와 같은 오류메시지를 표시한다.
- 만약 모든 경우를 다룰 수 없는 상황이라면, Switc문 대신 if-else문을 사용하는 것이 좋다.
3. 일치하는 Switch문의 case 코드를 실행하고 난 뒤, 그 프로그램은 Switch문에서 벗어난다. 다음 case가 이어서 나오지 않기 때문에 일일이 case에 break out을 적을 필요가 없다.
c. for-in
1. for-in을 사용하여, 각 key-value 쌍에 사용할 이름의 쌍을 제공하여 딕셔너리 항목을 조회한다.
- 아래의 코드에서 제공된 이름의 쌍은 ( _, numbers )이다.
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
// Prints "25"
- 딕셔너리는 순서가 없는 collection이므로 key와 value는 임의의 순서로 조회된다.
d. while / repeat-while
1. 조건이 끝날 때까지 코드 블록을 반복하려면 while을 사용할 수 있다.
var n = 2
while n < 100 {
n *= 2
}
print(n)
// Prints "128"
2. 루프의 조건이 끝에 있을 경우 최소한 한 번의 루프를 돌리는 것을 보장한다 <- repeat while에 대한 설명
var m = 2
repeat {
m *= 2
} while m < 100
print(m)
// Prints "128"
e. range of indexes
1. 인덱스의 범위를 만들기 위해 . . < 를 사용하여 루프에 인덱스를 만들 수 있다.
var total = 0
for i in 0..<4 {
total += i
}
print(total)
// Prints "6"
- . . < → 가장 상위값을 생략하는 범위 / 파이썬으로 따지면 range(0,4)와 동일 ex) 0 ..< 4 == 0, 1, 2, 3
- . . . → 양쪽 값 모두 포함한다. ex) 0 ... 4 == 0, 1, 2, 3, 4
뒤로 갈수록 지금 한 것보다 어려워질 것 같아서 공식문서 A Swift Tour를 끝까지 포스팅할 수 있을지는 모르겠지만 되는 데까지 해보려고 합니다. 혹시 틀린 부분이나 추가해주고 싶은 부분이 있으면 알려주세요.