💛

Swift

Swift

데이터 타입

Int: 정수인 숫자를 표현하는 데이터 타입. -2,147,483,648 ~ 2,147,483,647 사이 숫자를 표현합니다.
Float: 부동 소수점 숫자를 표현하는 데이터 타입. 32비트 부동 소수(소수점 이하 6자리까지) 표현 가능합니다.
Double: 부동 소수점 숫자를 표현하는 데이터 타입. 64비트 부동소수(소수점 이하 15자리 이상) 표현 가능합니다.
Bool: 참(true)와 거짓(false) 을 표현할 수 있는 데이터 타입.
String: 문자열을 표현하는 데이터 타입. 텍스트를 표현할 수 있습니다.
Character: 단일 문자를 표현하는 데이터 타입.
Any: 다양한 데이터 타입의 값을 수용할 수 있는 데이터 타입. 반드시 형 변환을 하여 사용해야합니다.
Tuple: 여러 데이터의 묶음. 다양한 데이터 타입을 가진 값들을 묶을 수 있습니다.
var age: Int = 18 // Int 타입 var interestRate: Float = 0.123456 // Float 타입 var interestRate: Double = 0.123456789101234 // Double 타입 var isOpen: Bool = true // Bool 타입 var isLogged: Bool = false var name: String = "David" // String 타입 var characterA: Character = "A" // Character 타입 var anyValue: Any = 1000 // Any 타입 var http404Error: (Int, String) = (404, "Not Found") // Tuple 튜플
Swift
복사

컬렉션(Collection)

