Swift 공식 문서와 한글 번역을 정리하고 공부합니다.
상수 : let
한 번 값을 할당하면 바꿀 수 없다.
변수 : var
값을 변경할 수 있다.
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
var x = 0.0, y = 0.0, z = 0.0
한 줄에서 다수의 상수 또는 변수를 선언할 수 있다.
변수나 상수를 선언할 때, type annotation을 통해 타입을 지정할 수 있다. 이는 상수 또는 변수가 어떤 타입을 저장할 수 있는지를 명시적으로 보여준다.
var welcomeMessage: String
welcomeMessage = "Hello"
var red, green, blue: Double
"welcomeMessage
이라는 변수를 String
타입으로 선언한다. "
타입이 모두 같다면, 한 줄에서 다수의 상수 또는 변수의 타입을 지정할 수 있다.
Swift는 타입 추론을 제공하기 때문에 type annotation을 명시하지 않았다면 최초 값을 토대로 타입을 추론하게 된다.
let π = 3.14159
let 상수 = "상수입니다."
let 🐶🐮 = "dogcow"
상수와 변수 이름은 유니코드에서 지원하는 대부분의 문자를 포함할 수 있다.
다음의 경우는 허용하지 않는다.
공백이 포함된 이름
숫자로 시작하는 이름
연산자로 사용될 수 있는 기호 및 화살표
Swift에서 사용하고 있는 예약어 또는 키워드
┌와 같은 box-drawing characters
│와 같은 line-drawing characters
해당 코드 범위 내에서 이미 사용하고 있는 이름
한 번 상수와 변수의 타입을 선언했다면,
같은 이름으로 재정의할 수 없다.
다른 타입의 값을 저장할 수 없다.
상수를 변수로 바꾸거나, 변수를 상수로 바꿀 수 없다.
기존에 존재하는 변수를 호환되는 타입의 값으로 변경할 수 있다.
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
상수를 변경하려고 할 시, 컴파일 과정에서 에러가 발생한다.
let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.
print(_:separator:terminator:)
을 사용해 상수 혹은 변수를 출력할 수 있다.
print 함수는 그것의 결과물을 XCode의 console
에 출력한다. seperator
와 terminator
인자에는 기본값이 존재하기 때문에 생략이 가능하다.
출력하면서 줄을 넘기고 싶지 않다면 print(someValue, terminator: "")
와 같은 형태로 terminator
에 값을 할당한다.
Swift는 문자열 보간(string interpolation)
을 사용할 수 있다.
String에 placeholder로서 상수 혹은 변수 이름을 포함하는 방식으로 작동한다.
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"
실행되지 않는 텍스트인 주석을 코드에 포함시킬 수 있다.
한 줄의 주석은 //
로 시작하고, 여러 줄의 주석은 /* */
안에 포함시키면 된다.
// This is a comment.
/* This is also a comment
but is written over multiple lines. */
주석 안에 다른 주석이 중첩될 수 있다.
/* This is the start of the first multiline comment.
/* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */
Swift는 세미콜론(;)
을 필요로 하지 않는다. 하지만 한 줄에 여러 구문을 작성하고자 할 경우에는 세미콜론으로 각각을 분리해야 한다.
let cat = "🐱"; print(cat)
// Prints "🐱"
Swift에서 정수(Integers)
는 양의 정수, 0, 음의 정수를 모두 포함하는 signed
와 양의 정수, 0만을 포함하는 unsigned
로 나뉘어진다.
Swift는 8, 16, 32, 64 비트의 정수 형태를 제공한다.
UInt8 : 8-bit unsigned integer
Int32 : 32-bit signed integer
다음과 같이 각 정수 타입의 최솟값과 최댓값에 접근할 수 있다.
let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8
일반적으로는 Integer
의 사이즈를 지정할 필요가 없다. Int
타입은 32비트 플랫폼에서는 Int32
로, 64비트 플랫폼에서는 Int64
로 설정된다.
UInt
역시 같은 방법으로 사이즈가 결정된다. UInt
는 특별히 unsigned integer
가 필요한 것이 아니면 사용하지 않는 것이 권장된다.
Floating-point
타입은 integer
타입보다 더 넓은 범위의 수를 표현할 수 있다. Swift에서는 두 가지 종류의 Floating-Point Number 타입이 있다.
Double
타입은 64비트의 Floating-point number를 표현한다.
Float
타입은 32비트의 Floating-point number를 표현한다.
Double
타입은 소수점 이하 15자리, Float
은 소수점 이하 6자리를 표현할 수 있다. 일반적으로 Double을 사용하는 것이 권장된다.
Swift는 type-safe
언어다. 이는 코드가 작동할 수 있도록 하는 값들의 타입을 명확하게 만들어주는 장점이 있다.
코드를 컴파일할 때 타입 체크를 실시하며, 타입이 맞지 않을 경우 에러를 발생시킨다. 개발 과정에서 빠르게 에러를 발견하고 고칠 수 있도록 해 준다.
타입 체크는 다른 타입의 값을 사용함으로써 발생하는 에러를 피할 수 있게 돕는다. 하지만 이는 선언한 모든 상수와 변수가 특정 타입만을 가져야 한다는 뜻은 아니다. 만약 값의 타입을 특정하지 않는다면, Swift는 타입 추론
을 통해 적당한 타입을 찾아 적용한다. 타입 추론은 컴파일러가 코드를 컴파일할 때 특정 표현의 타입을 자동으로 추론할 수 있도록 한다.
타입 추론은 초기값과 함께 상수, 변수를 선언할 때 특히 유용하다. 이는 리터럴(literal)
을 상수나 변수에 할당할 때 자주 사용된다.
let a(상수) = 1(리터럴)
상수 : 변하지 않는 변수. 참조변수를 상수로 지정할 때, 참조변수는 상수지만 그 주소가 가리키는 데이터들까지 상수라는 의미는 아니다.
리터럴 : 변수에 넣는 변하지 않는 데이터 그 자체.
let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int
let pi = 3.14159
// pi is inferred to be of type Double
만약 integer
와 floating-point
리터럴을 결합한다면, 그 상수/변수의 타입은Double
로 추론된다.
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
정수 리터럴은 다음과 같이 작성될 수 있다.
10진수 : 접두사를 붙일 필요가 없다.
2진수 : 0b
8진수 : 0o
16진수 : 0x
let decimalInteger = 17
let binaryInteger = 0b10001 // 17 in binary notation
let octalInteger = 0o21 // 17 in octal notation
let hexadecimalInteger = 0x11 // 17 in hexadecimal notation
실수 리터럴은 10진수 혹은 16진수가 될 수 있다. 실수 리터럴은 항상 소수점 양 쪽(정수부, 소수부)의 값을 갖고 있어야 한다.
10진수 소수는 e의 거듭제곱으로 표현할 수 있다. 16진수는 e 대신 p가 사용된다.
1.25e2
means 1.25 x 102, or125.0
.1.25e-2
means 1.25 x 10-2, or0.0125
.0xFp2
means 15 x 22, or60.0
.0xFp-2
means 15 x 2-2, or3.75
// 12.1875를 표현하는 세 가지 방법
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
숫자 리터럴(Numeric literals)은 가독성을 위해 추가적인 포맷팅을 포함할 수 있다. 정수와 실수 모두 추가적인 0으로 채워지거나 언더스코어(_)을 추가하는 것이 가능하다. 이러한 포맷팅은 리터럴에 어떤 영향도 미치지 않는다.
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
각각의 숫자 타입마다 정수형 상수, 변수에 저장할 수 있는 숫자의 범위가 다르다. Int8
상수, 변수의 경우 -128부터 127까지의 숫자만 저장할 수 있다. 반면 UInt8
상수, 변수는 0부터 255까지의 숫자를 저장할 수 있다. 이 범위에 맞지 않을 경우 코드가 컴파일될 때 에러가 발생한다.
let cannotBeNegative: UInt8 = -1
// UInt8 cannot store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 cannot store a number larger than its maximum value,
// and so this will also report an error
각각의 숫자 타입이 저장할 수 있는 범위가 다르기 때문에, 상황에 맞춰 사전에 타입 변환을 실시해야 한다. 이러한 opt-in approach
는 hidden conversion errors
를 방지하고 타입 변환의 의도를 명시적으로 만든다.
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
SomeType(ofInitialValue)
은 Swift 타입의 initializer
를 호출하는 기본적인 방법이다. 위 사례에서 UInt16
은 UInt8
값을 받아들일 수 있는 initializer를 갖고 있으며, 기존의 UInt8
값을 새로운 UInt16
값으로 만든다.
정수와 실수 사이의 변환은 반드시 명시적이어야 한다.
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
상수 three
는 Double
타입의 새로운 값으로 만들어져 양쪽 값이 같아지게 된다. 이러한 변환이 없는 합연산은 허용되지 않는다.
실수에서 정수로 변환하는 것도 반드시 명시적이어야 한다. 새로운 정수로 변환되는 실수값은 항상 truncated
된다. (4.75 -> 4, -3.9 -> -3)
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int
상수/변수를 결합하는 것(three + pointOneFourOneFiveNine 또는 three + 0.14159)과 리터럴끼리 결합하는 것(3 + 0.14159)은 엄연히 다르다. 리터럴끼리의 결합은 에러가 발생하지 않는다. 숫자 리터럴은 명시적 타입을 갖고 있지 않으며, 컴파일 과정에서 평가되어 타입이 추론된다.
Type Alias
는 기존의 타입을 대체하는 이름을 typealias
키워드를 사용해 정의한다. 기존의 타입명을 문맥적으로 더 적합한 이름으로 지칭하는 데 유용하다.
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
Swift의 Boolean
타입은 Bool
로 칭한다. Swift는 true
와 false
두 가지의 상수값을 제공한다.
let orangesAreOrange = true
let turnipsAreDelicious = false
Boolean
값은 조건문과 함께할 때 특히 유용하다.
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else {
print("Eww, turnips are horrible.")
}
// Prints "Eww, turnips are horrible."
스위프트의 타입 세이프는 Boolean
이 아닌 값이 그 역할을 대신하지 못하도록 한다.
let i = 1
if i {
// this example will not compile, and will report an error
}
if i == 1 {
// this example will compile successfully
}
튜플(Tuple)
은 다양한 값을 하나의 결합 값으로 묶은 것이다. 튜플 안의 값은 어떤 타입이라도 될 수 있고, 같은 타입으로 묶이지 않아도 된다.
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
다양한 타입의 조합으로 튜플을 생성할 수 있다. 또한 튜플 내의 값을 상수, 변수들로 나누어 분해할 수 있다.
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// 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)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"
튜플은 함수의 리턴값으로 유용하게 쓸 수 있다. 다양한 타입으로 조합된 튜플을 사용하여 단일 타입을 반환할 때보다 더 유용한 정보를 제공하는 함수를 만드는 것이 가능하다.
빈 값이 들어갈 수 있다면, optionals
를 사용할 수 있다.
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"
Swift의 Int
타입은 String
을 Int
로 변환시켜주는 initializer
를 갖고 있다. 하지만 모든 문자열이 정수로 변환되는 것은 아니다. "123"은 123으로 변환될 수 있지만, "hello, world"는 정수로 바뀔 수 없다. 이처럼 initializer
가 실패할 수 있기 때문에, optional Int
를 반환한다. 이는 Int?
로 표현된다. 물음표는 이 상수/변수가 포함하고 있는 값이 optional
이며, Int
값을 포함하거나 아무것도 포함하지 않음을 뜻한다.
optional 변수
의 변수 없음 상태는 nil
로 나타낸다.
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
만약 optional 변수
를 초기화하지 않고 선언한다면 자동으로 nil
값이 저장된다.
var surveyAnswer: String?
// surveyAnswer is automatically set to nil
Swift의 nil과 Objective-C의 nil은 다른 개념이다. Objective-C에서 nil은 존재하지 않는 오브젝트를 가리키는 포인터다. 반면 Swift에서 nil은 포인터가 아니라 특정 타입의 값이 존재하지 않음을 나타낸다. 오브젝트 타입뿐만 아니라 어떤 것이라도 nil이 될 수 있다.
optional
을 nil
과 비교하는 if
구문을 사용하여 optional
이 값을 포함하고 있는지 판단할 수 있다.
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."
값을 포함하고 있는 게 확실하다면, optional
의 이름 뒤에 느낌표(!)를 붙여서 값에 접근할 수 있다. 느낌표는 "이 optional
은 확실히 값을 가지고 있다."는 것을 뜻한다. 이를 optional value의 forced unwrapping
이라 칭한다.
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// Prints "convertedNumber has an integer value of 123."
optional binding
을 통해 optional
이 값을 포함하고 있는지 찾고, 만약 그렇다면 이를 임시 상수/변수로 활용할 수 있다. 한 번의 액션으로 optional
내부의 값을 체크하고, 그 값을 상수/변수로 추출한다. 이를 if
와 while
구문에 사용 가능하다.
if let constantName = someOptional {
// statements
}
if let actualNumber = Int(possibleNumber) {
print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
print("The string \"\(possibleNumber)\" could not be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"
이 코드는 다음과 같이 해석할 수 있다.
"만약 Int(possibleNumber)
가 반환하는 optional Int
가 값을 포함하고 있다면, actualNumber
라는 새로운 상수에 이를 할당한다."
변환이 성공했다면 actualNumber
는 첫 번째 조건문 안에서 사용할 수 있게 된다. 여기서는 값에 접근하기 위해 느낌표(!)를 사용할 필요가 없다.
많은 수의 optional binding
과 조건을 ,
로 구분하여 하나의 if
구문에 포함시킬 수 있다. optional binding
이 nil
이거나 조건이 false
인 것이 단 하나라도 존재한다면, 그 if
구문은 false
로 취급된다.
if let firstNumber = Int("4"), <let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// Prints "4 < 42 < 100"
optional
이 첫 번째 값을 할당받은 이후에 항상 값을 가지고 있는 경우가 있을 것이다. 이 경우에는 optional
의 값을 체크하고 unwrap할 필요를 없애고 항상 접근할 수 있도록 하는 것이 유용하다.
이러한 optional
을 implicitly unwrapped optionals
라 부른다. 타입의 뒤에 물음표(?) 대신 느낌표(!)를 붙임으로써 이를 표현할 수 있다.
implicitly unwrapped optionals
는 첫 번째 값이 할당된 즉시 값이 존재하며, 앞으로도 계속 존재할 것임이 확실할 때 사용하면 유용하다. 클래스를 초기화 할 때 가장 잘 쓸 수 있다.
implicitly unwrapped optionals
는 이면에서는 일반 optional value
와 동일하지만, 접근할 때마다 unwrapping 할 필요 없이 non-optional value
처럼 사용할 수 있다.
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation point
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation point
let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.
implicitly unwrapped optionals
를 사용할 때 Swift는 우선 일반적인 optional
값처럼 이를 사용하려고 시도한다. 위의 코드에서 assumedString
은 implicitString
에 할당되기 전 force-unwrap된다. implicitString
이 explicit non-optional
값이기 때문이다. optionalString
은 타입이 명시적이지 않고, 일반적인 optional
값으로 할당된다.
만약 implicitly unwrapped optionals
이 nil
이고 여기에 접근하려 시도한다면 런타임 에러가 발생한다.
implicitly unwrapped optionals
역시 nil
체크와 optional binding
이 가능하다.
if assumedString != nil {
print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."
if let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
실행 중 에러가 발생했을 때 대응하기 위해 error handling
을 사용한다.
함수는 에러를 만났을 때 그것을 던진다(throws)
. 이를 잡아서(catch)
적절히 처리할 수 있다.
func canThrowAnError() throws {
// this function may or may not throw an error
}
throws
키워드를 통해 이 함수는 에러를 던질 수 있음을 알려준다. 이와 같이 에러를 던질 수 있는 함수를 호출할 때, try
키워드를 표시해야 한다.
do {
try canThrowAnError()
// no error was thrown
} catch {
// an error was thrown
}
do
구문에서 새로운 범위를 생성하고, 에러를 catch
구문에 전달할 수 있도록 한다.
여러 개의 에러는 다음과 같이 처리한다.
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
assertions
와 preconditions
는 런타임 시에 발생하는 체크다. 코드를 실행하기 전에 핵심적인 상태를 만족하는지 확인하고자 사용한다. assertions
와 preconditions
안의 Boolean 조건이 true
라면 코드가 실행되지만, false
라면 코드 실행이 멈추고 앱이 종료된다.
코드 작성 중 세운 가설이나 예상을 표현하기위해 assertions
와 preconditions
을 사용한다. 때문에 이 안에 코드의 일부분을 포함시킬 수 있다. assertions
는 실수와 개발 중의 잘못된 가정을 찾는 데 도움을 주며, preconditions
는 제품의 이슈를 발견하는 데 도움을 준다.
에러 처리와는 다르게, assertions
와 preconditions
는 예상되거나 회복 가능한 에러를 사용하지 않는다. assertions
와 preconditions
의 실패는 곧 프로그램 상태가 무효임을 나타내며, 이를 잡을 방법이 없기 때문이다.
assertions
와 preconditions
의 차이는 체크가 되는 시기이다.
assertion : 디버그 빌드에서만.
preconditions : 디버그와 배포 빌드 둘 다.
assert(_:_:file:line:)
함수를 호출함으로써 assertion
을 작성할 수 있다. true
와 false
를 판별할 표현식과 false
일 때 나타낼 메시지를 전달한다. 메시지는 생략 가능하다.
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.
만약 코드가 이미 조건을 체크했다면, assertionsFailure(_:file:line:)
함수로 assertion
이 실패했음을 나타낸다.
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.")
}
코드가 실행되려면 반드시 참이어야 하지만 잠재적으로 실패할 수 있는 조건에 precondition
을 사용한다.
precondition(_:_:file:line:)
함수를 호출하여 precondition
를 사용한다. true
와 false
를 판별할 표현식과 false
일 때 나타낼 메시지를 전달한다. 메시지는 생략 가능하다.
또한 실패가 발생했음을 나타내는 preconditionFailure(_:file:line:)
을 호출할 수도 있다.
unchecked mode(-0unchecked)
에서 컴파일 한다면precondition
은 작동되지 않는다. 컴파일러는 항상 참이라고 가정한다. 하지만fatalError(_:file:line:)
함수는 최적화 세팅과 무관하게 실행을 중단한다.프로토타이핑과 초기 개발 단계에서
fatalError(_:file:line:)
를 사용할 수 있다.
감자합니다 잘 참고하겠습니다