변수(variables)와 상수(constants)
// identifier: name, age
// type: String, Int
// value: "Sally", 17
// var, let은...?
var age: Int = 17 // 변수
let name: String = "Sally" // 상수
age = 144 // now age is 144
name = "Compass" // Cannot assign to value: 'name' is a 'let' constant
변수와 상수는 identifier, 그리고 특정 타입의 value로 구성된다.
차이는 있다. 값을 할당한 이후에 상수의 값은 수정할 수 없고, 변수의 값은 수정할 수 있다.
변수와 상수의 이름(identifier)을 설정할 때는 몇 가지 규칙이 있다.
불가: 공백, 수학 기호, 화살표, 특정 유니코드, 박스 문자(⏌,┤), 맨 앞에 오는 숫자, Swift keywords(var, let, func, ...)
변수와 상수 정의하기
변수와 상수는 사용하기 전에, 반드시 정의해야 한다.
Swift keywords를 참고하면, 변수는 var, 상수는 let이라는 키워드를 앞에 붙여야 한다.
기본적으로 상수로 선언하는 것을 권장한다.
값을 바꿔야 하는 명확한 이유가 있다면 변수로 선언한다.
안전한 프로그램 실행을 위해 중요하며, 값이 바뀌지 않는 변수가 있다면 Xcode가 컴파일 단계에서 경고한다.
정의(definition)와 선언(declaration)의 차이
가장 큰 차이는 `메모리를 할당하는가?` 이다.
선언은 이름을 밝히는 것이다. 메모리를 할당하지 않는다.
정의는 선언된 변수/상수를 설명한다. 값이나 함수에 대한 내용을 포함한다. 메모리를 할당한다.
그러나 Swift는 둘을 구분하지 않는다.
더 정확히는 선언과 동시에 정의가 되기 때문에, Swift는 선언(+정의)하다로 용어를 통일한다.
따라서 아래에서도 선언하다 만을 사용하겠다. (*타입을 정의하다 는 OK)
참고: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/declarations/
변수와 상수는 한 줄에 하나씩 선언해도 되고, 여러 개를 선언해도 된다.
// Constants
let freezingTemperatureOfWaterCelsius = 0
let speedOfLightKmSec = 300000
// Variables
var currentTemperature = 22
var currentSpeed = 55
// Constants
let freezingTemperatureOfWaterCelsius = 0, speedOfLightKmSec = 300000
// Variables
var currentTemperature = 22, currentSpeed = 55
타입(Type)
타입 안정성 (Type Safety)
스위프트는 type-safe 언어이기 때문에, 변수/상수에 값을 저장하기 전에, 타입을 정의해야 한다.
정의된 타입과 다른 타입의 값을 할당하려고 하면, 컴파일 단계에서 에러를 찾아낸다.
vat integerVar = 10
integerVar = "My String" // Cannot assign value of type 'String' to type 'Int'
타입 추론 (Type inference)
컴파일러는 초기값을 근거로, 변수/상수의 타입을 추론할 수 있다.
이는 우리가 변수/상수를 선언할 때 타입을 생략할 수 있게 해준다.
var a = 1 // Int
var b = 1.0 // Double
var c = 3.14 // Double
var d = "h" // String
var e = "hello" // String
var f = true // Boolean
// Float, Character로는 추론하지 않는다.
Swift는 타입 추론과 타입을 명시하는 것 중에서 하나만 선택할 것을 권장한다.
초기값이 있으면 타입 추론을, 초기값이 없을 때만 타입을 명시하는 것이 좋다.
var leastNumOfHamburger = 3 // 초기값 있으면 타입 추론 // Int
var randomVal: Int // 초기값 없으면 타입 명시
숫자형 타입(Numeric types)
정수형 타입(integer types)