Array: 동일한 타입의 요소들을 저장하는 순서가 있는 컬렉션. 특정 요소의 인덱스를 사용하여 접근할 수 있고 수정할 수 있으며 배열의 크기는 동적으로 조절됩니다.
Dictionary: 순서를 정의하지 않고 같은 타입의 key와 같은 타입의 value를 저장하는 컬렉션. key는 고유(unique)해야하므로 중복을 허용하지 않으며, 실물 사전을 찾는 것처럼 순서가 아닌 식별자 기준으로 값을 찾을 때 Dictionary를 사용합니다.
Set: 순서를 정의하지 않고 동일한 타입의 값을 저장하는 컬렉션. 세트 안에 있는 모든 값은 고유(unique)해야하므로 중복을 허용하지 않습니다. 항목의 순서가 중요하지 않거나 항목이 한 번만 표시되도록 해야 하는 경우 사용합니다.
// 자주 사용하는 메서드 // Array var numbers: [Int] = [1, 2, 3] let count: Int = numbers.count // 배열 갯수 확인 : 3 let isEmpty: Bool = numbers.isEmpty // 배열 비었는지 확인 : false numbers[0] // 1 numbers[0...1] // [1, 2] numbers.first // Optional(1) numbers.last // Optional(3) numbers.append(4) // [1, 2, 3, 4] numbers.append(contentsOf: [5, 6, 7]) // [1, 2, 3, 4, 5, 6, 7] numbers.insert(0, at: 0) // [0, 1, 2, 3, 4, 5, 6, 7] numbers.insert(contentsOf: [10, 100], at: 2) // [0, 1, 10, 100, 2, 3, 4, 5, 6, 7] numbers.remove(at: 2) // [0, 1, 100, 2, 3, 4, 5, 6, 7] numbers.removeFirst() // [1, 100, 2, 3, 4, 5, 6, 7] numbers.removeLast() // [1, 100, 2, 3, 4, 5, 6] numbers.popLast() // [1, 100, 2, 3, 4, 5] numbers.removeSubrange(1...3) // [1, 4, 5] numbers.removeAll() // [] // Array - 비교 var array1 = [1, 2, 3] var array2 = [1, 2, 3] var array3 = [1, 2, 3, 4, 5] array1 == array2 //true array1.elementsEqual(array3) //false // Array - 정렬 let unsorted = [1, 5, 3, 8, 6, 10, 14] unsorted.sort() // unsorted 배열을 오름차순 정렬 [1, 3, 5, 6, 8, 10, 14] unsorted.sort(by: >) // unsorted 배열을 내림차순 정렬 [14, 10, 8, 6, 5, 3, 1] let sortedArray = unsorted.sorted() // unsorted 배열을 오름차순 정렬하여 반환 [1, 3, 5, 6, 8, 10, 14] let sortedArray2 = unsorted.sorted(by: >) // unsorted 배열을 내림차순 정렬하여 반환 [14, 10, 8, 6, 5, 3, 1] // Dictionary var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"] airports.keys // ["YYZ", "DUB"] airports.values // ["Toronto Pearson", "Dublin"] airports.keys.sorted() // ["DUB", "YYZ"] airports.values.sorted() // ["Dublin", "Toronto Pearson"] airports["APL"] = "Apple International" // airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin", "APL": "Apple International"] airports["APL"] = nil // key에 매칭된 value 값 초기화 print(airports.count) // airports.count는 2 print(airports.keys) // 딕셔너리 airports에 있는 모든 key들. 딕셔너리 airports에 있는 모든 key들 let newYyz = airports.updateValue("Hello YYZ", forKey: "YYZ") // 해당 key가 있다면 value를 덮어쓰고, 덮어쓰기 전 기존값울 반환 print(newYyz) // 출력값: Optional("Toronto Pearson") print(airports["YYZ"]) // 출력값: Optional("Hello YYZ") let newApl = airports.updateValue("Hello APL", forKey: "APL") // 해당 key가 없다면 그 key에 해당하는 value에 값을 추가하고 nil을 반환 print(newApl) // 출력값: nil print(airports["APL"]) // 출력값: Optional("Hello APL") // Set var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"] favoriteGenres.isEmpty // false favoriteGenres.count // 3 favoriteGenres.contains("Rock") // true favoriteGenres.randomElement() // Hip hop (다른 element가 나올 수 있음) favoriteGenres.remove("Rock") // "Rock" -> 삭제된 요소를 리턴 favoriteGenres // ["Classical", "Hip hop"] favoriteGenres.remove(5) // nil -> 존재하지 않는 요소를 삭제했을 때 에러는 발생하지 않고 nil 리턴 favoriteGenres.removeAll() // [] let oddDigits: Set = [1, 3, 5, 7, 9] let evenDigits: Set = [0, 2, 4, 6, 8] let singleDigitPrimeNumbers: Set = [2, 3, 5, 7] // Set - 합집합 oddDigits.union(evenDigits).sorted() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // Set - 교집합 oddDigits.intersection(evenDigits).sorted() // [] // Set - 차집합 oddDigits.subtracting(singleDigitPrimeNumbers).sorted() // [1, 9] // Set - 대칭 차집합 oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted() // [1, 2, 9]
Swift
복사

프로퍼티 - 저장 프로퍼티 (Stored Property)

let: 변경할 수 없는 상수(Constant) 선언시 사용합니다.
var: 변경할 수 있는 변수(Variable) 선언시 사용합니다.
let name = "철수" name = "영희" // 🚨Error var age = 10 age = 50 // ✅
Swift
복사

프로퍼티 - 연산 프로퍼티 (Computed Property)

그 때 그 때 값을 계산하여 반환하는 프로퍼티
매번 계산하여 값을 알려주기 때문에 var를 사용하여 선언하고 let 사용은 불가능합니다.
매번 계산하기 때문에 값을 저장하지 않습니다.
var x = 10 var y = 20 var sum: Int { // sum이 연산 프로퍼티 get { return x + y } set { sum = x + y } } print(sum) // 출력값: 30 // get, set 중 get만 필요한 경우에는 키워드 생략 가능 var sum1: Int { return x + y } // 아래와 같이 더 축약 가능 var sum2: Int { x + y // return 구문만 있을 경우 return 키워드 생략 가능 }
Swift
복사

프로퍼티 옵저버

didSet: 프로퍼티를 관찰(observe)하면서 새 값이 저장된 직후에 호출되며 이전 프로퍼티의 값을 oldValue로 제공됩니다.
willSet: 프로퍼티를 관찰하면서 값이 새 값이 저장되기 직전에 호출되며 새로운 프로퍼티의 값을 newValue로 제공합니다.
var myProperty: Int = 20{ didSet(oldValue){ print(oldValue) } willSet(newValue){ print(newValue) } } var name: String = "Unknown" { willSet { print("현재 이름 = \(name), 바뀔 이름 = \(newValue)") } didSet { print("현재 이름 = \(name), 바뀌기 전 이름 = \(oldValue)") } } name = "Peter" // willSet이 먼저 실행 // 현재 이름 = Unknown, 바뀔 이름 = Peter // 현재 이름 = Peter, 바뀌기 전 이름 = Unknown
Swift
복사

접근 제한자

다른 소스 파일이나 모듈의 코드에서 코드 일부에 대한 접근을 제한하는 키워드
[제약이 적음] open < public < internal < fileprivate < private [제약이 많음]
private(set)으로 프로퍼티를 외부에서 수정 불가능한 읽기 전용 모드를 만들 수 있습니다.
접근 제한자를 작성하지 않으면 기본적으로 internal로 판단합니다.
상위 요소보다 하위 요소가 더 높은 접근 수준을 가질 수 없습니다.

함수

특정 작업을 수행하는 코드 덩어리
기본적으로 함수 이름, 매개 변수 (Parameter), 리턴 타입 (Return Type) 등을 사용하여 정의하고 네이밍 컨벤션은 카멜 케이스를 사용합니다.
func 함수_이름(아규먼트_레이블: 파라미터_타입) -> 리턴_타입 { // ... 코드 } // 다양한 함수 표현법 func sayHi(friend: String) { print("Hi~~ \(friend)!") } sayHi(friend: "영호") // 출력값: Hi~~ 영호! func sayHi(to friend: String) { print("Hi~! \(friend)!") } sayHi(to: "영호") // 출력값: Hi~! 영호! func sayHi(_ friend: String) -> String { return ("Hi~ :) \(friend)!") } print(sayHi("영호")) // 출력값: Hi~ :) 영호!
Swift
복사

연산자

산술 연산자: 덧셈, 뺄셈, 곱셈, 나눗셈, 나머지 연산한 결과값을 반환하는 연산자
비교 연산자: 비교한 값을 true와 false로 반환하는 연산자
논리 연산자: 논리적으로 비교한 값을 true와 false로 반환하는 연산자
범위 연산자: 범위를 나타내는 연산자
삼항 연산자: question ? answer1 : answer2 형식으로, question의 답이 true면 answer1을 false면 answer2 값을 사용하는 연산자.
// 산술 연산자 var result = 1 + 2 // result는 3 result += 5 // result = result + 5와 같음. 따라서 result는 8 result = 10 - 6 // result는 4 result -= 3 // result = result - 3와 같음. 따라서 result는 1 result = 8 * 2 // result는 16 result = 12 / 5 // result는 2 result = 10 % 3 // result는 10을 3으로 나눈 후 나머지 이므로 1 // 비교 연산자 var result = (1 == 2) // result는 false result = (1 != 2) // result는 true result = (1 > 2) // result는 false result = (1 < 2) // result는 true result = (1 >= 2) // result는 false result = (2 <= 2) // result는 true // 논리 연산자 var allowedEntry = false allowedEntry = !allowedEntry // allowedEntry는 true let enteredDoorCode = true let passedRetinaScan = false let permittedAccess = enteredDoorCode && passedRetinaScan // permittedAccess는 false let enter = allowedEntry || permittedAccess // enter는 true // 범위 연산자 (1...5) // 1, 2, 3, 4, 5 (1..<5) // 1, 2, 3, 4 (3...) // 3, 4, 5, 6, 7 ... // 삼항 연산자 let height = 150 var nickname = (height > 185) ? "Daddy Long Legs" : "TomTom" // nickname은 TomTom
Swift
복사

조건문

if 문: 조건을 확인하는 문법으로 ifelse if 에 작성한 조건이 true인 경우에만 구현부 코드를 실행하는 조건문. 작성한 조건이 모두 false일 경우 else 부분의 코드가 실행됩니다.
switch 문: 가능한 여러 개의 일치하는 case와 값을 비교하며 일치하는 첫 번째 case를 기반으로 구현부 코드 블록을 실행하는 조건문. 모든 케이스가 적용되지 않는 경우 default 에 구현된 코드가 실행하며 특정 케이스에 실행 구문이 없을 경우 break 키워드를 반드시 사용해야 합니다.
// if 문 ar temperature = 17 if temperature <= 13 { print("쌀쌀한 날씨가 지속되겠습니다.") } else if temperature >= 22 { print("해가 떠오르는 낮부터는 더위 예상됩니다.") } else { print("밤낮으로 선선한 날씨가 예상됩니다.") } // 출력값: 밤낮으로 선선한 날씨가 예상됩니다. // switch 문 let cookieCount = 62 let message: String switch cookieCount { case 0: message = "🍪 없음 🙅‍♂️" case 1..<5: message = "🍪 아주 조금 있음" case 5..<12: message = "🍪 조금 있음" case 12..<100: message = "🍪 꽤 있음 🍪" case 100..<1000: message = "🍪🍪 많음 🍪🍪" default: message = "🍪🍪🍪엄청 많음🍪🍪🍪" } print(message) // 출력값: "🍪 꽤 있음 🍪"
Swift
복사

반복문

For-In문: 순회할 수 있는 타입(배열, 딕셔너리 등)을 순회하거나 특정 횟수만큼 로직을 반복할 때 주로 사용하는 반복문
While문: 특정 조건이 만족하는 동안 내부로직을 계속해서 실행하는 반복문
// For-In 문 let students = ["Tom": 2, "Harry": 4, "Sarah": 1] for (name, grade) in students { print("\(name)\(grade) 학년이야") } // 출력값: // Tom 은 2 학년이야 // Harry 은 4 학년이야 // Sarah 은 1 학년이야 // While 문 let lastName : [String] = ["송", "김", "박", "정" ] var index : Int = 0 while index < 4 { print("옆집 \(lastName[index]) 씨네 \(index)번째 결혼식") index += 1 } // 출력값: // 옆집 송 씨네 0번째 결혼식 // 옆집 김 씨네 1번째 결혼식 // 옆집 박 씨네 2번째 결혼식 // 옆집 정 씨네 3번째 결혼식
Swift
복사

옵셔널

값이 nil일 가능성이 있는 상황에서 옵셔널(Optional)을 사용합니다.
옵셔널 타입끼리의 연산은 불가능합니다.
옵셔널 바인딩: if let/varguard let/var를 사용하여 변수의 값이 nil인지 아닌지 검사한 후, nil이 아닌경우(값이 존재하는 경우) 그 값을 다른 변수에 대입시켜 바인딩을 할 수 있습니다.
강제 언래핑: !로 옵셔널 타입의 값을 강제로 언래핑하는 것보다 옵셔널 바인딩을 사용하는 것이 훨씬 안전합니다.
??로 값이 nil일 경우를 위해 기본값을 설정할 수 있습니다(nil-coalescing)
옵셔널 체이닝: 옵셔널을 연쇄적으로 사용하는 것을 의미하며, . 을 통해 내부 프로퍼티나 메서드에 연속적으로 접근할 때 사용합니다.
let optionalString1: String? = "Hello, " let optionalString2: String? = "world!" // 옵셔널 String 값들을 연결하려는 시도 let result = optionalString1 + optionalString2 // 🚨Error // 강제 언래핑 let number = Int("42")! // ✅ number는 42 let address: String? = nil print(address!) // 🚨 Error // 옵셔널 바인딩 - if let/var let boyName : String? let girlName : String? boyName = "하늘" girlName = "나연" if let boy = boyName, var girl = girlName { // boy, girl 변수는 if let {} 범위 안에서 사용 가능 girl = "수지" // var로 선언한 경우 범위 안에서 값 변경 가능 } // 옵셔널 바인딩 - guard let/var let x : Int? = 10 let y : Int? = nil func optionalBinding() { guard let x = x else { return } print(x) guard let y = y else { return } // y는 nil 이므로 여기서 return print(y) // 위에서 return 하였기 때문에 이 코드 라인은 실행되지 않음 } optionalBinding() // 출력값: 10 // 기본값 설정 var optNumber: Int? = 3 let number = optNumber ?? 5 // number는 3이고 Int? 타입이 아닌 Int 타입 // 옵셔널 체이닝 struct Person { var name: String var address: Address } struct Address { var city: String var street: String var detail: String } let samAddress = Address(city: "서울", street: "신논현로", detail: "100") let sam: Person? = Person(name: "Sam", address: samAddress) print(sam.address.city) // 🚨 Error. print(sam?.address.city) // ✅ 출력값: 서울
Swift
복사

클로저

이름없는 함수 즉, 코드 블록을 의미
클로저는 일반적으로 기능을 저장하기 위해 사용합니다.
클래스와 마찬가지로 참조 타입(Reference type)입니다.
// 클로저 { (parameters) -> return type in // 구현 코드 } // 클로저 예시 - 1 let closure1 = { (param: String) -> String in return param + "!" } print(closure1("스티브")) // 출력값: 스티브! // 클로저 예시 - 2 func closureCaseFunction(a: Int, b: Int, closure: (Int) -> Void) { let c = a + b closure(c) } closureCaseFunction(a: 1, b: 2) {(number) in print("result : \(number)") // result : 3 } // 클로저 예시 - 3 func performClosure(param: (String) -> Int) { param("Swift") } performClosure(param: { str in return str.count }) performClosure(param: { $0.count // 코드가 한 줄인 경우 return 생략 가능 }) // 탈출 클로저
Swift
복사

탈출 클로저

@escaping 키워드를 붙인 클로저로 함수의 실행이 종료된 후에 함수 밖에서 실행시키는 코드 블록.
탈출 클로저를 활용하여 기존에 있던 함수 범위 내부의 자원들을 활용해서 비동기적 작업을 할 수 있습니다.
@escaping 를 사용하는 클로저에서 self의 요소를 사용할 경우, self를 명시적으로 언급해야 합니다.
class Tutor { var name = "iOS튜터" func asyncEscaping(closure: @escaping (String) -> ()) { DispatchQueue.main.asyncAfter(deadline: .now() + 3) { closure(self.name) } } } let tutor = Tutor() tutor.asyncEscaping { str in print("name : \(str)") // 출력값: name : iOS 튜터 }
Swift
복사

고차함수

map: 컬렉션 내부의 기존 데이터를 변형(transform)하여 새로운 컬렉션을 생성하는 함수
filter: 기존 컨테이너의 요소 중 조건에 만족하는 값에 대한 새로운 컨테이너를 만들어 반환하는 함수
reduce: 기존 컨테이너에서 내부의 값들을 결합하여 새로운 컨테이너를 만들어 반환하는 함수
// map let stringArray = ["1", "2", "3", "4", "5"] numberArray = stringArray.map { if let changeToInt = Int($0) { return changeToInt } return 0 } // numberArray는 [1,2,3,4,5] // filter let numbers1 = [1, 2, 3, 4, 5, 6, 7, 8, 9] let evenNumbers2 = numbers1.filter { $0 % 2 == 0 } // evenNumbers2는 [2, 4, 6, 8] // reduce let numbers3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] let sum1 = numbers3.reduce(0, +) // sum1은 55
Swift
복사

클래스

프로퍼티에 값을 저장하거나 메서드를 통해 기능을 제공하 이걸 하나로 프로퍼티나 메서드를 하나로 캡슐화하여 값을 저장하거나 기능을 제공하는 사용자 정의 타입.
이니셜라이저(Initializer)를 통해 초기값을 설정할 수 있습니다.
다른 클래스에 상속(inheritance)을 할 수도 받을 수도 있습니다.
클래스는 참조 타입(Reference Type)으로 변수나 상수에 할당될 때에는 값을 복사하는 것이 아니라 참조(주소)가 복사되어 같은 인스턴스를 가리키게 됩니다. 따라서 동일한 인스턴스를 공유하면 한 쪽에서 값을 변경하면 다른 쪽에서도 영향을 받게 됩니다.
override: 부모 클래스에서 상속받은 메서드, 속성 또는 서브스크립트를 자식 클래스에서 재정의할 때 사용하는 키워드
super: 자식 클래스에서 부모 클래스의 메서드, 속성 또는 초기화 메서드를 호출할 때 사용하는 키워드
final: 클래스, 메서드, 속성 또는 서브스크립트를 표시하여 상속이 불가능하도록 만드는 키워드
class Person { var name: String init(name: String) { // 이니셜라이저를 이용하여 초기값 설정 self.name = name } func introduce() { print("my name is \(name)") } } var person1 = Person(name: "Alice") // Person 클래스를 인스턴스화 시킨 것 var person2 = person1 // 참조 복사 person2.name = "Bob" person1.introduce() // my name is Bob person2.introduce() // my name is Bob // 상속 // Student 클래스가 Person 클래스를 class Student: Person { var studentID: Int init(name: String, age: Int, studentID: Int) { self.studentID = studentID super.init(name: name, age: age) } func study() { print("\(name) is studying.") } } let john = Student(name: "John", age: 20, studentID: 123) john.introduce() // 출력: my name is John john.study() // 출력: John is studying. // override, super class Animal { func makeSound() { print("Some generic sound") } } class Dog: Animal { override func makeSound() { super.makeSound() // 부모 클래스 Animal의 메서드 호출 print("Bark!") } } let dog = Dog() dog.makeSound() // 출력값: // Some generic sound // Bark! // final final class Vehicle { final var wheels: Int = 0 final func makeSound() { print("Some generic sound") } }
Swift
복사

구조체

클래스와 마찬가지로 프로퍼티에 값을 저장하거나 메서드를 통해 기능을 제공하고 이걸 하나로 캡슐화할 수 있는 사용자 정의 타입.
생성자(initializer)를 정의하지 않으면 구조체가 자동으로 생성자(Memberwise Initializer.)를 제공합니다.
값 타입(Value Type)으로 변수나 상수에 할당될 때 값의 복사본이 생성되는 타입입니다.
클래스와 달리 상속을 할 수 없습니다.
// 값 타입인 구조체 예시 struct Point { var x: Int var y: Int } var point1 = Point(x: 5, y: 10) var point2 = point1 // 값 복사 point2.x = 15 print(point1) // 출력: Point(x: 5, y: 10) print(point2) // 출력: Point(x: 15, y: 10)
Swift
복사

열거형

관련된 값으로 이뤄진 그룹을 같은 타입으로 선언해 타입 안전성(type-safety)을 보장하는 방법
구조체와 같이 값 타입(Value Type)입니다.
// 자주 사용하는 활용 enum CompassDirection: CaseIterable { // CaseIterable 프로토콜 채택시 allCases 사용 가능 case north(latitude: String, longitude: String) // 연관 값 case south case east case west } var direction: CompassDirection = .south var anotherDirection = direction // 값 복사 direction = .east // 값을 변경해도 anotherDirection에는 영향이 없음 print(direction) // 출력: east print(anotherDirection) // 출력: south let north = CompassDirection.north(latitude: "123456", longitude: "789123") func processTrade(direction: CompassDirection) { switch direction { case .north(let latitude, let longitude): print("북쪽 방향의 위도 \(latitude) 경도 \(longitude).") case .south print("남쪽 입니다.") case .east: print("동쪽입니다") case .west: print("서쪽입니다") } } let allDirections = CompassDirection.allCases.count print("\(allDirections)개의 방향이 있습니다.") // 출력값: 4개의 방향이 있습니다.
Swift
복사

프로토콜

특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항 등을 정의 해놓은 규약 혹은 약속
class, structure, enum이 프로토콜을 ‘채택’하고 모든 요구사항을 충족하면 프로토콜을 준수했다고 합니다.
프로토콜은 설계된 조건만 정의를 하고 제시를 할 뿐 스스로 기능을 구현하지 않습니다.
// 예시 protocol Student { var studentId: Int { get set } var name: String { get } func printInfo() -> String } struct UnderGraduateStudent: Student { var studentId: Int var name: String var major: String func printInfo() -> String { return "\(name), whose student id is \(studentId), is major in \(major)" } } struct GraduateStudent: Student { var studentId: Int var name: String var degree: String var labNumber: Int func printInfo() -> String { return "\(name), member of lab no.\(labNumber), has a \(degree) degree" } } // 프로토콜은 타입으로서도 사용가능 let underGraduate: Student = UnderGraduateStudent(studentId: 1, name: "홍길동", major: "computer") let graduate: Student = GraduateStudent(studentId: 2, name: "김철수", degree: "master", labNumber: 104) let studentArray: [Student] = [underGraduate, graduate]
Swift
복사

타입 캐스팅

인스턴스의 타입을 확인 하거나, 해당 인스턴스를 슈퍼 클래스(부모 클래스)나 하위 클래스(자식 클래스)로 취급하는 방법
is: is 연산자는 타입을 체크하는 연산자로, 비교 결과를 bool 타입을 반환합니다.(타입 체킹)
as: 캐스팅하려는 타입이 같은 타입 이거나 수퍼클래스 타입이라는 것을 알 때 사용하며 컴파일 단계에서 캐스팅이 실행됩니다. 캐스팅에 실패할 경우 에러가 발생하여 항상 타입 캐스팅이 성공할 경우에만 사용할 수 있습니다.
as?: 성공하면 옵셔널 타입의 인스턴스를 반환하고 실패하면 nil 을 반환하며 런타임에 캐스팅이 실행됩니다. 실패할 가능성이 있으면 as?를 사용하는 것이 좋습니다.
as1: 캐스팅에 성공한 경우 인스턴스를 반환하며 런타임에 특정 타입으로 강제 캐스팅합니다. 강제 타입 캐스팅에 실패할 경우 런타임 에러가 발생할 수 있습니다.
// is let char: Character = "A" print(char is Character) // 출력값: true print(char is String) // 출력값: false // 타입 캐스팅 class Person { var id = 0 var name = "name" var email = "hgk@gmail.com" } class Worker: Person { var salary = 300 } class Programmer: Worker { var lang = "Swift" } // 업캐스팅 - as let person1 = Person() let worker1 = Worker() let programmer1 = Programmer() let personList = [person1, worker1, programmer1] // 타입을 선언하지 않았지만 Person 타입으로 인식 -> 즉 업캐스팅이 되었음 personList[1].name //personList[1].salary // Person 타입으로 보고 있기 때문에 salary에 접근하지 못함 let worker2 = Worker() worker2.salary let workerPerson = worker2 as Person //workerPerson.salary // Person 타입으로 보고 있기 때문에 salary에 접근하지 못함 // 다운캐스팅 - as? / as! // as? let pro = programmer1 as? Programmer // 타입 변환이 될 수도 있고 안될 수도 있기 때문에 옵셔널을 리턴 if let person2 = programmer1 as? Programmer { person2.lang } if let person3 = worker1 as? Programmer { person3.lang } // as! let pro2 = worker2 as! Programmer // 🚨Error : 타입 변환 실패시 오류
Swift
복사

제네릭(Generic)

다양한 타입에서 작동하도록 일반화된 코드를 작성할 수 있게 해주는 타입
실제 타입 이름을 써주는 대신에 placeholder를 사용합니다. [ eg: T, V, U ]
placeholder의 실제 타입은 함수가 호출되는 순간 결정됩니다.
struct Queue<T> { private var queue: [T] = [] public var count: Int { return queue.count } public var isEmpty: Bool { return queue.isEmpty } public mutating func enqueue(_ element: T) { queue.append(element) } public mutating func dequeue() -> T? { return isEmpty ? nil : queue.removeFirst() } } var queue = Queue<Int>() queue.enqueue(10) queue.enqueue(20) queue.dequeue() // 10
Swift
복사

Extension

structure, class, enum, protocol 타입에 새로운 기능을 추가할 수 있는 문법으로, 해당 타입의 소스코드에 접근하지 않고 기능을 확장 가능할 수 있습니다.
Extension으로 구현 가능한 것: 새로운 계산된 속성(Computed Property) 추가, 새로운 인스턴스/타입 메서드 추가, 새로운 초기화(Initializer) 추가, 프로토콜 채택(Protocol Conformance), 서브스크립트 추가(Subscripting), 중첩 타입(Nested Type) 추가
Extension으로 구현 불가능한 것: 저장 프로퍼티(Stored Property) 추가, 기존 기능의 재정의(Override), 초기화 메서드(Initializer)의 재정의, 기존 타입의 저장된 프로퍼티에 기본값 설정
// Extension 예시 - 1 extension String { var length: Int { return self.count } } let str = "Hello" print(str.length) // 출력: 5 // Extension 예시 - 2 extension Int { func squared() -> Int { return self * self } } let number = 3 print(number.squared()) // 출력: 9 // Extension 예시 - 3 extension Double { init(fromString str: String) { self = Double(str) ?? 0.0 } } let value = Double(fromString: "3.14") print(value) // 출력: 3.14 // Extension 예시 - 4 protocol Printable { func printDescription() } struct MyStruct {} extension MyStruct: Printable { func printDescription() { print("Printing description of MyStruct") } } let myInstance = MyStruct() myInstance.printDescription() // 출력: Printing description of MyStruct
Swift
복사

ARC(Automatic Reference Counting)

Swift의 메모리 관리 기법 중 하나로, 객체나 인스턴스가 참조되는 횟수를 추적하여 메모리에서 해제할 시점을 결정합니다. 객체가 생성될 때마다 참조 횟수가 1 증가하고, 해당 객체를 참조하는 다른 객체나 변수가 없어지거나 더 이상 사용되지 않을 때 참조 횟수가 1 감소합니다. 참조 횟수가 0이 되면 해당 객체는 메모리에서 해제됩니다.
ARC의 작동 방식
1.
객체 생성: 객체가 생성되면 참조 횟수가 1 증가합니다.
2.
객체 참조: 객체를 다른 변수나 상수에 할당하면 해당 객체의 참조 횟수가 1 증가합니다.
3.
참조 해제: 객체의 참조가 없어지면(참조하는 변수나 상수가 없거나 nil이 할당되면) 참조 횟수가 1 감소합니다.
4.
Zeroing Weak References: 약한 참조(Weak Reference)는 객체의 참조 횟수를 증가시키지 않고 추적합니다. 객체가 해제되면 약한 참조는 자동으로 nil로 설정됩니다.
참조 종류
1.
강한 참조(strong):
2.
약한 참조(weak): 옵셔널로 선언되는 참조. 참조하는 객체를 강제로 유지하지 않고, 참조 대상이 메모리에서 해제되면 자동으로 nil로 설정됩니다. 두 객체가 서로를 강하게 참조하는 경우, 순환 참조로 인해 메모리 누수가 발생할 수 있는데 한쪽을 weak로 선언하여 순환 참조 문제를 해결할 수 있습니다.
3.
비소유 참조(unowned): 옵셔널이 아닌 비소유 참조. 항상 값이 있다고 가정하며 참조하는 객체가 해제되면 런타임 에러가 발생할 수 있습니다.
// 레퍼런스 카운팅 예시 class Person { let name: String init(name: String) { self.name = name print("\(name) is being initialized") } deinit { print("\(name) is being deinitialized") } } var reference1: Person? var reference2: Person? var reference3: Person? reference1 = Person(name: "John Appleseed") // RC: 1️⃣ // Prints "John Appleseed is being initialized" reference2 = reference1 // RC: 2️⃣ reference3 = reference1 // RC: 3️⃣ reference1 = nil // RC: 2️⃣ reference2 = nil // RC: 1️⃣ reference3 = nil // RC: 0️⃣ // Prints "John Appleseed is being deinitialized" // 약한 참조(Weak Reference)를 사용한 해결 방법 class Man { var name: String weak var girlfriend: Woman? init(name: String) { self.name = name } deinit { print("Man Deinit!") } } class Woman { var name: String var boyfriend: Man? init(name: String) { self.name = name } deinit { print("Woman Deinit!") } } var chelosu: Man? = .init(name: "철수") var yeonghee: Woman? = .init(name: "영희") chelosu?.girlfriend = yeonghee yeonghee?.boyfriend = chelosu chelosu = nil yeonghee = nil chelosu?.girlfriend // nil
Swift
복사