정수형 타입은 signed(+, -, 0)와 unsigned(+, 0)로 나뉜다.
signed 타입은 부호를 표시하기 위해 하나의 비트를 사용하기 때문에, unsigned 타입보다 표시 가능 범위가 좁다.
기본적으로 Int, 혹은 UInt 타입을 사용하는 것을 권장한다.
범위 확인은 다음과 같이 가능하다.
UInt8.max // 255
Int8.max // 127
Int8.min // -128
정수형 타입은 10진수, 2진수, 8진수, 16진수 형태로 표현할 수 있다.
변수/상수의 값에 적당한 prefix를 붙여주면, 컴파일러가 추론 가능하다.
var a = 95 // 10진수(decimal)
var b = 0b1011111 // 2진수(binary): 0b
var c = 0o137 // 8진수(octal): 0o
var d = 0x5f // 16진수(hexadecimal): 0x
print(a, b, c, d) // 95, 95, 95, 95
가독성을 위해 정수형 값에 언더스코어(_)를 사용하는 것이 허용된다.
컴파일러는 이 언더스코어를 무시한다.
let speedOfLightKmSec = 300_000 // 300000
정수형 타입에는 isMultipe(of:) 메서드가 있다.
해당 변수의 값이 of의 인자로 나누어 떨어지는지 확인할 수 있다.
가독성 측면에서 권장한다.
let number = 4
// 직접 나누기
if number % 2 == 0 {
print("Even")
} else {
print("Odd")
}
// 메서드 사용하기
if number.isMultiple(of: 2) {
print("Even")
} else {
print("Odd")
}
소숫점 타입(Floating-point, Double)
Swift의 소숫점 타입은 Float 그리고 Double이 있다.
Float: 32-bit 길이의 값을 표현한다. (Float16, Float32, Float64, Float80이 기본적으로 지원된다.)
Double: 64-bit 길이의 값을 표현한다.
Double을 사용하는 것을 권장한다. (Float은 특별한 이유가 있는 경우에 사용한다.)
상술하였듯이, Swift의 컴파일러는 기본적으로 소숫점 타입을 Double로 추론한다.
십진법 기준 Double은 최소 15자리수를 표현할 수 있으며, Float은 적게는 6자리수까지 표현 가능하다.
따라서 값을 표현하는 정확도에서 차이가 발생한다.
let f: Float = 0.111_111_166 + 0.222_222_222 // 0.3333334
let d: Double = 0.111_111_166 + 0.222_222_222 // 0.333333388
정수 타입과 소숫점 타입의 연산
정수 타입과 소숫점 타입의 연산은 불가능하다.
다시 말해, Swift는 정수와 소숫점 타입을 구분한다.
var a: Int = 3
var b: Double = 0.14
car c = a + b // operator '+' cannot be applied to operands of type Int and Double
따라서 하나의 타입으로 통일해 주어야 하는데, 보통은 정확도가 높은(더 많은 메모리를 사용하는) 타입으로 맞춰준다.
아래와 같이 타입을 바꿔주는 Double()과 같은 함수를, convenience initializer라고 부른다.
var a: Int = 3
var b: Double = 0.14
var c = Double(a) + b
불리언 타입(Boolean type)
불리언 타입의 값은 true, false 두 가지가 있다.
불리언 타입은 일반적으로 조건문(conditional statement)에 사용된다. (if, while, guard, ...)
let isSwiftCool = true
var isItRaining = false
if isSwiftCool {
print("YEA, I cannot wait to learn it")
}
if isItRaining {
print("Get a rain coat")
}
// 출력: "YEA, I cannot wait to learn it"
불리언 값의 부정문(true <-> false)을 표현하는 두 가지 방법이 있다.
isItRaining = !isItRaining // 값을 알고 있는 경우 사용한다.
isItRaining.toggle() // 값을 모를 경우 사용한다. 가독성에도 좋다고 한다. 가독성은 잘 모르겠다...
문자열 타입(String type)
문자열은 문자의 집합(collection of characters)으로 정의된다.
문자열은 아래와 같이 표현한다.
// 한 줄로 선언
var stringOne = "Hello"
// 여러 줄로 선언, 여러 줄 안에 한 줄 quote를 사용할 수도 있음.
var multiLine = """
This is a multiline string literal.
Jon says, "multiline string literals are cool"
"""
문자열은 순서를 갖는다. (ordered collection)
따라서 다음과 같은 방법으로 문자열에 포함된 문자를 순서대로 출력할 수 있다.
var stringOne = "Hello"
for char in stringOne {
print(char)
}
stringOne.map {
print($0)
}
// H
// e
// l
// l
// o
서로 다른 문자열을 합칠 수도 있다.
var stringA = "Apple"
let stringB = "Pen"
let string = "PineApplePen"
var stringC = stringA + stringB // stringC now is "ApplePen"
stringA += string // stringA now is "ApplePineApplePen"
변수를 문자열 안에 포함하는 문자열 보간법(String Interpolation)도 가능하다.
var stringA = "Jon"
var stringB = "Hello \(stringA)" // Hello John
Swift 5.1부터는 raw string을 생성할 수 있게 됐다.
문자열 안에서 double quote를 표현하고 싶으면 다음 방법을 사용하면 된다.
// 기존 double quote 표현 방식
let str = "The main character said \"hello\""
// raw string을 활용한 방식
let str1 = #"The main character said "hello""#
// raw string을 활용한 문자열 보간법
let ans = 42
var str2 = #"The answer is \#(ans)"#
문자열 메서드
// 소문자 / 대문자로 변환, lowercased() / uppercased()
var stringOne = "hElLo"
print("Lowercase String: \(stringOne.lowercased())") // hello
print("Uppercase String: \(stringOne.uppercased())") // HELLO
// 빈 문자열인지 확인, isEmpty() -> Bool
let stringOne = "You are the one"
let stringTwo = ""
print(stringOne.isEmpty) // false
print(stringTwo.isEmpty) // true
// 문자열 치환, replacingOccurrances(of:)
var stringOne = "one,to,three,four"
var stringTwo = stringOne.replacingOccurrences(of: "to", with: "two")
print(stringTwo) // one, two, three, four
부분 문자열(substring)
문자열 중에 일부를 추출해서 부분 문자열로 만들 수 있다.
주의할 점은 부분 문자열은 Substring의 인스턴스이다. String과는 다른 타입이다.
또한 부분 문자열은 임시적으로 사용하는 타입이다.
원본 문자열의 메모리를 공유하고 있기 때문에, 원본 문자열이 바뀌면 부분 문자열도 바뀐다.
따라서 값을 제대로 저장하려면 String으로 변환해야 한다.
다음과 같은 방법으로 부분 문자열을 만들 수 있다.
var path = "/one/two/three/four"
// 시작 인덱스와 끝 인덱스를 만든다, 이때 각각 변수는 String.index 타입이다. 정수형과는 다르다.
let startIndex = path.index(path.startIndex, offsetBy: 4)
let endIndex = path.index(path.startIndex, offsetBy: 14)
// 만든 인덱스로 subscripting 한다
let sPath = path[startIndex ..< endIndex] // Substring: "/two/three"
let newStr = String(sPath) // String: "/two/three"
// 한 쪽 범위가 열린 subscripting도 가능하다
path[..<startIndex] // String.SubSequence: "/one"
path[endIndex...] // String.SubSequence: "/four"
// 문자열(String)의 시작과 끝에 있는 문자(Character)를 추출할 수 있다. Optional 임을 유의하자.
path.first // Optional<Character>: "/"
path.last // Optional<Character>: "r"
// count 프로퍼티
path.count // 19
String.SubSequence는 SubString의 typealias이다.
즉 같은 말이다.
일반적으로는 SubString을 사용하는 듯하다.
참고
<Mastering Swift 5.3>, Jon Hoffman, 6th Edition
Chapter 3: Learning about Variables, Constants, Strings, and Operators
'Dev > Swift' 카테고리의 다른 글
| [Swift] 타입(Type)의 종류 (0) | 2024.02.04 |
|---|---|
| [Swift] class와 struct의 차이 (메모리를 중심으로) (2) | 2024.01.25 |
| Swift 문법 (3) - Tuple, Enumeration, Operators (1) | 2024.01.14 |
| Swift 문법 (1) - Swift 문법 특징 (0) | 2024.01.11 |
| Swift 문법 (0) - Swift란? (0) | 2024.01.11 |