├── .gitignore ├── README.md ├── _config.yml ├── contents ├── 00_introduction │ ├── 00_introduction.pdf │ ├── README.md │ └── console_log │ │ ├── README.md │ │ └── console_log.swift ├── 01_let_var │ ├── README.md │ └── let_var.swift ├── 02_data_types │ ├── README.md │ ├── any_anyobject_nil.swift │ └── data_types.swift ├── 03_collection_types │ ├── README.md │ └── collection_types.swift ├── 04_function │ ├── README.md │ └── function.swift ├── 05_conditional │ ├── README.md │ └── conditional.swift ├── 06_loop │ ├── README.md │ └── loop.swift ├── 07_optional │ ├── README.md │ ├── optional.pdf │ ├── optional.swift │ ├── optional_unwrapping.pdf │ └── optional_unwrapping.swift ├── 08_struct │ ├── README.md │ └── struct.swift ├── 09_class │ ├── README.md │ └── class.swift ├── 10_enum │ ├── README.md │ └── enum.swift ├── 11_value_reference │ ├── README.md │ ├── value_reference.pdf │ └── value_reference.swift ├── 12_closure │ ├── README.md │ └── closure.swift ├── 13_property │ ├── README.md │ └── property.swift ├── 14_inheritance │ ├── README.md │ └── inheritance.swift ├── 15_init_deinit │ ├── README.md │ └── init_deinit.swift ├── 16_optional_chaining │ ├── README.md │ └── optional_chaining.swift ├── 17_type_casting │ ├── README.md │ └── type_casting.swift ├── 18_assert_guard │ ├── README.md │ └── assert_guard.swift ├── 19_protocol │ ├── README.md │ └── protocol.swift ├── 20_extension │ ├── README.md │ └── extension.swift ├── 21_error_handling │ ├── README.md │ └── error_handling.swift ├── 22_higher_order_function │ ├── README.md │ └── higher_order_function.swift └── 23_more │ └── README.md ├── images └── yagom.png └── index.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/macos,linux,xcode 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### macOS ### 20 | *.DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must end with two \r 25 | Icon 26 | 27 | 28 | # Thumbnails 29 | ._* 30 | 31 | # Files that might appear in the root of a volume 32 | .DocumentRevisions-V100 33 | .fseventsd 34 | .Spotlight-V100 35 | .TemporaryItems 36 | .Trashes 37 | .VolumeIcon.icns 38 | .com.apple.timemachine.donotpresent 39 | 40 | # Directories potentially created on remote AFP share 41 | .AppleDB 42 | .AppleDesktop 43 | Network Trash Folder 44 | Temporary Items 45 | .apdisk 46 | 47 | ### Xcode ### 48 | # Xcode 49 | # 50 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 51 | 52 | ## Build generated 53 | build/ 54 | DerivedData/ 55 | 56 | ## Various settings 57 | *.pbxuser 58 | !default.pbxuser 59 | *.mode1v3 60 | !default.mode1v3 61 | *.mode2v3 62 | !default.mode2v3 63 | *.perspectivev3 64 | !default.perspectivev3 65 | xcuserdata/ 66 | 67 | ## Other 68 | *.moved-aside 69 | *.xccheckout 70 | *.xcscmblueprint 71 | 72 | ### Xcode Patch ### 73 | *.xcodeproj/* 74 | !*.xcodeproj/project.pbxproj 75 | !*.xcodeproj/xcshareddata/ 76 | !*.xcworkspace/contents.xcworkspacedata 77 | /*.gcno 78 | 79 | # End of https://www.gitignore.io/api/macos,linux,xcode 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift 기본문법 2 | 3 | ## 수강대상 4 | * 프로그래밍에 대한 기초지식이 있는 컴퓨터관련 전공생 5 | * 다른 언어를 사용해 본 프로그래머 6 | * (객체지향) 프로그래밍 유경험자 7 | 8 | ## 강의개요 9 | 기존의 프로그래머 또는 프로그래밍 개념이 있는 사람이 빠르게 스위프트 문법을 익힐 수 있도록 스위프트의 기초적인 핵심문법을 제공 10 | 11 | ### 사전숙지사항 12 | 스위프트는 문법표현의 다양성이 매우 높은 언어입니다. 그래서 스위프트 문법의 모든 형태를 알기는 꽤 오랜 시간이 걸립니다. 그렇지만 최소한의 핵심 문법을 통해 무리없이 스위프트 문법을 익힐 수 있도록 간단한 예제와 함께 설명합니다. 13 | 스위프트 **문법의 모든 내용을 포함하지는 않으며**, 깊은 내용보다는 **핵심적인 내용**만을 전달합니다. 14 | 먼저 핵심적인 기초문법을 익힌 후 Apple의 [Swift Programming Language Guide](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html), [Swift Programming Language Guide - iBooks](https://itunes.apple.com/kr/book/swift-programming-language/id881256329?mt=11) 또는 [스위프트 관련 서적](http://book.naver.com/search/search.nhn?sm=sta_hty.book&sug=&where=nexearch&query=스위프트+프로그래밍)을 참고하면 좋습니다. 15 | 16 | 해당 강의는 **Swift 5.1 버전이 기준**입니다. 17 | 다른 버전의 문법은 상이할 수 있으니 [Swift 가이드 문서 변경 내역](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html)을 참고하면 좋습니다. 18 | 19 | 또, **스위프트의 [API 가이드라인](https://swift.org/documentation/api-design-guidelines/)** 에 따라 이름을 짓는 것이 좋습니다. 20 | 21 | **스위프트 언어 자체의 문법과 활용만을 다룹니다.** iOS / macOS 등 애플리케이션 제작을 위한 프레임워크 관련한 내용은 다루지 않습니다. 22 | 23 | __강의 페이지 링크__ 24 | 25 | * [야곰닷넷 - yagom.net](https://yagom.net/courses/swift-basic/) 26 | * [Github Page](https://yagom.github.io/swift_basic) 27 | * [구름EDU](https://edu.goorm.io/lecture/1141/%EC%95%BC%EA%B3%B0%EC%9D%98-%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D) 28 | * [인프런](https://www.inflearn.com/course/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95/) 29 | 30 | ## 지식공유자 31 | 32 | ![yagom](images/yagom.png) 33 | 34 | ### yagom 35 | * [Blog](http://blog.yagom.net) 36 | * [Facebook](https://fb.com/yagomsoft) 37 | * [Facebook Page](https://fb.com/yagompage) 38 | * [Facebook Group](https://fb.com/groups/yagom) 39 | * [Contacts](https://yagom.github.io/contacts) 40 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate 2 | 3 | title: yagom's Swift Basic 4 | description: 야곰의 스위프트 기본 문법 강좌입니다. 5 | 6 | show_downloads: false 7 | google_analytics: UA-97421875-1 -------------------------------------------------------------------------------- /contents/00_introduction/00_introduction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yagom/swift_basic/4db5b6c463c6a513fd2edb93fa760280155344a4/contents/00_introduction/00_introduction.pdf -------------------------------------------------------------------------------- /contents/00_introduction/README.md: -------------------------------------------------------------------------------- 1 | # 강좌소개 2 | 3 | [![클릭하여 이동](http://img.youtube.com/vi/2n-fSlW-jts/0.jpg)](http://www.youtube.com/watch?v=2n-fSlW-jts "introduction") 4 | 5 | [PDF자료](00_introduction.pdf) -------------------------------------------------------------------------------- /contents/00_introduction/console_log/README.md: -------------------------------------------------------------------------------- 1 | # Swift 기초 2 | 3 | 작성자 : [yagom](http://blog.yagom.net) 4 | 5 | 6 | [![클릭하여 이동](http://img.youtube.com/vi/8Xe_fr_WRgc/0.jpg)](http://www.youtube.com/watch?v=8Xe_fr_WRgc "console_log") 7 | 8 | [소스코드](console_log.swift) 9 | 10 | 11 | ## 이름짓기 규칙 12 | 13 | ### Lower Camel Case 14 | 15 | > fuction, method, variable, constant 16 | > > someVariableName 17 | 18 | ### Upper Camel Case 19 | > type(class, struct, enum, extension…) 20 | > > Person, Point, Week 21 | 22 | ```Swift는 모든 대소문자를 구분합니다.``` 23 | 24 | 25 | ## 콘솔로그 남기기 26 | > print 함수 27 | > > 단순 문자열 출력 28 | 29 | > dump 함수 30 | > > 인스턴스의 자세한 설명(description 프로퍼티)까지 출력 31 | 32 | ## 문자열 보간법 33 | `String Interpolation` 34 | 35 | 프로그램 실행 중 문자열 내에 변수 또는 상수의 실질적인 값을 표현하기 위해 사용합니다. 36 | 37 | `\()` 38 | 39 | 40 | ## 관련 문서 41 | * [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/) 42 | * [The Swift Programming Language - Strings and Characters](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID292) -------------------------------------------------------------------------------- /contents/00_introduction/console_log/console_log.swift: -------------------------------------------------------------------------------- 1 | /* 콘솔로그와 문자열 보간법 */ 2 | 3 | import Swift 4 | 5 | let age: Int = 10 6 | 7 | "안녕하세요! 저는 \(age)살입니다" 8 | // == "안녕하세요! 저는 10살입니다" 9 | 10 | "안녕하세요! 저는 \(age + 5)살입니다" 11 | // == "안녕하세요! 저는 15살입니다" 12 | 13 | print("안녕하세요! 저는 \(age + 5)살입니다") 14 | 15 | print("\n####################\n") 16 | 17 | class Person { 18 | var name: String = "yagom" 19 | var age: Int = 10 20 | } 21 | 22 | let yagom: Person = Person() 23 | 24 | print(yagom) 25 | 26 | print("\n####################\n") 27 | 28 | dump(yagom) 29 | 30 | 31 | -------------------------------------------------------------------------------- /contents/01_let_var/README.md: -------------------------------------------------------------------------------- 1 | # 상수, 변수의 선언 2 | 3 | [![클릭하여 이동](http://img.youtube.com/vi/umgxytoUdsE/0.jpg)](http://www.youtube.com/watch?v=umgxytoUdsE "let_var") 4 | 5 | [소스코드](let_var.swift) 6 | 7 | 8 | * 상수 선언 키워드 __let__ 9 | * 변수 선언 키워드 __var__ 10 | 11 | __let__ `이름`: `타입` = `값` 12 | __var__ `이름`: `타입` = `값` 13 | 14 | > 값의 타입이 명확하다면 타입은 생략 가능 15 | 16 | ```swift 17 | let 이름 = 값 18 | var 이름 = 값 19 | ``` 20 | 21 | ```swift 22 | let constant: String = "차후에 변경이 불가능한 상수 let" 23 | var variable: String = "차후에 변경이 가능한 변수 var" 24 | 25 | variable = "변수는 이렇게 차후에 다른 값을 할당할 수 있지만" 26 | // constant = "상수는 차후에 값을 변경할 수 없습니다" // 오류발생 27 | ``` 28 | 29 | ## 상수 선언 후에 나중에 값 할당하기 30 | 31 | > 나중에 할당하려고 하는 상수나 변수는 타입을 _꼭_ 명시해주어야 합니다 32 | 33 | ```swift 34 | let sum: Int 35 | let inputA: Int = 100 36 | let inputB: Int = 200 37 | 38 | // 선언 후 첫 할당 39 | sum = inputA + inputB 40 | 41 | // sum = 1 // 그 이후에는 다시 값을 바꿀 수 없습니다, 오류발생 42 | 43 | // 변수도 물론 차후에 할당하는 것이 가능합니다 44 | var nickName: String 45 | 46 | nickName = "yagom" 47 | 48 | // 변수는 차후에 다시 다른 값을 할당해도 문제가 없지요 49 | nickName = "야곰" 50 | ``` 51 | 52 | ## 생각해보기 53 | 54 | 다음과 같은 경우에 각 값은 상수와 변수 중 어느 것으로 선언하는 것이 더 좋을지 생각해 봅시다. 55 | 56 | ```swift 57 | OOO name = "yagom" 58 | OOO numberToAdd = 5 59 | OOO pi = 3.14195 60 | OOO maxItemCount = 1000 61 | ``` 62 | 63 | ## 관련문서 64 | 65 | * [The Swift Programming Language - The Basics](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html) 66 | 67 | -------------------------------------------------------------------------------- /contents/01_let_var/let_var.swift: -------------------------------------------------------------------------------- 1 | /* 상수와 변수 */ 2 | 3 | import Swift 4 | 5 | // 상수, 변수의 선언 6 | // 상수 선언 키워드 let 7 | // 변수 선언 키워드 var 8 | 9 | // 상수의 선언 10 | // let 이름: 타입 = 값 11 | 12 | // 변수의 선언 13 | // var 이름: 타입 = 값 14 | 15 | // 값의 타입이 명확하다면 타입은 생략 가능 16 | // let 이름 = 값 17 | // var 이름 = 값 18 | 19 | let constant: String = "차후에 변경이 불가능한 상수 let" 20 | var variable: String = "차후에 변경이 가능한 변수 var" 21 | 22 | variable = "변수는 이렇게 차후에 다른 값을 할당할 수 있지만" 23 | // constant = "상수는 차후에 값을 변경할 수 없습니다" // 오류발생 24 | 25 | // 상수 선언 후에 나중에 값 할당하기 26 | 27 | // 나중에 할당하려고 하는 상수나 변수는 타입을 꼭 명시해주어야 합니다 28 | let sum: Int 29 | let inputA: Int = 100 30 | let inputB: Int = 200 31 | 32 | // 선언 후 첫 할당 33 | sum = inputA + inputB 34 | 35 | // sum = 1 // 그 이후에는 다시 값을 바꿀 수 없습니다, 오류발생 36 | 37 | 38 | // 변수도 물론 차후에 할당하는 것이 가능합니다 39 | var nickName: String 40 | 41 | nickName = "yagom" 42 | 43 | // 변수는 차후에 다시 다른 값을 할당해도 문제가 없지요 44 | nickName = "야곰" 45 | -------------------------------------------------------------------------------- /contents/02_data_types/README.md: -------------------------------------------------------------------------------- 1 | # Swift의 기본 데이터 타입 2 | 3 | * Bool 4 | * Int, UInt 5 | * Float, Double 6 | * Character, String 7 | 8 | [![클릭하여 이동](http://img.youtube.com/vi/3qu7gpzE9IE/0.jpg)](http://www.youtube.com/watch?v=3qu7gpzE9IE "data_types") 9 | 10 | [소스코드](data_types.swift) 11 | 12 | ## Bool 13 | `true`와 `false`만을 값으로 가지는 타입 14 | 15 | ```swift 16 | var someBool: Bool = true 17 | someBool = false 18 | // someBool = 0 // 컴파일 오류발생 19 | // someBool = 1 // 컴파일 오류발생 20 | ``` 21 | 22 | ## Int, UInt 23 | 24 | ### Int 25 | 정수 타입. 현재는 기본적으로 64비트 정수형. 26 | 27 | ```swift 28 | var someInt: Int = -100 29 | // someInt = 100.1 // 컴파일 오류발생 30 | ``` 31 | 32 | ### UInt 33 | 양의 정수 타입. 현재는 기본적으로 64비트 양의 정수형. 34 | 35 | ```swift 36 | var someUInt: UInt = 100 37 | // someUInt = -100 // 컴파일 오류발생 38 | // someUInt = someInt // 컴파일 오류발생 39 | ``` 40 | 41 | ## Float, Double 42 | 43 | ### Float 44 | 실수 타입. 32비트 부동소수형. 45 | 46 | ```swift 47 | var someFloat: Float = 3.14 48 | someFloat = 3 49 | ``` 50 | 51 | ### Double 52 | 실수타입. 64비트 부동소수형. 53 | 54 | ```swift 55 | var someDouble: Double = 3.14 56 | someDouble = 3 57 | // someDouble = someFloat // 컴파일 오류발생 58 | ``` 59 | 60 | ## Character, String 61 | 62 | ### Character 63 | 문자 타입. 유니코드 사용. 큰따옴표("") 사용. 64 | 65 | ```swift 66 | var someCharacter: Character = "🇰🇷" 67 | someCharacter = "😄" 68 | someCharacter = "가" 69 | someCharacter = "A" 70 | // someCharacter = "하하하" // 컴파일 오류발생 71 | print(someCharacter) 72 | ``` 73 | 74 | ### String 75 | 문자열 타입. 유니코드 사용. 큰따옴표("") 사용. 76 | 77 | ```swift 78 | var someString: String = "하하하 😄 " 79 | someString = someString + "웃으면 복이와요" 80 | print(someString) 81 | 82 | // someString = someCharacter // 컴파일 오류발생 83 | ``` 84 | 85 | 여러줄 문자열은 큰따옴표 세 개 사용. 86 | 87 | ```swift 88 | someString = """ 89 | 여러줄 문자열을 90 | 사용할 수 있습니다. 91 | 첫 줄에 겹따옴표 세 개, 92 | 마지막 줄에 겹따옴표 세 개를 93 | 사용하면 됩니다. 94 | """ 95 | 96 | someString = """ 97 | 겹따옴표 세 개인 줄(첫줄, 끝줄)에서 98 | 줄 바꿈을 하지 않으면 오류가 발생합니다. 99 | """ 100 | 101 | /* 102 | someString = """오류발생 103 | 오류발생""" 104 | */ 105 | ``` 106 | 107 | 108 | ## 생각해보기 109 | 110 | 다음 코드에서 integer, floatingPoint, apple 상수는 각각 어떤 타입이 될까요? 상상해보고 확인해보세요~! 111 | 112 | ```swift 113 | let integer = 100 114 | let floatingPoint = 12.34 115 | let apple = "A" 116 | ``` 117 | `힌트 : type(of: ) ` 118 | 119 | 120 | # Any, AnyObject, nil 121 | 122 | * Any 123 | * AnyObject 124 | * nil 125 | 126 | [![클릭하여 이동](http://img.youtube.com/vi/1QV4-B5ibd4/0.jpg)](http://www.youtube.com/watch?v=1QV4-B5ibd4 "any_anyobject_nil") 127 | 128 | [소스코드](any_anyobject_nil.swift) 129 | 130 | ## Any 131 | 132 | Swift의 모든 타입을 지칭하는 키워드 133 | 134 | ```swift 135 | var someAny: Any = 100 136 | someAny = "어떤 타입도 수용 가능합니다" 137 | someAny = 123.12 138 | ``` 139 | 140 | > Any 타입에 Double 자료를 넣어두었더라도 Any는 Double 타입이 아니기 때문에 할당할 수 없습니다. 명시적으로 타입을 변환해 주어야 합니다. 타입 변환은 차후에 다룹니다 141 | 142 | ```swift 143 | let someDouble: Double = someAny // 컴파일 오류발생 144 | ``` 145 | 146 | ## AnyObject 147 | 모든 클래스 타입을 지칭하는 프로토콜 148 | 149 | > 클래스와 프로토콜에 대한 설명은 차후에 합니다 150 | 151 | ```swift 152 | class SomeClass {} 153 | 154 | var someAnyObject: AnyObject = SomeClass() 155 | ``` 156 | 157 | AnyObject는 클래스의 인스턴스만 수용 가능하기 때문에 클래스의 인스턴스가 아니면 할당할 수 없습니다. 158 | 159 | ```swift 160 | someAnyObject = 123.12 // 컴파일 오류발생 161 | ``` 162 | 163 | ## nil 164 | 165 | 없음을 의미하는 키워드 166 | 167 | > 다른 언어의 `NULL`, `Null`, `null` 등과 유사한 표현입니다. 168 | 169 | 아래 코드에서 `someAny`는 `Any` 타입이고, `someAnyObject`는 `AnyObject` 타입이기 때문에 `nil`을 할당할 수 없습니다. 170 | `nil`을 다루는 방법은 __옵셔널__ 파트에서 다룹니다. 171 | 172 | ```swift 173 | someAny = nil // 컴파일 오류발생 174 | someAnyObject = nil // 컴파일 오류발생 175 | ``` 176 | 177 | 178 | ## 관련문서 179 | 180 | * [The Swift Programming Language - The Basics](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html) 181 | 182 | -------------------------------------------------------------------------------- /contents/02_data_types/any_anyobject_nil.swift: -------------------------------------------------------------------------------- 1 | /* Any, AnyObject, nil */ 2 | 3 | import Swift 4 | 5 | /* 6 | Any - Swift의 모든 타입을 지칭하는 키워드 7 | AnyObject - 모든 클래스 타입을 지칭하는 프로토콜 8 | nil - 없음을 의미하는 키워드 9 | */ 10 | 11 | //MARK: - Any 12 | var someAny: Any = 100 13 | someAny = "어떤 타입도 수용 가능합니다" 14 | someAny = 123.12 15 | 16 | // Any 타입에 Double 자료를 넣어두었더라도 17 | // Any는 Double 타입이 아니기 때문에 할당할 수 없습니다 18 | // 명시적으로 타입을 변환해 주어야 합니다. 19 | // 타입 변환은 차후에 다룹니다 20 | //let someDouble: Double = someAny // 컴파일 오류발생 21 | 22 | //MARK: - AnyObject 23 | 24 | class SomeClass {} 25 | 26 | var someAnyObject: AnyObject = SomeClass() 27 | 28 | // AnyObject는 클래스의 인스턴스만 수용 가능하기 때문에 29 | // 클래스의 인스턴스가 아니면 할당할 수 없습니다 30 | //someAnyObject = 123.12 // 컴파일 오류발생 31 | 32 | // nil은 없음을 의미하는 키워드입니다 33 | // 다른 언어의 NULL, Null, null 등과 유사한 표현입니다. 34 | // someAny는 Any 타입이고, 35 | // someAnyObject는 AnyObject 타입이기 때문에 36 | // nil을 할당할 수 없습니다 37 | // nil을 다루는 방법은 옵셔널에서 다룹니다. 38 | 39 | //MARK: - nil 40 | 41 | //someAny = nil // 컴파일 오류발생 42 | //someAnyObject = nil // 컴파일 오류발생 43 | 44 | -------------------------------------------------------------------------------- /contents/02_data_types/data_types.swift: -------------------------------------------------------------------------------- 1 | /* 기본 데이터 타입 */ 2 | 3 | import UIKit 4 | 5 | // Swift의 기본 데이터 타입 6 | // Bool, Int, UInt, Float, Double, Character, String 7 | 8 | // Bool 9 | var someBool: Bool = true 10 | someBool = false 11 | // someBool = 0 // 컴파일 오류발생 12 | // someBool = 1 // 컴파일 오류발생 13 | 14 | // Int 15 | var someInt: Int = -100 16 | // someInt = 100.1 // 컴파일 오류발생 17 | 18 | // UInt 19 | var someUInt: UInt = 100 20 | // someUInt = -100 // 컴파일 오류발생 21 | // someUInt = someInt // 컴파일 오류발생 22 | 23 | // Float 24 | var someFloat: Float = 3.14 25 | someFloat = 3 26 | 27 | // Double 28 | var someDouble: Double = 3.14 29 | someDouble = 3 30 | // someDouble = someFloat // 컴파일 오류발생 31 | 32 | // Character 33 | var someCharacter: Character = "🇰🇷" 34 | someCharacter = "😄" 35 | someCharacter = "가" 36 | someCharacter = "A" 37 | // someCharacter = "하하하" // 컴파일 오류발생 38 | print(someCharacter) 39 | 40 | // String 41 | var someString: String = "하하하 😄 " 42 | someString = someString + "웃으면 복이와요" 43 | print(someString) 44 | 45 | // someString = someCharacter // 컴파일 오류발생 46 | 47 | someString = """ 48 | 여러줄 문자열을 49 | 사용할 수 있습니다. 50 | 첫 줄에 겹따옴표 세 개, 51 | 마지막 줄에 겹따옴표 세 개를 52 | 사용하면 됩니다. 53 | """ 54 | 55 | someString = """ 56 | 겹따옴표 세 개인 줄(첫줄, 끝줄)에서 57 | 줄 바꿈을 하지 않으면 오류가 발생합니다. 58 | """ 59 | 60 | /* 61 | someString = """오류발생 62 | 오류발생""" 63 | */ 64 | -------------------------------------------------------------------------------- /contents/03_collection_types/README.md: -------------------------------------------------------------------------------- 1 | # 컬렉션 타입 2 | 3 | * Array 4 | * Dictionary 5 | * Set 6 | 7 | 8 | [![클릭하여 이동](http://img.youtube.com/vi/FWFbZGoQPUI/0.jpg)](http://www.youtube.com/watch?v=FWFbZGoQPUI "collection_types") 9 | 10 | [소스코드](collection_types.swift) 11 | 12 | 13 | | 타입 | 설명 | 14 | |:------|:------| 15 | | Array | 순서가 있는 리스트 컬렉션 | 16 | | Dictionary | `키`와 `값`의 쌍으로 이루어진 컬렉션 | 17 | | Set | 순서가 없고, 멤버가 유일한 컬렉션 | 18 | 19 | ## Array 20 | 21 | Array는 멤버가 순서(인덱스)를 가진 리스트 형태의 컬렉션 타입입니다. 22 | 23 | > Array 선언 및 생성 24 | > > Array는 여러 리터럴 문법을 활용할 수 있어서 표현 방법이 다양합니다 25 | 26 | ```swift 27 | // 빈 Int Array 생성 28 | var integers: Array = Array() 29 | 30 | // 같은 표현 31 | // var integers: Array = [Int]() 32 | // var integers: Array = [] 33 | // var integers: [Int] = Array() 34 | // var integers: [Int] = [Int]() 35 | // var integers: [Int] = [] 36 | // var integers = [Int]() 37 | ``` 38 | > Array 활용 39 | 40 | ```swift 41 | integers.append(1) 42 | integers.append(100) 43 | 44 | // Int 타입이 아니므로 Array에 추가할 수 없습니다 45 | //integers.append(101.1) 46 | 47 | print(integers) // [1, 100] 48 | 49 | // 멤버 포함 여부 확인 50 | print(integers.contains(100)) // true 51 | print(integers.contains(99)) // false 52 | 53 | // 멤버 교체 54 | integers[0] = 99 55 | 56 | // 멤버 삭제 57 | integers.remove(at: 0) 58 | integers.removeLast() 59 | integers.removeAll() 60 | 61 | // 멤버 수 확인 62 | print(integers.count) 63 | 64 | // 인덱스를 벗어나 접근하려면 익셉션 런타임 오류발생 65 | //integers[0] 66 | ``` 67 | 68 | > let을 사용하여 Array를 선언하면 불변 Array가 됩니다 69 | 70 | ```swift 71 | let immutableArray = [1, 2, 3] 72 | 73 | // 수정이 불가능한 Array이므로 멤버를 추가하거나 삭제할 수 없습니다 74 | //immutableArray.append(4) 75 | //immutableArray.removeAll() 76 | ``` 77 | 78 | ## Dictionary 79 | 80 | Dictionary는 `키`와 `값`의 쌍으로 이루어진 컬렉션 타입입니다. 81 | 82 | > Dictionary의 선언과 생성 83 | > > Dictionary는 여러 리터럴 문법을 활용할 수 있어서 표현 방법이 다양합니다 84 | 85 | ```swift 86 | // Key가 String 타입이고 Value가 Any인 빈 Dictionary 생성 87 | var anyDictionary: Dictionary = [String: Any]() 88 | 89 | // 같은 표현 90 | // var anyDictionary: Dictionary = Dictionary() 91 | // var anyDictionary: Dictionary = [:] 92 | // var anyDictionary: [String: Any] = Dictionary() 93 | // var anyDictionary: [String: Any] = [String: Any]() 94 | // var anyDictionary: [String: Any] = [:] 95 | // var anyDictionary = [String: Any]() 96 | 97 | ``` 98 | 99 | > Dictionary 활용 100 | 101 | ```swift 102 | 103 | // 키에 해당하는 값 할당 104 | anyDictionary["someKey"] = "value" 105 | anyDictionary["anotherKey"] = 100 106 | 107 | print(anyDictionary) // ["someKey": "value", "anotherKey": 100] 108 | 109 | // 키에 해당하는 값 변경 110 | anyDictionary["someKey"] = "dictionary" 111 | print(anyDictionary) ["someKey": "dictionary", "anotherKey": 100] 112 | 113 | // 키에 해당하는 값 제거 114 | anyDictionary.removeValue(forKey: "anotherKey") 115 | anyDictionary["someKey"] = nil 116 | print(anyDictionary) 117 | ``` 118 | 119 | > let을 사용하여 Dictionary를 선언하면 불변 Dictionary가 됩니다 120 | 121 | ```swift 122 | let emptyDictionary: [String: String] = [:] 123 | let initalizedDictionary: [String: String] = ["name": "yagom", "gender": "male"] 124 | 125 | // 불변 Dictionary이므로 값 변경 불가 126 | //emptyDictionary["key"] = "value" 127 | ``` 128 | 129 | > 키에 해당하는 값을 다른 변수나 상수에 할당하고자 할 때는 옵셔널과 타입 캐스팅 파트에서 다룹니다 130 | 131 | ```swift 132 | // "name"이라는 키에 해당하는 값이 없을 수 있으므로 133 | // String 타입의 값이 나올 것이라는 보장이 없습니다. 134 | // 컴파일 오류가 발생합니다 135 | let someValue: String = initalizedDictionary["name"] 136 | ``` 137 | 138 | ## Set 139 | Set는 순서가 없고, 멤버가 유일한 것을 보장하는 컬렉션 타입입니다. 140 | 141 | > Set의 선언과 생성 142 | 143 | ``` 144 | // 빈 Int Set 생성 145 | var integerSet: Set = Set() 146 | integerSet.insert(1) 147 | integerSet.insert(100) 148 | integerSet.insert(99) 149 | integerSet.insert(99) 150 | integerSet.insert(99) 151 | 152 | print(integerSet) // [100, 99, 1] 153 | print(integerSet.contains(1)) // true 154 | print(integerSet.contains(2)) // false 155 | 156 | integerSet.remove(100) 157 | integerSet.removeFirst() 158 | 159 | print(integerSet.count) // 1 160 | ``` 161 | 162 | > Set는 집합연산에 많이 활용됩니다 163 | 164 | ``` 165 | // Set는 집합 연산에 꽤 유용합니다 166 | let setA: Set = [1, 2, 3, 4, 5] 167 | let setB: Set = [3, 4, 5, 6, 7] 168 | 169 | // 합집합 170 | let union: Set = setA.union(setB) 171 | print(union) // [2, 4, 5, 6, 7, 3, 1] 172 | 173 | // 합집합 오름차순 정렬 174 | let sortedUnion: [Int] = union.sorted() 175 | print(sortedUnion) // [1, 2, 3, 4, 5, 6, 7] 176 | 177 | // 교집합 178 | let intersection: Set = setA.intersection(setB) 179 | print(intersection) // [5, 3, 4] 180 | 181 | // 차집합 182 | let subtracting: Set = setA.subtracting(setB) 183 | print(subtracting) // [2, 1] 184 | ``` 185 | 186 | 187 | ## 생각해보기 188 | 189 | 다음과 같은 경우에는 각각 어떤 컬렉션 타입을, 상수/변수 선언 중 어떤 것을 사용하면 유용할지 생각해 봅시다. 190 | 191 | * 영어 알파벳 소문자를 모아두는 컬렉션 192 | * 책의 제목과 저자 정리를 위한 컬렉션 193 | * 우리반 학생 명부 작성을 위한 컬렉션 194 | 195 | 196 | 197 | ## 관련문서 198 | 199 | * [The Swift Programming Language - Collection Types](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html) 200 | -------------------------------------------------------------------------------- /contents/03_collection_types/collection_types.swift: -------------------------------------------------------------------------------- 1 | /* 컬렉션 타입 */ 2 | /* Array, Dictionary, Set */ 3 | 4 | import Swift 5 | 6 | /* 7 | Array - 순서가 있는 리스트 컬렉션 8 | Dictionary - 키와 값의 쌍으로 이루어진 컬렉션 9 | Set - 순서가 없고, 멤버가 유일한 컬렉션 10 | */ 11 | 12 | 13 | //MARK: - Array 14 | 15 | // 빈 Int Array 생성 16 | var integers: Array = Array() 17 | integers.append(1) 18 | integers.append(100) 19 | //integers.append(101.1) 20 | 21 | print(integers) // [1, 100] 22 | 23 | print(integers.contains(100)) // true 24 | print(integers.contains(99)) // false 25 | 26 | integers.remove(at: 0) 27 | integers.removeLast() 28 | integers.removeAll() 29 | 30 | print(integers.count) // 0 31 | 32 | //integers[0] // 범위 초과 - 런타임 오류 발생 33 | 34 | // Array와 [Double]는 동일한 표현 35 | // 빈 Double Array 생성 36 | var doubles: Array = [Double]() 37 | 38 | // 빈 String Array 생성 39 | var strings: [String] = [String]() 40 | 41 | // 빈 Character Array 생성 42 | // []는 새로운 빈 Array 43 | var characters: [Character] = [] 44 | 45 | // let을 사용하여 Array를 선언하면 불변 Array 46 | let immutableArray = [1, 2, 3] 47 | 48 | // 불면 Array의 요소는 추가/삭제 불가 - 컴파일 오류 발생 49 | //immutableArray.append(4) 50 | //immutableArray.removeAll() 51 | 52 | 53 | 54 | 55 | //MARK: - Dictionary 56 | 57 | // Key가 String 타입이고 Value가 Any인 빈 Dictionary 생성 58 | var anyDictionary: Dictionary = [String: Any]() 59 | anyDictionary["someKey"] = "value" 60 | anyDictionary["anotherKey"] = 100 61 | print(anyDictionary) // ["someKey": "value", "anotherKey": 100] 62 | 63 | // Key에 해당하는 값 변경 64 | anyDictionary["someKey"] = "dictionary" 65 | print(anyDictionary) // ["someKey": "dictionary", "anotherKey": 100] 66 | 67 | // Key에 해당하는 값 제거 68 | anyDictionary.removeValue(forKey: "anotherKey") 69 | anyDictionary["someKey"] = nil 70 | print(anyDictionary) // [:] 71 | 72 | // 빈 Dictionary 생성 73 | let emptyDictionary: [String: String] = [:] 74 | 75 | // 초기 값을 가지는 Dictionary 생성 76 | let initalizedDictionary: [String: String] = ["name": "yagom", "gender": "male"] 77 | 78 | // let으로 선언한 불변 Dictionary는 수정 불가 - 컴파일 오류 발생 79 | //emptyDictionary["key"] = "value" 80 | 81 | // name 키에 해당하는 값이 Dictionary에 존재하지 않을 수 있으므로 82 | // 컴파일 오류 발생 - 옵셔널 파트에서 상세히 다룹니다 83 | //let someValue: String = initalizedDictionary["name"] 84 | 85 | 86 | 87 | 88 | //MARK: - Set 89 | 90 | // 빈 Int Set 생성 91 | var integerSet: Set = Set() 92 | integerSet.insert(1) 93 | integerSet.insert(100) 94 | integerSet.insert(99) 95 | integerSet.insert(99) 96 | integerSet.insert(99) 97 | 98 | print(integerSet) // [100, 99, 1] 99 | print(integerSet.contains(1)) // true 100 | print(integerSet.contains(2)) // false 101 | 102 | integerSet.remove(100) 103 | integerSet.removeFirst() 104 | 105 | print(integerSet.count) // 1 106 | 107 | // Set는 집합 연산에 꽤 유용합니다 108 | let setA: Set = [1, 2, 3, 4, 5] 109 | let setB: Set = [3, 4, 5, 6, 7] 110 | 111 | // 합집합 112 | let union: Set = setA.union(setB) 113 | print(union) // [2, 4, 5, 6, 7, 3, 1] 114 | 115 | // 합집합 오름차순 정렬 116 | let sortedUnion: [Int] = union.sorted() 117 | print(sortedUnion) // [1, 2, 3, 4, 5, 6, 7] 118 | 119 | // 교집합 120 | let intersection: Set = setA.intersection(setB) 121 | print(intersection) // [5, 3, 4] 122 | 123 | // 차집합 124 | let subtracting: Set = setA.subtracting(setB) 125 | print(subtracting) // [2, 1] 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /contents/04_function/README.md: -------------------------------------------------------------------------------- 1 | # 함수 2 | 3 | [![클릭하여 이동](http://img.youtube.com/vi/TkM2Xcnt2Xk/0.jpg)](http://www.youtube.com/watch?v=TkM2Xcnt2Xk "function_basic") 4 | 5 | [소스](function.swift) 6 | 7 | 8 | ## 함수의 선언 9 | 10 | ### 함수선언의 기본형태 11 | 12 | ```swift 13 | func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 ...) -> 반환타입 { 14 | /* 함수 구현부 */ 15 | return 반환값 16 | } 17 | 18 | // 예) 19 | // sum이라는 이름을 가지고 20 | // a와 b라는 Int 타입의 매개변수를 가지며 21 | // Int 타입의 값을 반환하는 함수 22 | func sum(a: Int, b: Int) -> Int { 23 | return a + b 24 | } 25 | 26 | ``` 27 | 28 | ### 반환 값이 없는 함수 29 | 30 | ```swift 31 | func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 ...) -> Void { 32 | /* 함수 구현부 */ 33 | return 34 | } 35 | 36 | // 예) 37 | func printMyName(name: String) -> Void { 38 | print(name) 39 | } 40 | 41 | // 반환 값이 없는 경우, 반환 타입(Void)을 생략해 줄 수 있습니다 42 | func printYourName(name: String) { 43 | print(name) 44 | } 45 | 46 | ``` 47 | 48 | ### 매개변수가 없는 함수 49 | 50 | ```swift 51 | func 함수이름() -> 반환타입 { 52 | /* 함수 구현부 */ 53 | return 반환값 54 | } 55 | 56 | // 예) 57 | func maximumIntegerValue() -> Int { 58 | return Int.max 59 | } 60 | ``` 61 | 62 | ### 매개변수와 반환값이 없는 함수 63 | ```swift 64 | func 함수이름() -> Void { 65 | /* 함수 구현부 */ 66 | return 67 | } 68 | 69 | // 함수 구현이 짧은 경우 70 | // 가독성을 해치지 않는 범위에서 71 | // 줄바꿈을 하지 않고 한 줄에 표현해도 무관합니다 72 | func hello() -> Void { print("hello") } 73 | 74 | 75 | // 반환 값이 없는 경우, 반환 타입(Void)을 생략해 줄 수 있습니다 76 | func 함수이름() { 77 | /* 함수 구현부 */ 78 | return 79 | } 80 | 81 | func bye() { print("bye") } 82 | ``` 83 | 84 | ## 함수의 호출 85 | 86 | ```swift 87 | sum(a: 3, b: 5) // 8 88 | 89 | printMyName(name: "yagom") // yagom 90 | 91 | printYourName(name: "hana") // hana 92 | 93 | maximumIntegerValue() // Int의 최댓값 94 | 95 | hello() // hello 96 | 97 | bye() // bye 98 | ``` 99 | 100 | 101 | [![클릭하여 이동](http://img.youtube.com/vi/0sMRDBv3HtU/0.jpg)](http://www.youtube.com/watch?v=0sMRDBv3HtU "function_advance") 102 | 103 | 104 | ## 매개변수 기본 값 105 | 매개변수에 기본적으로 전달될 값을 미리 지정할 수 있습니다 106 | > 기본값을 갖는 매개변수는 매개변수 목록 중에 뒤쪽에 위치하는 것이 좋습니다 107 | 108 | ```swift 109 | func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 = 매개변수 기본값 ...) -> 반환타입 { 110 | /* 함수 구현부 */ 111 | return 반환값 112 | } 113 | 114 | func greeting(friend: String, me: String = "yagom") { 115 | print("Hello \(friend)! I'm \(me)") 116 | } 117 | 118 | // 매개변수 기본값을 가지는 매개변수는 호출시 생략할 수 있습니다 119 | greeting(friend: "hana") // Hello hana! I'm yagom 120 | greeting(friend: "john", me: "eric") // Hello john! I'm eric 121 | 122 | ``` 123 | 124 | ## 전달인자 레이블 125 | 함수를 호출할 때 함수 사용자의 입장에서 매개변수의 역할을 좀 더 명확하게 표현하고자 할 때 사용합니다 126 | 127 | ```swift 128 | func 함수이름(전달인자 레이블 매개변수1이름: 매개변수1타입, 전달인자 레이블 매개변수2이름: 매개변수2타입 ...) -> 반환타입 { 129 | /* 함수 구현부 */ 130 | return 131 | } 132 | 133 | // 함수 내부에서 전달인자를 사용할 때에는 매개변수 이름을 사용합니다 134 | func greeting(to friend: String, from me: String) { 135 | print("Hello \(friend)! I'm \(me)") 136 | } 137 | 138 | // 함수를 호출할 때에는 전달인자 레이블을 사용해야 합니다 139 | greeting(to: "hana", from: "yagom") // Hello hana! I'm yagom 140 | ``` 141 | 142 | ## 가변 매개변수 143 | 전달 받을 값의 개수를 알기 어려울 때 사용할 수 있습니다 144 | > 가변 매개변수는 함수당 하나만 가질 수 있습니다 145 | 146 | ```swift 147 | func 함수이름(매개변수1이름: 매개변수1타입, 전달인자 레이블 매개변수2이름: 매개변수2타입...) -> 반환타입 { 148 | /* 함수 구현부 */ 149 | return 150 | } 151 | 152 | func sayHelloToFriends(me: String, friends: String...) -> String { 153 | return "Hello \(friends)! I'm \(me)!" 154 | } 155 | print(sayHelloToFriends(me: "yagom", friends: "hana", "eric", "wing")) 156 | // Hello ["hana", "eric", "wing"]! I'm yagom! 157 | 158 | print(sayHelloToFriends(me: "yagom")) 159 | // Hello []! I'm yagom! 160 | 161 | ``` 162 | 163 | > 위에 설명한 함수의 다양한 모양은 모두 섞어서 사용 가능합니다 164 | 165 | ## 데이터 타입으로서의 함수 166 | 167 | 스위프트는 함수형 프로그래밍 패러다임을 포함하는 다중 패러다임 언어이므로 스위프트의 함수는 일급객체입니다. 그래서 함수를 변수, 상수 등에 할당이 가능하고 매개변수를 통해 전달할 수도 있습니다 168 | 169 | ### 함수의 타입표현 170 | 반환타입을 생략할 수 없습니다. 171 | 172 | ```swift 173 | (매개변수1타입, 매개변수2타입 ...) -> 반환타입 174 | ``` 175 | 176 | ### 함수타입 사용 177 | ```swift 178 | var someFunction: (String, String) -> Void = greeting(to:from:) 179 | someFunction("eric", "yagom") // Hello eric! I'm yagom 180 | 181 | someFunction = greeting(friend:me:) 182 | someFunction("eric", "yagom") // Hello eric! I'm yagom 183 | 184 | 185 | // 타입이 다른 함수는 할당할 수 없습니다 - 컴파일 오류 발생 186 | //someFunction = sayHelloToFriends(me: friends:) 187 | 188 | 189 | func runAnother(function: (String, String) -> Void) { 190 | function("jenny", "mike") 191 | } 192 | 193 | // Hello jenny! I'm mike 194 | runAnother(function: greeting(friend:me:)) 195 | 196 | // Hello jenny! I'm mike 197 | runAnother(function: someFunction) 198 | ``` 199 | 200 | 201 | > 참고 : 스위프트의 전반적인 문법에서 띄어쓰기는 신경써야할 때가 많습니다 202 | 203 | 204 | ## 관련문서 205 | 206 | * [The Swift Programming Language - Functions](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html) 207 | -------------------------------------------------------------------------------- /contents/04_function/function.swift: -------------------------------------------------------------------------------- 1 | /* 함수 */ 2 | 3 | import Swift 4 | 5 | //MARK: - 함수의 선언 6 | 7 | //MARK: 함수선언의 기본형태 8 | //func <#함수이름#>(<#매개변수1이름#>: <#매개변수1타입#>, <#매개변수2이름#>: <#매개변수2타입#> ...) -> <#반환타입#> { 9 | // <#함수 구현부#> 10 | // return <#반환값#> 11 | //} 12 | 13 | func sum(a: Int, b: Int) -> Int { 14 | return a + b 15 | } 16 | 17 | 18 | 19 | //MARK: 반환 값이 없는 함수 20 | //func <#함수이름#>(<#매개변수1이름#>: <#매개변수1타입#>, <#매개변수2이름#>: <#매개변수2타입#> ...) -> Void { 21 | // <#함수 구현부#> 22 | // return 23 | //} 24 | 25 | func printMyName(name: String) -> Void { 26 | print(name) 27 | } 28 | 29 | 30 | 31 | 32 | //func <#함수이름#>(<#매개변수1이름#>: <#매개변수1타입#>, <#매개변수2이름#>: <#매개변수2타입#> ...) { 33 | // <#함수 구현부#> 34 | // return 35 | //} 36 | 37 | func printYourName(name: String) { 38 | print(name) 39 | } 40 | 41 | 42 | 43 | 44 | //MARK: 매개변수가 없는 함수 45 | //func <#함수이름#>() -> <#반환타입#> { 46 | // <#함수 구현부#> 47 | // return <#반환값#> 48 | //} 49 | 50 | func maximumIntegerValue() -> Int { 51 | return Int.max 52 | } 53 | 54 | //MARK: - 매개변수 기본값 55 | 56 | // 기본값을 갖는 매개변수는 매개변수 목록 중에 뒤쪽에 위치하는 것이 좋습니다 57 | //func <#함수이름#>(<#매개변수1이름#>: <#매개변수1타입#>, <#매개변수2이름#>: <#매개변수2타입#> = <#매개변수 기본값#> ...) -> <#반환타입#> { 58 | // <#함수 구현부#> 59 | // return <#반환값#> 60 | //} 61 | 62 | func greeting(friend: String, me: String = "yagom") { 63 | print("Hello \(friend)! I'm \(me)") 64 | } 65 | 66 | // 매개변수 기본값을 가지는 매개변수는 생략할 수 있습니다 67 | greeting(friend: "hana") // Hello hana! I'm yagom 68 | greeting(friend: "john", me: "eric") // Hello john! I'm eric 69 | 70 | 71 | //MARK: - 전달인자 레이블 72 | 73 | // 전달인자 레이블은 함수를 호출할 때 74 | // 매개변수의 역할을 좀 더 명확하게 하거나 75 | // 함수 사용자의 입장에서 표현하고자 할 때 사용합니다 76 | //func <#함수이름#>(<#전달인자 레이블#> <#매개변수1이름#>: <#매개변수1타입#>, <#전달인자 레이블#> <#매개변수2이름#>: <#매개변수2타입#> ...) -> <#반환타입#> { 77 | // <#함수 구현부#> 78 | // return 79 | //} 80 | 81 | // 함수 내부에서 전달인자를 사용할 때에는 매개변수 이름을 사용합니다 82 | func greeting(to friend: String, from me: String) { 83 | print("Hello \(friend)! I'm \(me)") 84 | } 85 | 86 | // 함수를 호출할 때에는 전달인자 레이블을 사용해야 합니다 87 | greeting(to: "hana", from: "yagom") // Hello hana! I'm yagom 88 | 89 | 90 | //MARK: - 가변 매개변수 91 | 92 | // 전달 받을 값의 개수를 알기 어려울 때 사용할 수 있습니다 93 | // 가변 매개변수는 함수당 하나만 가질 수 있습니다 94 | 95 | //func <#함수이름#>(<#매개변수1이름#>: <#매개변수1타입#>, <#전달인자 레이블#> <#매개변수2이름#>: <#매개변수2타입#>...) -> <#반환타입#> { 96 | // <#함수 구현부#> 97 | // return 98 | //} 99 | 100 | func sayHelloToFriends(me: String, friends: String...) -> String { 101 | return "Hello \(friends)! I'm \(me)!" 102 | } 103 | print(sayHelloToFriends(me: "yagom", friends: "hana", "eric", "wing")) 104 | // Hello ["hana", "eric", "wing"]! I'm yagom! 105 | 106 | print(sayHelloToFriends(me: "yagom")) 107 | // Hello []! I'm yagom! 108 | 109 | 110 | /* 111 | 위에 설명한 함수의 다양한 모양은 모두 섞어서 사용 가능합니다 112 | */ 113 | 114 | //MARK: - 데이터 타입으로서의 함수 115 | 116 | // 스위프트는 함수형 프로그래밍 패러다임을 포함하는 다중 패러다임 언어입니다 117 | // 스위프트의 함수는 일급객체이므로 변수, 상수 등에 저장이 가능하고 118 | // 매개변수를 통해 전달할 수도 있습니다 119 | 120 | //MARK: 함수의 타입표현 121 | // 반환타입을 생략할 수 없습니다 122 | // (<#매개변수1타입#>, <#매개변수2타입#> ...) -> <#반환타입#> 123 | 124 | var someFunction: (String, String) -> Void = greeting(to:from:) 125 | someFunction("eric", "yagom") // Hello eric! I'm yagom 126 | 127 | someFunction = greeting(friend:me:) 128 | someFunction("eric", "yagom") // Hello eric! I'm yagom 129 | 130 | 131 | // 타입이 다른 함수는 할당할 수 없습니다 132 | //someFunction = sayHelloToFriends(me: friends:) 133 | 134 | 135 | func runAnother(function: (String, String) -> Void) { 136 | function("jenny", "mike") 137 | } 138 | 139 | // Hello jenny! I'm mike 140 | runAnother(function: greeting(friend:me:)) 141 | 142 | // Hello jenny! I'm mike 143 | runAnother(function: someFunction) 144 | 145 | -------------------------------------------------------------------------------- /contents/05_conditional/README.md: -------------------------------------------------------------------------------- 1 | # 조건문 2 | 3 | * if-else 4 | * switch 5 | 6 | 7 | [![클릭하여 이동](http://img.youtube.com/vi/TmO3cTiJDIc/0.jpg)](http://www.youtube.com/watch?v=TmO3cTiJDIc "conditional") 8 | 9 | [소스코드](conditional.swift) 10 | 11 | 12 | ## if-else 구문 13 | 14 | ### if-else 구문의 기본 형태 15 | 16 | if만 단독적으로 사용해도 되고, else if, else와 조합해서 사용 가능합니다. 17 | if 뒤의 조건 값에는 Bool 타입의 값만 위치해야 하며, 조건 값을 감싸는 소괄호는 선택사항입니다. 18 | 19 | ```swift 20 | if 조건 { 21 | /* 실행 구문 */ 22 | } else if 조건 { 23 | /* 실행 구문 */ 24 | } else { 25 | /* 실행 구문 */ 26 | } 27 | ``` 28 | 29 | ### if-else의 사용 30 | ```swift 31 | let someInteger = 100 32 | 33 | if someInteger < 100 { 34 | print("100 미만") 35 | } else if someInteger > 100 { 36 | print("100 초과") 37 | } else { 38 | print("100") 39 | } // 100 40 | 41 | // 스위프트의 조건에는 항상 Bool 타입이 들어와야합니다 42 | // someInteger는 Bool 타입이 아닌 Int 타입이기 때문에 43 | // 컴파일 오류가 발생합니다 44 | //if someInteger { } 45 | ``` 46 | 47 | ## switch 구문 48 | 스위프트의 switch 구문은 다른 언어에 비해 굉장히 강력한 힘을 발휘합니다. 기본적으로 사용하던 정수타입의 값만 비교하는 것이 아니라 대부분의 스위프트 기본 타입을 지원하며, 다양한 패턴과도 응용이 가능합니다. 스위프트의 다양한 패턴은 [Swift Programming Language Reference의 패턴](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/doc/uid/TP40014097-CH36-ID419)에서 확인할 수 있습니다. 49 | 50 | > * 각각의 case 내부에는 실행가능한 코드가 반드시 위치해야 합니다 51 | > * 매우 한정적인 값(ex. enum의 case 등)이 비교값이 아닌 한 default 구문은 반드시 작성해야 합니다 52 | > * 명시적 break를 하지 않아도 자동으로 case마다 break 됩니다 53 | > * fallthrough 키워드를 사용하여 break를 무시할 수 있습니다 54 | > * 쉼표(,)를 사용하여 하나의 case에 여러 패턴을 명시할 수 있습니다 55 | 56 | ### switch 구문의 기본 형태 57 | 58 | ```swift 59 | switch 비교값 { 60 | case 패턴: 61 | /* 실행 구문 */ 62 | default: 63 | /* 실행 구문 */ 64 | } 65 | ``` 66 | 67 | ### switch 구문의 사용 68 | 69 | ```swift 70 | // 범위 연산자를 활용하면 더욱 쉽고 유용합니다 71 | switch someInteger { 72 | case 0: 73 | print("zero") 74 | case 1..<100: 75 | print("1~99") 76 | case 100: 77 | print("100") 78 | case 101...Int.max: 79 | print("over 100") 80 | default: 81 | print("unknown") 82 | } // 100 83 | 84 | // 정수 외의 대부분의 기본 타입을 사용할 수 있습니다 85 | switch "yagom" { 86 | case "jake": 87 | print("jake") 88 | case "mina": 89 | print("mina") 90 | case "yagom": 91 | print("yagom!!") 92 | default: 93 | print("unknown") 94 | } // yagom!! 95 | 96 | ``` 97 | 98 | > 기본 문법을 익힌 뒤 차후에 더 많은 switch 구문과 패턴의 활용에 대해 알아봅시다 99 | 100 | 101 | ## 관련문서 102 | 103 | * [The Swift Programming Language - Control Flow](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html) 104 | -------------------------------------------------------------------------------- /contents/05_conditional/conditional.swift: -------------------------------------------------------------------------------- 1 | /* 조건문 */ 2 | 3 | import Swift 4 | 5 | let someInteger = 100 6 | 7 | //MARK: - if-else 8 | 9 | //if <#condition#> { 10 | // <#statements#> 11 | //} else if <#condition#> { 12 | // <#statements#> 13 | //} else { 14 | // <#statements#> 15 | //} 16 | 17 | if someInteger < 100 { 18 | print("100 미만") 19 | } else if someInteger > 100 { 20 | print("100 초과") 21 | } else { 22 | print("100") 23 | } // 100 24 | 25 | // 스위프트의 조건에는 항상 Bool 타입이 들어와야합니다 26 | // someInteger는 Bool 타입이 아닌 Int 타입이기 때문에 27 | // 컴파일 오류가 발생합니다 28 | //if someInteger { } 29 | 30 | //MARK: - switch 31 | 32 | //switch <#value#> { 33 | //case <#pattern#>: 34 | // <#code#> 35 | //default: 36 | // <#code#> 37 | //} 38 | 39 | // 범위 연산자를 활용하면 더욱 쉽고 유용합니다 40 | switch someInteger { 41 | case 0: 42 | print("zero") 43 | case 1..<100: 44 | print("1~99") 45 | case 100: 46 | print("100") 47 | case 101...Int.max: 48 | print("over 100") 49 | default: 50 | print("unknown") 51 | } // 100 52 | 53 | // 정수 외의 대부분의 기본 타입을 사용할 수 있습니다 54 | switch "yagom" { 55 | case "jake": 56 | print("jake") 57 | case "mina": 58 | print("mina") 59 | case "yagom": 60 | print("yagom!!") 61 | default: 62 | print("unknown") 63 | } // yagom!! 64 | 65 | -------------------------------------------------------------------------------- /contents/06_loop/README.md: -------------------------------------------------------------------------------- 1 | # 반복문 2 | 3 | * for-in 4 | * while 5 | * repeat-while 6 | 7 | 8 | [![클릭하여 이동](http://img.youtube.com/vi/9QEiAzui0-s/0.jpg)](http://www.youtube.com/watch?v=9QEiAzui0-s "loop") 9 | 10 | [소스코드](loop.swift) 11 | 12 | 13 | ## for-in 구문 14 | 15 | 기존 언어의 for-each 구문과 유사합니다. Dictionary의 경우 이터레이션 아이템으로 튜플이 들어옵니다. 튜플에 관해서는 [Swift Language Guide의 Tuples 부분](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html)을 참고하면 되겠습니다. 16 | 17 | ### for-in 구문의 기본 형태 18 | 19 | ```swift 20 | for item in items { 21 | /* 실행 구문 */ 22 | } 23 | ``` 24 | 25 | ### for-in 구문의 사용 26 | ```swift 27 | var integers = [1, 2, 3] 28 | let people = ["yagom": 10, "eric": 15, "mike": 12] 29 | 30 | for integer in integers { 31 | print(integer) 32 | } 33 | 34 | // Dictionary의 item은 key와 value로 구성된 튜플 타입입니다 35 | for (name, age) in people { 36 | print("\(name): \(age)") 37 | } 38 | ``` 39 | 40 | ## while 구문 41 | 42 | ### while 구문의 기본 형태 43 | 44 | ```swift 45 | while 조건 { 46 | /* 실행 구문 */ 47 | } 48 | ``` 49 | 50 | ### while 구문의 사용 51 | 52 | ```swift 53 | while integers.count > 1 { 54 | integers.removeLast() 55 | } 56 | ``` 57 | 58 | ## repeat-while 구문 59 | 기존 언어의 do-while 구문과 형태 및 동작이 유사합니다 60 | 61 | ### repeat-while 구문의 기본 형태 62 | 63 | ```swift 64 | repeat { 65 | /* 실행 구문 */ 66 | } while 조건 67 | ``` 68 | 69 | ### repeat-while 구문의 사용 70 | 71 | ```swift 72 | repeat { 73 | integers.removeLast() 74 | } while integers.count > 0 75 | ``` 76 | 77 | 78 | ## 관련문서 79 | 80 | * [The Swift Programming Language - Control Flow](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html) -------------------------------------------------------------------------------- /contents/06_loop/loop.swift: -------------------------------------------------------------------------------- 1 | /* 반복문 */ 2 | 3 | import Swift 4 | 5 | var integers = [1, 2, 3] 6 | let people = ["yagom": 10, "eric": 15, "mike": 12] 7 | 8 | //MARK: - for-in 9 | 10 | //for <#item#> in <#items#> { 11 | // <#code#> 12 | //} 13 | 14 | for integer in integers { 15 | print(integer) 16 | } 17 | 18 | // Dictionary의 item은 key와 value로 구성된 튜플 타입입니다 19 | for (name, age) in people { 20 | print("\(name): \(age)") 21 | } 22 | 23 | 24 | 25 | //MARK: - while 26 | 27 | //while <#condition#> { 28 | // <#code#> 29 | //} 30 | 31 | while integers.count > 1 { 32 | integers.removeLast() 33 | } 34 | 35 | 36 | //MARK: - repeat-while 37 | 38 | //repeat { 39 | // <#code#> 40 | //} while <#condition#> 41 | 42 | repeat { 43 | integers.removeLast() 44 | } while integers.count > 0 45 | 46 | -------------------------------------------------------------------------------- /contents/07_optional/README.md: -------------------------------------------------------------------------------- 1 | # 옵셔널 2 | 3 | [![클릭하여 이동](http://img.youtube.com/vi/RxScvfe1dyU/0.jpg)](http://www.youtube.com/watch?v=RxScvfe1dyU "optional") 4 | 5 | [학습자료](optional.pdf), 6 | [소스코드](optional.swift) 7 | 8 | 9 | # 옵셔널 값 추출 10 | [![클릭하여 이동](http://img.youtube.com/vi/YBofMKyfDaQ/0.jpg)](http://www.youtube.com/watch?v=YBofMKyfDaQ "optional_unwrapping") 11 | 12 | [학습자료](optional_unwrapping.pdf), 13 | [소스코드](optional_unwrapping.swift) 14 | 15 | 16 | # 관련문서 17 | 18 | * [The Swift Programming Language - The Basics](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html) 19 | 20 | -------------------------------------------------------------------------------- /contents/07_optional/optional.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yagom/swift_basic/4db5b6c463c6a513fd2edb93fa760280155344a4/contents/07_optional/optional.pdf -------------------------------------------------------------------------------- /contents/07_optional/optional.swift: -------------------------------------------------------------------------------- 1 | /* 옵셔널 */ 2 | 3 | import Swift 4 | 5 | //MARK:- 암시적 추출 옵셔널 6 | // Implicitly Unwrapped Optional 7 | 8 | var implicitlyUnwrappedOptionalValue: Int! = 100 9 | 10 | switch implicitlyUnwrappedOptionalValue { 11 | case .none: 12 | print("This Optional variable is nil") 13 | case .some(let value): 14 | print("Value is \(value)") 15 | } 16 | 17 | // 기존 변수처럼 사용 가능 18 | implicitlyUnwrappedOptionalValue = implicitlyUnwrappedOptionalValue + 1 19 | 20 | // nil 할당 가능 21 | implicitlyUnwrappedOptionalValue = nil 22 | 23 | // 잘못된 접근으로 인한 런타임 오류 발생 24 | //implicitlyUnwrappedOptionalValue = implicitlyUnwrappedOptionalValue + 1 25 | 26 | 27 | //MARK:- 옵셔널 28 | // Optional 29 | 30 | var optionalValue: Int? = 100 31 | 32 | switch optionalValue { 33 | case .none: 34 | print("This Optional variable is nil") 35 | case .some(let value): 36 | print("Value is \(value)") 37 | } 38 | 39 | // nil 할당 가능 40 | optionalValue = nil 41 | 42 | // 기존 변수처럼 사용불가 - 옵셔널과 일반 값은 다른 타입이므로 연산불가 43 | //optionalValue = optionalValue + 1 44 | -------------------------------------------------------------------------------- /contents/07_optional/optional_unwrapping.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yagom/swift_basic/4db5b6c463c6a513fd2edb93fa760280155344a4/contents/07_optional/optional_unwrapping.pdf -------------------------------------------------------------------------------- /contents/07_optional/optional_unwrapping.swift: -------------------------------------------------------------------------------- 1 | /* 옵셔널 추출 */ 2 | 3 | // 옵셔널 바인딩 4 | // 강제 추출 5 | 6 | import Swift 7 | 8 | //MARK:- Optional Binding 9 | 10 | func printName(_ name: String) { 11 | print(name) 12 | } 13 | 14 | var myName: String? = nil 15 | 16 | //printName(myName) 17 | // 전달되는 값의 타입이 다르기 때문에 컴파일 오류발생 18 | 19 | if let name: String = myName { 20 | printName(name) 21 | } else { 22 | print("myName == nil") 23 | } 24 | 25 | 26 | var yourName: String! = nil 27 | 28 | if let name: String = yourName { 29 | printName(name) 30 | } else { 31 | print("yourName == nil") 32 | } 33 | 34 | // name 상수는 if-let 구문 내에서만 사용가능합니다 35 | // 상수 사용범위를 벗어났기 때문에 컴파일 오류 발생 36 | //printName(name) 37 | 38 | // ,를 사용해 한 번에 여러 옵셔널을 바인딩 할 수 있습니다 39 | // 모든 옵셔널에 값이 있을 때만 동작합니다 40 | myName = "yagom" 41 | yourName = nil 42 | 43 | if let name = myName, let friend = yourName { 44 | print("\(name) and \(friend)") 45 | } 46 | // yourName이 nil이기 때문에 실행되지 않습니다 47 | 48 | yourName = "hana" 49 | 50 | if let name = myName, let friend = yourName { 51 | print("\(name) and \(friend)") 52 | } 53 | // yagom and hana 54 | 55 | 56 | //MARK:- Force Unwrapping 57 | 58 | printName(myName!) // yagom 59 | 60 | myName = nil 61 | 62 | //print(myName!) 63 | // 강제추출시 값이 없으므로 런타임 오류 발생 64 | 65 | yourName = nil 66 | 67 | //printName(yourName) 68 | // nil 값이 전달되기 때문에 런타임 오류발생 69 | 70 | -------------------------------------------------------------------------------- /contents/08_struct/README.md: -------------------------------------------------------------------------------- 1 | # 구조체 2 | 3 | [![클릭하여 이동](http://img.youtube.com/vi/0ZF5lhpEcC8/0.jpg)](http://www.youtube.com/watch?v=0ZF5lhpEcC8 "struct") 4 | 5 | [소스코드](struct.swift) 6 | 7 | 8 | ## 정의 문법 9 | 스위프트 대부분의 타입은 구조체로 이루어져 있습니다. 구조체는 값 타입입니다. 타입이름은 대문자 카멜케이스를 사용하여 정의합니다. 10 | 11 | ```swift 12 | struct 이름 { 13 | /* 구현부 */ 14 | } 15 | ``` 16 | 17 | ### 프로퍼티 및 메서드 구현 18 | 19 | ```swift 20 | struct Sample { 21 | // 가변 프로퍼티 22 | var mutableProperty: Int = 100 23 | 24 | // 불변 프로퍼티 25 | let immutableProperty: Int = 100 26 | 27 | // 타입 프로퍼티 28 | static var typeProperty: Int = 100 29 | 30 | // 인스턴스 메서드 31 | func instanceMethod() { 32 | print("instance method") 33 | } 34 | 35 | // 타입 메서드 36 | static func typeMethod() { 37 | print("type method") 38 | } 39 | } 40 | 41 | ``` 42 | 43 | ### 구조체 사용 44 | ```swift 45 | // 가변 인스턴스 생성 46 | var mutable: Sample = Sample() 47 | 48 | mutable.mutableProperty = 200 49 | 50 | // 불변 프로퍼티는 인스턴스 생성 후 수정할 수 없습니다 51 | // 컴파일 오류 발생 52 | //mutable.immutableProperty = 200 53 | 54 | // 불변 인스턴스 55 | let immutable: Sample = Sample() 56 | 57 | // 불변 인스턴스는 아무리 가변 프로퍼티라도 58 | // 인스턴스 생성 후에 수정할 수 없습니다 59 | // 컴파일 오류 발생 60 | //immutable.mutableProperty = 200 61 | //immutable.immutableProperty = 200 62 | 63 | 64 | // 타입 프로퍼티 및 메서드 65 | Sample.typeProperty = 300 66 | Sample.typeMethod() // type method 67 | 68 | // 인스턴스에서는 타입 프로퍼티나 타입 메서드를 69 | // 사용할 수 없습니다 70 | // 컴파일 오류 발생 71 | //mutable.typeProperty = 400 72 | //mutable.typeMethod() 73 | ``` 74 | 75 | ## 학생 구조체 만들어보기 76 | 77 | ```swift 78 | struct Student { 79 | // 가변 프로퍼티 80 | var name: String = "unknown" 81 | 82 | // 키워드도 `로 묶어주면 이름으로 사용할 수 있습니다 83 | var `class`: String = "Swift" 84 | 85 | // 타입 메서드 86 | static func selfIntroduce() { 87 | print("학생타입입니다") 88 | } 89 | 90 | // 인스턴스 메서드 91 | // self는 인스턴스 자신을 지칭하며, 몇몇 경우를 제외하고 사용은 선택사항입니다 92 | func selfIntroduce() { 93 | print("저는 \(self.class)반 \(name)입니다") 94 | } 95 | } 96 | 97 | // 타입 메서드 사용 98 | Student.selfIntroduce() // 학생타입입니다 99 | 100 | // 가변 인스턴스 생성 101 | var yagom: Student = Student() 102 | yagom.name = "yagom" 103 | yagom.class = "스위프트" 104 | yagom.selfIntroduce() // 저는 스위프트반 yagom입니다 105 | 106 | // 불변 인스턴스 생성 107 | let jina: Student = Student() 108 | 109 | // 불변 인스턴스이므로 프로퍼티 값 변경 불가 110 | // 컴파일 오류 발생 111 | //jina.name = "jina" 112 | jina.selfIntroduce() // 저는 Swift반 unknown입니다 113 | 114 | ``` 115 | 116 | 117 | ## 관련문서 118 | 119 | * [The Swift Programming Language - Classes and Structures](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html) -------------------------------------------------------------------------------- /contents/08_struct/struct.swift: -------------------------------------------------------------------------------- 1 | /* 구조체 */ 2 | 3 | import Swift 4 | 5 | //MARK: - 정의 6 | 7 | //struct <#이름#> { 8 | // /* <#구현부#> */ 9 | //} 10 | 11 | //MARK: 프로퍼티 및 메서드 12 | 13 | struct Sample { 14 | // 가변 프로퍼티 15 | var mutableProperty: Int = 100 16 | 17 | // 불변 프로퍼티 18 | let immutableProperty: Int = 100 19 | 20 | // 타입 프로퍼티 21 | static var typeProperty: Int = 100 22 | 23 | // 인스턴스 메서드 24 | func instanceMethod() { 25 | print("instance method") 26 | } 27 | 28 | // 타입 메서드 29 | static func typeMethod() { 30 | print("type method") 31 | } 32 | } 33 | 34 | //MARK: 구조체 사용 35 | 36 | // 가변 인스턴스 37 | var mutable: Sample = Sample() 38 | 39 | // 불변 프로퍼티는 인스턴스 생성 후 수정할 수 없습니다 40 | // 컴파일 오류 발생 41 | //mutable.immutableProperty = 200 42 | 43 | // 불변 인스턴스 44 | let immutable: Sample = Sample() 45 | 46 | // 불변 인스턴스는 아무리 가변 프로퍼티라도 47 | // 인스턴스 생성 후에 수정할 수 없습니다 48 | // 컴파일 오류 발생 49 | //immutable.immutableProperty = 200 50 | 51 | 52 | // 타입 프로퍼티 및 메서드 53 | Sample.typeProperty = 300 54 | Sample.typeMethod() // type method 55 | 56 | // 인스턴스에서는 타입 프로퍼티나 타입 메서드를 57 | // 사용할 수 없습니다 58 | // 컴파일 오류 발생 59 | //mutable.typeProperty = 400 60 | //mutable.typeMethod() 61 | 62 | 63 | //MARK: - 학생 구조체 64 | 65 | struct Student { 66 | // 가변 프로퍼티 67 | var name: String = "unknown" 68 | 69 | // 키워드도 `로 묶어주면 이름으로 사용할 수 있습니다 70 | var `class`: String = "Swift" 71 | 72 | // 타입 메서드 73 | static func selfIntroduce() { 74 | print("학생타입입니다") 75 | } 76 | 77 | // 인스턴스 메서드 78 | // self는 인스턴스 자신을 지칭하며, 몇몇 경우를 제외하고 사용은 선택사항입니다 79 | func selfIntroduce() { 80 | print("저는 \(self.class)반 \(name)입니다") 81 | } 82 | } 83 | 84 | // 타입 메서드 사용 85 | Student.selfIntroduce() // 학생타입입니다 86 | 87 | // 가변 인스턴스 생성 88 | var yagom: Student = Student() 89 | yagom.name = "yagom" 90 | yagom.class = "스위프트" 91 | yagom.selfIntroduce() // 저는 스위프트반 yagom입니다 92 | 93 | // 불변 인스턴스 생성 94 | let jina: Student = Student() 95 | 96 | // 불변 인스턴스이므로 프로퍼티 값 변경 불가 97 | // 컴파일 오류 발생 98 | //jina.name = "jina" 99 | jina.selfIntroduce() // 저는 Swift반 unknown입니다 100 | -------------------------------------------------------------------------------- /contents/09_class/README.md: -------------------------------------------------------------------------------- 1 | # 클래스 2 | 3 | [![클릭하여 이동](http://img.youtube.com/vi/mV3wZ0tM_bk/0.jpg)](http://www.youtube.com/watch?v=mV3wZ0tM_bk "class") 4 | 5 | [소스코드](class.swift) 6 | 7 | ## 정의 문법 8 | 클래스는 참조 타입입니다. 타입이름은 대문자 카멜케이스를 사용하여 정의합니다. 9 | 10 | ```swift 11 | class 이름 { 12 | /* 구현부 */ 13 | } 14 | ``` 15 | 16 | ### 프로퍼티 및 메서드 구현 17 | 18 | 클래스의 타입 메서드는 두 종류가 있습니다. 상속 후 재정의가 가능한 class 타입메서드, 상속 후 재정의가 불가능한 static 타입메서드가 있습니다. 자세한 내용은 상속 부분에서 다시 다룹니다. 19 | 20 | ```swift 21 | class Sample { 22 | // 가변 프로퍼티 23 | var mutableProperty: Int = 100 24 | 25 | // 불변 프로퍼티 26 | let immutableProperty: Int = 100 27 | 28 | // 타입 프로퍼티 29 | static var typeProperty: Int = 100 30 | 31 | // 인스턴스 메서드 32 | func instanceMethod() { 33 | print("instance method") 34 | } 35 | 36 | // 타입 메서드 37 | // 재정의 불가 타입 메서드 - static 38 | static func typeMethod() { 39 | print("type method - static") 40 | } 41 | 42 | // 재정의 가능 타입 메서드 - class 43 | class func classMethod() { 44 | print("type method - class") 45 | } 46 | } 47 | 48 | ``` 49 | 50 | ### 클래스 사용 51 | ```swift 52 | 53 | // 인스턴스 생성 - 참조정보 수정 가능 54 | var mutableReference: Sample = Sample() 55 | 56 | mutableReference.mutableProperty = 200 57 | 58 | // 불변 프로퍼티는 인스턴스 생성 후 수정할 수 없습니다 59 | // 컴파일 오류 발생 60 | //mutableReference.immutableProperty = 200 61 | 62 | 63 | // 인스턴스 생성 - 참조정보 수정 불가 64 | let immutableReference: Sample = Sample() 65 | 66 | // 클래스의 인스턴스는 참조 타입이므로 let으로 선언되었더라도 인스턴스 프로퍼티의 값 변경이 가능합니다 67 | immutableReference.mutableProperty = 200 68 | 69 | // 다만 참조정보를 변경할 수는 없습니다 70 | // 컴파일 오류 발생 71 | //immutableReference = mutableReference 72 | 73 | // 참조 타입이라도 불변 인스턴스는 74 | // 인스턴스 생성 후에 수정할 수 없습니다 75 | // 컴파일 오류 발생 76 | //immutableReference.immutableProperty = 200 77 | 78 | 79 | // 타입 프로퍼티 및 메서드 80 | Sample.typeProperty = 300 81 | Sample.typeMethod() // type method 82 | 83 | // 인스턴스에서는 타입 프로퍼티나 타입 메서드를 84 | // 사용할 수 없습니다 85 | // 컴파일 오류 발생 86 | //mutableReference.typeProperty = 400 87 | //mutableReference.typeMethod() 88 | 89 | ``` 90 | 91 | ## 학생 클래스 만들어보기 92 | 93 | ```swift 94 | class Student { 95 | // 가변 프로퍼티 96 | var name: String = "unknown" 97 | 98 | // 키워드도 `로 묶어주면 이름으로 사용할 수 있습니다 99 | var `class`: String = "Swift" 100 | 101 | // 타입 메서드 102 | class func selfIntroduce() { 103 | print("학생타입입니다") 104 | } 105 | 106 | // 인스턴스 메서드 107 | // self는 인스턴스 자신을 지칭하며, 몇몇 경우를 제외하고 사용은 선택사항입니다 108 | func selfIntroduce() { 109 | print("저는 \(self.class)반 \(name)입니다") 110 | } 111 | } 112 | 113 | // 타입 메서드 사용 114 | Student.selfIntroduce() // 학생타입입니다 115 | 116 | // 인스턴스 생성 117 | var yagom: Student = Student() 118 | yagom.name = "yagom" 119 | yagom.class = "스위프트" 120 | yagom.selfIntroduce() // 저는 스위프트반 yagom입니다 121 | 122 | // 인스턴스 생성 123 | let jina: Student = Student() 124 | jina.name = "jina" 125 | jina.selfIntroduce() // 저는 Swift반 jina입니다 126 | 127 | ``` 128 | 129 | 130 | ## 관련문서 131 | 132 | * [The Swift Programming Language - Classes and Structures](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html) -------------------------------------------------------------------------------- /contents/09_class/class.swift: -------------------------------------------------------------------------------- 1 | /* 클래스 */ 2 | 3 | import Swift 4 | 5 | //MARK: - 정의 6 | 7 | //class <#이름#> { 8 | // /* <#구현부#> */ 9 | //} 10 | 11 | //MARK: 프로퍼티 및 메서드 12 | 13 | class Sample { 14 | // 가변 프로퍼티 15 | var mutableProperty: Int = 100 16 | 17 | // 불변 프로퍼티 18 | let immutableProperty: Int = 100 19 | 20 | // 타입 프로퍼티 21 | static var typeProperty: Int = 100 22 | 23 | // 인스턴스 메서드 24 | func instanceMethod() { 25 | print("instance method") 26 | } 27 | 28 | // 타입 메서드 29 | // 재정의 불가 타입 메서드 - static 30 | static func typeMethod() { 31 | print("type method - static") 32 | } 33 | 34 | // 재정의 가능 타입 메서드 - class 35 | class func classMethod() { 36 | print("type method - class") 37 | } 38 | } 39 | 40 | 41 | //MARK: 클래스 사용 42 | 43 | // 인스턴스 생성 - 참조정보 수정 가능 44 | var mutableReference: Sample = Sample() 45 | 46 | mutableReference.mutableProperty = 200 47 | 48 | // 불변 프로퍼티는 인스턴스 생성 후 수정할 수 없습니다 49 | // 컴파일 오류 발생 50 | //mutableReference.immutableProperty = 200 51 | 52 | 53 | // 인스턴스 생성 - 참조정보 수정 불가 54 | let immutableReference: Sample = Sample() 55 | 56 | // 클래스의 인스턴스는 참조 타입이므로 let으로 선언되었더라도 57 | // 인스턴스 프로퍼티의 값 변경이 가능합니다 58 | immutableReference.mutableProperty = 200 59 | 60 | // 다만 참조정보를 변경할 수는 없습니다 61 | // 컴파일 오류 발생 62 | //immutableReference = mutableReference 63 | 64 | // 참조 타입이라도 불변 인스턴스는 65 | // 인스턴스 생성 후에 수정할 수 없습니다 66 | // 컴파일 오류 발생 67 | //immutableReference.immutableProperty = 200 68 | 69 | 70 | // 타입 프로퍼티 및 메서드 71 | Sample.typeProperty = 300 72 | Sample.typeMethod() // type method 73 | 74 | // 인스턴스에서는 타입 프로퍼티나 타입 메서드를 75 | // 사용할 수 없습니다 76 | // 컴파일 오류 발생 77 | //mutableReference.typeProperty = 400 78 | //mutableReference.typeMethod() 79 | 80 | 81 | //MARK: - 학생 클래스 82 | 83 | class Student { 84 | // 가변 프로퍼티 85 | var name: String = "unknown" 86 | 87 | // 키워드도 `로 묶어주면 이름으로 사용할 수 있습니다 88 | var `class`: String = "Swift" 89 | 90 | // 타입 메서드 91 | 92 | class func selfIntroduce() { 93 | print("학생타입입니다") 94 | } 95 | 96 | // 인스턴스 메서드 97 | // self는 인스턴스 자신을 지칭하며, 몇몇 경우를 제외하고 사용은 선택사항입니다 98 | func selfIntroduce() { 99 | print("저는 \(self.class)반 \(name)입니다") 100 | } 101 | } 102 | 103 | // 타입 메서드 사용 104 | Student.selfIntroduce() // 학생타입입니다 105 | 106 | // 인스턴스 생성 107 | var yagom: Student = Student() 108 | yagom.name = "yagom" 109 | yagom.class = "스위프트" 110 | yagom.selfIntroduce() // 저는 스위프트반 yagom입니다 111 | 112 | // 인스턴스 생성 113 | let jina: Student = Student() 114 | jina.name = "jina" 115 | jina.selfIntroduce() // 저는 Swift반 jina입니다 116 | -------------------------------------------------------------------------------- /contents/10_enum/README.md: -------------------------------------------------------------------------------- 1 | # 열거형 2 | 3 | [![클릭하여 이동](http://img.youtube.com/vi/uKJiaYYNJN4/0.jpg)](http://www.youtube.com/watch?v=uKJiaYYNJN4 "enum") 4 | 5 | [소스코드](enum.swift) 6 | 7 | 8 | ## 정의 문법 9 | 10 | 스위프트의 열거형은 다른 언어의 열거형과는 많이 다릅니다. 잘 살펴보아야 할 스위프트의 기능 중 하나입니다. 11 | 12 | * enum은 타입이므로 대문자 카멜케이스를 사용하여 이름을 정의합니다 13 | * 각 case는 소문자 카멜케이스로 정의합니다 14 | * 각 case는 그 자체가 고유의 값입니다 15 | * 각 케이스는 한 줄에 개별로도, 한 줄에 여러개도 정의할 수 있습니다 16 | 17 | ```swift 18 | enum 이름 { 19 | case 이름1 20 | case 이름2 21 | case 이름3, 이름4, 이름5 22 | // ... 23 | } 24 | ``` 25 | 26 | ### 열거형 사용 27 | 28 | ```swift 29 | enum Weekday { 30 | case mon 31 | case tue 32 | case wed 33 | case thu, fri, sat, sun 34 | } 35 | 36 | // 열거형 타입과 케이스를 모두 사용하여도 됩니다 37 | var day: Weekday = Weekday.mon 38 | 39 | // 타입이 명확하다면 .케이스 처럼 표현해도 무방합니다 40 | day = .tue 41 | 42 | print(day) // tue 43 | 44 | // switch의 비교값에 열거형 타입이 위치할 때 45 | // 모든 열거형 케이스를 포함한다면 46 | // default를 작성할 필요가 없습니다 47 | switch day { 48 | case .mon, .tue, .wed, .thu: 49 | print("평일입니다") 50 | case Weekday.fri: 51 | print("불금 파티!!") 52 | case .sat, .sun: 53 | print("신나는 주말!!") 54 | } 55 | // 평일입니다 56 | ``` 57 | 58 | ## 원시값 59 | 60 | C 언어의 enum처럼 정수값을 가질 수도 있습니다. rawValue를 사용하면 됩니다. 61 | `case별로 각각 다른 값을 가져야합니다` 62 | 63 | 64 | ```swift 65 | enum Fruit: Int { 66 | case apple = 0 67 | case grape = 1 68 | case peach 69 | 70 | // mango와 apple의 원시값이 같으므로 71 | // mango 케이스의 원시값을 0으로 정의할 수 없습니다 72 | // case mango = 0 73 | } 74 | 75 | print("Fruit.peach.rawValue == \(Fruit.peach.rawValue)") 76 | // Fruit.peach.rawValue == 2 77 | ``` 78 | 79 | 정수 타입 뿐만 아니라 `Hashable` 프로토콜을 따르는 모든 타입이 원시값의 타입으로 지정될 수 있습니다. 80 | 81 | ```swift 82 | enum School: String { 83 | case elementary = "초등" 84 | case middle = "중등" 85 | case high = "고등" 86 | case university 87 | } 88 | 89 | print("School.middle.rawValue == \(School.middle.rawValue)") 90 | // School.middle.rawValue == 중등 91 | 92 | // 열거형의 원시값 타입이 String일 때, 원시값이 지정되지 않았다면 93 | // case의 이름을 원시값으로 사용합니다 94 | print("School.university.rawValue == \(School.university.rawValue)") 95 | // School.middle.rawValue == university 96 | 97 | ``` 98 | 99 | ### 원시값을 통한 초기화 100 | rawValue를 통해 초기화 할 수 있습니다. rawValue가 case에 해당하지 않을 수 있으므로 rawValue를 통해 초기화 한 인스턴스는 옵셔널 타입입니다. 101 | 102 | ```swift 103 | // rawValue를 통해 초기화 한 열거형 값은 옵셔널 타입이므로 Fruit 타입이 아닙니다 104 | //let apple: Fruit = Fruit(rawValue: 0) 105 | let apple: Fruit? = Fruit(rawValue: 0) 106 | 107 | // if let 구문을 사용하면 rawValue에 해당하는 케이스를 곧바로 사용할 수 있습니다 108 | if let orange: Fruit = Fruit(rawValue: 5) { 109 | print("rawValue 5에 해당하는 케이스는 \(orange)입니다") 110 | } else { 111 | print("rawValue 5에 해당하는 케이스가 없습니다") 112 | } // rawValue 5에 해당하는 케이스가 없습니다 113 | ``` 114 | 115 | ## 메서드 116 | 스위프트의 열거형에는 메서드도 추가할 수 있습니다. 117 | 118 | ```swift 119 | enum Month { 120 | case dec, jan, feb 121 | case mar, apr, may 122 | case jun, jul, aug 123 | case sep, oct, nov 124 | 125 | func printMessage() { 126 | switch self { 127 | case .mar, .apr, .may: 128 | print("따스한 봄~") 129 | case .jun, .jul, .aug: 130 | print("여름 더워요~") 131 | case .sep, .oct, .nov: 132 | print("가을은 독서의 계절!") 133 | case .dec, .jan, .feb: 134 | print("추운 겨울입니다") 135 | } 136 | } 137 | } 138 | 139 | Month.mar.printMessage() 140 | // 따스한 봄~ 141 | ``` 142 | 143 | ## 관련문서 144 | 145 | * [The Swift Programming Language - Enumerations](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html) -------------------------------------------------------------------------------- /contents/10_enum/enum.swift: -------------------------------------------------------------------------------- 1 | /* 열거형 */ 2 | 3 | import Swift 4 | 5 | //MARK: - 정의 6 | 7 | //enum은 타입이므로 대문자 카멜케이스를 사용하여 이름을 정의합니다 8 | //각 case는 소문자 카멜케이스로 정의합니다 9 | //각 case는 그 자체가 고유의 값입니다 10 | 11 | //enum <#이름#> { 12 | // case <#이름1#> 13 | // case <#이름2#> 14 | // case <#이름3#>, <#이름4#>, <#이름5#> 15 | // ... 16 | //} 17 | 18 | //MARK: 열거형 사용 19 | 20 | enum Weekday { 21 | case mon 22 | case tue 23 | case wed 24 | case thu, fri, sat, sun 25 | } 26 | 27 | var day: Weekday = Weekday.mon 28 | day = .tue 29 | 30 | print(day) // tue 31 | 32 | switch day { 33 | case .mon, .tue, .wed, .thu: 34 | print("평일입니다") 35 | case Weekday.fri: 36 | print("불금 파티!!") 37 | case .sat, .sun: 38 | print("신나는 주말!!") 39 | } 40 | // 평일입니다 41 | 42 | 43 | //MARK: - 원시값 44 | 45 | //C 언어의 enum처럼 정수값을 가질 수도 있습니다 46 | //rawValue를 사용하면 됩니다 47 | //case별로 각각 다른 값을 가져야합니다 48 | 49 | enum Fruit: Int { 50 | case apple = 0 51 | case grape = 1 52 | case peach 53 | // case mango = 0 54 | } 55 | 56 | print("Fruit.peach.rawValue == \(Fruit.peach.rawValue)") 57 | // Fruit.peach.rawValue == 2 58 | 59 | //정수 타입 뿐만 아니라 Hashable 프로토콜을 따르는 모든 타입이 원시값의 타입으로 지정될 수 있습니다 60 | 61 | enum School: String { 62 | case elementary = "초등" 63 | case middle = "중등" 64 | case high = "고등" 65 | case university 66 | } 67 | 68 | print("School.middle.rawValue == \(School.middle.rawValue)") 69 | // School.middle.rawValue == 중등 70 | 71 | print("School.university.rawValue == \(School.university.rawValue)") 72 | // School.university.rawValue == university 73 | 74 | 75 | //MARK: 원시값을 통한 초기화 76 | 77 | //rawValue를 통해 초기화 할 수 있습니다 78 | //rawValue가 case에 해당하지 않을 수 있으므로 79 | //rawValue를 통해 초기화 한 인스턴스는 옵셔널 타입입니다 80 | 81 | //let apple: Fruit = Fruit(rawValue: 0) 82 | let apple: Fruit? = Fruit(rawValue: 0) 83 | 84 | if let orange: Fruit = Fruit(rawValue: 5) { 85 | print("rawValue 5에 해당하는 케이스는 \(orange)입니다") 86 | } else { 87 | print("rawValue 5에 해당하는 케이스가 없습니다") 88 | } // rawValue 5에 해당하는 케이스가 없습니다 89 | 90 | 91 | 92 | //MARK: 메서드 93 | 94 | enum Month { 95 | case dec, jan, feb 96 | case mar, apr, may 97 | case jun, jul, aug 98 | case sep, oct, nov 99 | 100 | func printMessage() { 101 | switch self { 102 | case .mar, .apr, .may: 103 | print("따스한 봄~") 104 | case .jun, .jul, .aug: 105 | print("여름 더워요~") 106 | case .sep, .oct, .nov: 107 | print("가을은 독서의 계절!") 108 | case .dec, .jan, .feb: 109 | print("추운 겨울입니다") 110 | } 111 | } 112 | } 113 | 114 | Month.mar.printMessage() 115 | // 따스한 봄~ 116 | -------------------------------------------------------------------------------- /contents/11_value_reference/README.md: -------------------------------------------------------------------------------- 1 | # Class vs Struct/Enum 2 | 3 | 4 | [![클릭하여 이동](http://img.youtube.com/vi/itMj3tVyrWo/0.jpg)](http://www.youtube.com/watch?v=itMj3tVyrWo "value_reference") 5 | 6 | [소스코드](value_reference.swift), [학습자료](value_reference.pdf) 7 | 8 | 9 | 열거형과 구조체는 값 타입이며, 클래스는 참조 타입이라는 것이 가장 큰 차이입니다. 또한, 클래스는 상속이 가능하지만 구조체와 열거형은 상속이 불가능합니다. 10 | 11 | ```swift 12 | struct ValueType { 13 | var property = 1 14 | } 15 | 16 | class ReferenceType { 17 | var property = 1 18 | } 19 | 20 | // 첫 번째 구조체 인스턴스 21 | let firstStructInstance = ValueType() 22 | // 두 번째 구조체 인스턴스에 첫 번째 인스턴스 값 복사 23 | var secondStructInstance = firstStructInstance 24 | // 두 번째 구조체 인스턴스 프로퍼티 값 수정 25 | secondStructInstance.property = 2 26 | 27 | // 두 번째 구조체 인스턴스는 첫 번째 구조체를 똑같이 복사한 28 | // 별도의 인스턴스이기 때문에 29 | // 두 번째 구조체 인스턴스의 프로퍼티 값을 변경해도 30 | // 첫 번째 구조체 인스턴스의 프로퍼티 값에는 영향이 없음 31 | print("first struct instance property : \(firstStructInstance.property)") // 1 32 | print("second struct instance property : \(secondStructInstance.property)") // 2 33 | 34 | 35 | // 클래스 인스턴스 생성 후 첫 번째 참조 생성 36 | let firstClassReference = ReferenceType() 37 | // 두 번째 참조 변수에 첫 번째 참조 할당 38 | let secondClassReference = firstClassReference 39 | secondClassReference.property = 2 40 | 41 | // 두 번째 클래스 참조는 첫 번째 클래스 인스턴스를 참조하기 때문에 42 | // 두 번째 참조를 통해 인스턴스의 프로퍼티 값을 변경하면 43 | // 첫 번째 클래스 인스턴스의 프로퍼티 값을 변경하게 됨 44 | print("first class reference property : \(firstClassReference.property)") // 2 45 | print("second class reference property : \(secondClassReference.property)") // 2 46 | ``` 47 | 48 | ## 관련문서 49 | 50 | * [The Swift Programming Language - Enumerations](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html) 51 | * [The Swift Programming Language - Classes and Structures](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html) -------------------------------------------------------------------------------- /contents/11_value_reference/value_reference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yagom/swift_basic/4db5b6c463c6a513fd2edb93fa760280155344a4/contents/11_value_reference/value_reference.pdf -------------------------------------------------------------------------------- /contents/11_value_reference/value_reference.swift: -------------------------------------------------------------------------------- 1 | /* 클래스, 구조체/열거형 비교 */ 2 | 3 | import Swift 4 | 5 | //MARK:- Class vs Struct/Enum 6 | 7 | struct ValueType { 8 | var property = 1 9 | } 10 | 11 | class ReferenceType { 12 | var property = 1 13 | } 14 | 15 | let firstStructInstance = ValueType() 16 | var secondStructInstance = firstStructInstance 17 | secondStructInstance.property = 2 18 | 19 | print("first struct instance property : \(firstStructInstance.property)") // 1 20 | print("second struct instance property : \(secondStructInstance.property)") // 2 21 | 22 | 23 | let firstClassReference = ReferenceType() 24 | var secondClassReference = firstClassReference 25 | secondClassReference.property = 2 26 | 27 | print("first class reference property : \(firstClassReference.property)") // 2 28 | print("second class reference property : \(secondClassReference.property)") // 2 29 | -------------------------------------------------------------------------------- /contents/12_closure/README.md: -------------------------------------------------------------------------------- 1 | # 클로저 2 | 3 | 클로저는 코드의 블럭입니다. [일급시민(first-citizen)](https://ko.wikipedia.org/wiki/일급_객체)으로, 전달인자, 변수, 상수 등으로 저장, 전달이 가능합니다. 4 | 함수는 클로저의 일종으로, `이름이 있는 클로저`라고 생각하면 됩니다. 5 | 6 | 7 | [![클릭하여 이동](http://img.youtube.com/vi/Ix9gGuupjBU/0.jpg)](http://www.youtube.com/watch?v=Ix9gGuupjBU "closure") 8 | 9 | [소스코드](closure.swift) 10 | 11 | 12 | ## 기본 클로저 문법 13 | 14 | ```swift 15 | { (매개변수 목록) -> 반환타입 in 16 | 실행 코드 17 | } 18 | ``` 19 | ## 클로저의 사용 20 | ```swift 21 | // sum이라는 상수에 클로저를 할당 22 | let sum: (Int, Int) -> Int = { (a: Int, b: Int) in 23 | return a + b 24 | } 25 | 26 | let sumResult: Int = sum(1, 2) 27 | print(sumResult) // 3 28 | ``` 29 | 30 | ## 함수의 전달인자로서의 클로저 31 | 32 | 클로저는 주로 함수의 전달인자로 많이 사용됩니다. 함수 내부에서 원하는 코드블럭을 실행할 수 있습니다. 33 | 34 | ```swift 35 | let add: (Int, Int) -> Int 36 | add = { (a: Int, b: Int) in 37 | return a + b 38 | } 39 | 40 | let substract: (Int, Int) -> Int 41 | substract = { (a: Int, b: Int) in 42 | return a - b 43 | } 44 | 45 | let divide: (Int, Int) -> Int 46 | divide = { (a: Int, b: Int) in 47 | return a / b 48 | } 49 | 50 | func calculate(a: Int, b: Int, method: (Int, Int) -> Int) -> Int { 51 | return method(a, b) 52 | } 53 | 54 | var calculated: Int 55 | 56 | calculated = calculate(a: 50, b: 10, method: add) 57 | 58 | print(calculated) // 60 59 | 60 | calculated = calculate(a: 50, b: 10, method: substract) 61 | 62 | print(calculated) // 40 63 | 64 | calculated = calculate(a: 50, b: 10, method: divide) 65 | 66 | print(calculated) // 5 67 | 68 | //따로 클로저를 상수/변수에 넣어 전달하지 않고, 69 | //함수를 호출할 때 클로저를 작성하여 전달할 수도 있습니다. 70 | 71 | calculated = calculate(a: 50, b: 10, method: { (left: Int, right: Int) -> Int in 72 | return left * right 73 | }) 74 | 75 | print(calculated) // 500 76 | 77 | ``` 78 | 79 | 80 | # 다양한 클로저표현 81 | 클로저는 다양한 모습으로 표현될 수 있습니다. 82 | 함수의 매개변수 마지막으로 전달되는 클로저는 `후행클로저(trailing closure)`로 함수 밖에 구현할 수 있습니다. 83 | 84 | 컴파일러가 클로저의 타입을 유추할 수 있는 경우 매개변수, 반환 타입을 생략할 수 있습니다. 85 | 86 | 반환 값이 있는 경우, 암시적으로 클로저의 맨 마지막 줄은 `return` 키워드를 생략하더라도 반환 값으로 취급합니다. 87 | 88 | 전달인자의 이름이 굳이 필요없고, 컴파일러가 타입을 유추할 수 있는 경우 축약된 전달인자 이름(`$0`, `$1`, `$2`...)을 사용 할 수 있습니다. 89 | 90 | 91 | [![클릭하여 이동](http://img.youtube.com/vi/WvqYKP6VgNQ/0.jpg)](http://www.youtube.com/watch?v=WvqYKP6VgNQ "closure_advanced") 92 | 93 | [소스코드](closure.swift) 94 | 95 | 96 | ----- 97 | 98 | 클로저 매개변수를 갖는 함수 `calculate(a:b:method:)`와 결과값을 저장할 변수 `result`를 먼저 선언해둡니다. 99 | 100 | ```swift 101 | func calculate(a: Int, b: Int, method: (Int, Int) -> Int) -> Int { 102 | return method(a, b) 103 | } 104 | 105 | var result: Int 106 | ``` 107 | 108 | ## 후행 클로저 109 | 클로저가 함수의 마지막 전달인자라면 마지막 매개변수 이름을 생략한 후 함수 소괄호 외부에 클로저를 구현할 수 있습니다. 110 | 111 | ```swift 112 | result = calculate(a: 10, b: 10) { (left: Int, right: Int) -> Int in 113 | return left + right 114 | } 115 | 116 | print(result) // 20 117 | ``` 118 | 119 | ## 반환타입 생략 120 | `calculate(a:b:method:)` 함수의 `method` 매개변수는 `Int` 타입을 반환할 것이라는 사실을 컴파일러도 알기 때문에 굳이 클로저에서 반환타입을 명시해 주지 않아도 됩니다. 121 | 대신 `in 키워드는 생략할 수 없습니다` 122 | 123 | ```swift 124 | result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) in 125 | return left + right 126 | }) 127 | 128 | print(result) // 20 129 | 130 | // 후행클로저와 함께 사용할 수도 있습니다 131 | result = calculate(a: 10, b: 10) { (left: Int, right: Int) in 132 | return left + right 133 | } 134 | 135 | print(result) // 20 136 | ``` 137 | 138 | ## 단축 인자이름 139 | 클로저의 매개변수 이름이 굳이 불필요하다면 단축 인자이름을 활용할 수 있습니다. 단축 인자이름은 클로저의 매개변수의 순서대로 `$0`, `$1`, `$2`... 처럼 표현합니다. 140 | 141 | ```swift 142 | result = calculate(a: 10, b: 10, method: { 143 | return $0 + $1 144 | }) 145 | 146 | print(result) // 20 147 | 148 | 149 | // 당연히 후행 클로저와 함께 사용할 수 있습니다 150 | result = calculate(a: 10, b: 10) { 151 | return $0 + $1 152 | } 153 | 154 | print(result) // 20 155 | ``` 156 | 157 | ## 암시적 반환 표현 158 | 클로저가 반환하는 값이 있다면 클로저의 마지막 줄의 결과값은 암시적으로 반환값으로 취급합니다. 159 | 160 | ```swift 161 | result = calculate(a: 10, b: 10) { 162 | $0 + $1 163 | } 164 | 165 | print(result) // 20 166 | 167 | // 간결하게 한 줄로 표현해 줄 수도 있습니다 168 | result = calculate(a: 10, b: 10) { $0 + $1 } 169 | 170 | print(result) // 20 171 | ``` 172 | 173 | ## 축약 전과 후 비교 174 | 175 | ```swift 176 | //축약 전 177 | result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) -> Int in 178 | return left + right 179 | }) 180 | 181 | //축약 후 182 | result = calculate(a: 10, b: 10) { $0 + $1 } 183 | 184 | print(result) // 20 185 | ``` 186 | 187 | ## 관련문서 188 | 189 | * [The Swift Programming Language - Closures](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html) 190 | * [일급시민(first-citizen)](https://ko.wikipedia.org/wiki/일급_객체) -------------------------------------------------------------------------------- /contents/12_closure/closure.swift: -------------------------------------------------------------------------------- 1 | /* 클로저 */ 2 | 3 | import Swift 4 | 5 | //코드의 블럭 6 | //일급 시민(first-citizen) 7 | //변수, 상수 등으로 저장, 전달인자로 전달이 가능 8 | //함수 : 이름이 있는 클로저 9 | 10 | //MARK: - 정의 11 | 12 | //{ (<#매개변수 목록#>) -> <#반환타입#> in 13 | // <#실행 코드#> 14 | //} 15 | 16 | // 함수를 사용한다면 17 | func sumFunction(a: Int, b: Int) -> Int { 18 | return a + b 19 | } 20 | 21 | var sumResult: Int = sumFunction(a: 1, b: 2) 22 | 23 | print(sumResult) // 3 24 | 25 | // 클로저의 사용 26 | var sum: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in 27 | return a + b 28 | } 29 | 30 | sumResult = sum(1, 2) 31 | print(sumResult) // 3 32 | 33 | // 함수는 클로저의 일종이므로 34 | // sum 변수에는 당연히 함수도 할당할 수 있습니다 35 | sum = sumFunction(a:b:) 36 | 37 | sumResult = sum(1, 2) 38 | print(sumResult) // 3 39 | 40 | 41 | 42 | //MARK: - 함수의 전달인자로서의 클로저 43 | 44 | let add: (Int, Int) -> Int 45 | add = { (a: Int, b: Int) -> Int in 46 | return a + b 47 | } 48 | 49 | let substract: (Int, Int) -> Int 50 | substract = { (a: Int, b: Int) -> Int in 51 | return a - b 52 | } 53 | 54 | let divide: (Int, Int) -> Int 55 | divide = { (a: Int, b: Int) -> Int in 56 | return a / b 57 | } 58 | 59 | func calculate(a: Int, b: Int, method: (Int, Int) -> Int) -> Int { 60 | return method(a, b) 61 | } 62 | 63 | var calculated: Int 64 | 65 | calculated = calculate(a: 50, b: 10, method: add) 66 | 67 | print(calculated) // 60 68 | 69 | calculated = calculate(a: 50, b: 10, method: substract) 70 | 71 | print(calculated) // 40 72 | 73 | calculated = calculate(a: 50, b: 10, method: divide) 74 | 75 | print(calculated) // 5 76 | 77 | calculated = calculate(a: 50, b: 10, method: { (left: Int, right: Int) -> Int in 78 | return left * right 79 | }) 80 | 81 | print(calculated) // 500 82 | 83 | 84 | /* 클로저 고급 */ 85 | 86 | var result: Int 87 | 88 | //MARK: - 후행 클로저 89 | // 클로저가 함수의 마지막 전달인자라면 90 | // 마지막 매개변수 이름을 생략한 후 91 | // 함수 소괄호 외부에 클로저를 구현할 수 있습니다 92 | 93 | result = calculate(a: 10, b: 10) { (left: Int, right: Int) -> Int in 94 | return left + right 95 | } 96 | 97 | print(result) // 20 98 | 99 | 100 | //MARK: - 반환타입 생략 101 | // calculate 함수의 method 매개변수는 102 | // Int 타입을 반환할 것이라는 사실을 컴파일러도 알기 때문에 103 | // 굳이 클로저에서 반환타입을 명시해 주지 않아도 됩니다 104 | // 대신 in 키워드는 생략할 수 없습니다 105 | 106 | result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) in 107 | return left + right 108 | }) 109 | 110 | print(result) // 20 111 | 112 | // 후행클로저와 함께 사용할 수도 있습니다 113 | result = calculate(a: 10, b: 10) { (left: Int, right: Int) in 114 | return left + right 115 | } 116 | 117 | //MARK: - 단축 인자이름 118 | // 클로저의 매개변수 이름이 굳이 불필요하다면 단축 인자이름을 활용할 수 있습니다 119 | // 단축 인자이름은 클로저의 매개변수의 순서대로 $0, $1... 처럼 표현합니다 120 | 121 | result = calculate(a: 10, b: 10, method: { 122 | return $0 + $1 123 | }) 124 | 125 | print(result) // 20 126 | 127 | // 당연히 후행 클로저와 함께 사용할 수 있습니다 128 | result = calculate(a: 10, b: 10) { 129 | return $0 + $1 130 | } 131 | 132 | print(result) // 20 133 | 134 | 135 | //MARK: - 암시적 반환 표현 136 | // 클로저가 반환하는 값이 있다면 137 | // 클로저의 마지막 줄의 결과값은 암시적으로 반환값으로 취급합니다 138 | 139 | result = calculate(a: 10, b: 10) { 140 | $0 + $1 141 | } 142 | 143 | print(result) // 20 144 | 145 | // 간결하게 한 줄로 표현해 줄 수도 있습니다 146 | result = calculate(a: 10, b: 10) { $0 + $1 } 147 | 148 | print(result) // 20 149 | 150 | // 축약하지 않은 클로저 문법과 축약 후의 문법 비교 151 | 152 | result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) -> Int in 153 | return left + right 154 | }) 155 | 156 | result = calculate(a: 10, b: 10) { $0 + $1 } 157 | 158 | print(result) // 20 159 | 160 | -------------------------------------------------------------------------------- /contents/13_property/README.md: -------------------------------------------------------------------------------- 1 | # 프로퍼티 2 | 3 | 프로퍼티는 클래스, 구조체, 열거형과 연관된 값입니다. 타입과 관련된 값을 저장할 수도, 연산할 수도 있습니다. 4 | 5 | 6 | [![클릭하여 이동](http://img.youtube.com/vi/aVGgy29-gOY/0.jpg)](http://www.youtube.com/watch?v=aVGgy29-gOY "property") 7 | 8 | [소스코드](closure.swift) 9 | 10 | 11 | ### 프로퍼티의 종류 12 | * 인스턴스 저장 프로퍼티 13 | * 타입 저장 프로퍼티 14 | * 인스턴스 연산 프로퍼티 15 | * 타입 연산 프로퍼티 16 | * 지연 저장 프로퍼티 17 | 18 | 이번 파트에서는 지연 저장 프로퍼티를 제외한 저장 프로퍼티와 연산 프로퍼티에 대해 알아봅니다. 19 | 20 | 21 | 22 | ## 정의와 사용 23 | 24 | 프로퍼티는 구조체, 클래스, 열거형 내부에 구현할 수 있습니다. 다만 열거형 내부에는 연산 프로퍼티만 구현할 수 있습니다. 연산 프로퍼티는 `var`로만 선언할 수 있습니다. 25 | 연산프로퍼티를 읽기전용으로는 구현할 수 있지만, 쓰기 전용으로는 구현할 수 없습니다. 읽기전용으로 구현하려면 `get` 블럭만 작성해주면 됩니다. 읽기전용은 `get`블럭을 생략할 수 있습니다. 읽기, 쓰기 모두 가능하게 하려면 `get` 블럭과 `set`블럭을 모두 구현해주면 됩니다. 26 | `set` 블럭에서 암시적 매개변수 `newValue`를 사용할 수 있습니다. 27 | 28 | ```swift 29 | struct Student { 30 | 31 | // 인스턴스 저장 프로퍼티 32 | var name: String = "" 33 | var `class`: String = "Swift" 34 | var koreanAge: Int = 0 35 | 36 | // 인스턴스 연산 프로퍼티 37 | var westernAge: Int { 38 | get { 39 | return koreanAge - 1 40 | } 41 | 42 | set(inputValue) { 43 | koreanAge = inputValue + 1 44 | } 45 | } 46 | 47 | // 타입 저장 프로퍼티 48 | static var typeDescription: String = "학생" 49 | 50 | /* 51 | // 인스턴스 메서드 52 | func selfIntroduce() { 53 | print("저는 \(self.class)반 \(name)입니다") 54 | } 55 | */ 56 | 57 | // 읽기전용 인스턴스 연산 프로퍼티 58 | // 간단히 위의 selfIntroduce() 메서드를 대체할 수 있습니다 59 | var selfIntroduction: String { 60 | get { 61 | return "저는 \(self.class)반 \(name)입니다" 62 | } 63 | } 64 | 65 | /* 66 | // 타입 메서드 67 | static func selfIntroduce() { 68 | print("학생타입입니다") 69 | } 70 | */ 71 | 72 | // 읽기전용 타입 연산 프로퍼티 73 | // 읽기전용에서는 get을 생략할 수 있습니다 74 | static var selfIntroduction: String { 75 | return "학생타입입니다" 76 | } 77 | } 78 | 79 | // 타입 연산 프로퍼티 사용 80 | print(Student.selfIntroduction) 81 | // 학생타입입니다 82 | 83 | // 인스턴스 생성 84 | var yagom: Student = Student() 85 | yagom.koreanAge = 10 86 | 87 | // 인스턴스 저장 프로퍼티 사용 88 | yagom.name = "yagom" 89 | print(yagom.name) 90 | // yagom 91 | 92 | // 인스턴스 연산 프로퍼티 사용 93 | print(yagom.selfIntroduction) 94 | // 저는 Swift반 yagom입니다 95 | 96 | print("제 한국나이는 \(yagom.koreanAge)살이고, 미쿡나이는 \(yagom.westernAge)살입니다.") 97 | // 제 한국나이는 10살이고, 미쿡나이는 9살입니다. 98 | ``` 99 | 100 | ## 응용 101 | 102 | ```swift 103 | struct Money { 104 | var currencyRate: Double = 1100 105 | var dollar: Double = 0 106 | var won: Double { 107 | get { 108 | return dollar * currencyRate 109 | } 110 | set { 111 | dollar = newValue / currencyRate 112 | } 113 | } 114 | } 115 | 116 | var moneyInMyPocket = Money() 117 | 118 | moneyInMyPocket.won = 11000 119 | 120 | print(moneyInMyPocket.won) 121 | // 11000.0 122 | 123 | moneyInMyPocket.dollar = 10 124 | 125 | print(moneyInMyPocket.won) 126 | // 11000.0 127 | 128 | ``` 129 | 130 | ## 지역변수 및 전역변수 131 | 저장 프로퍼티와 연산 프로퍼티의 기능은 함수, 메서드, 클로저, 타입 등의 외부에 위치한 지역/전역 변수에도 모두 사용 가능합니다. 132 | 133 | ```swift 134 | var a: Int = 100 135 | var b: Int = 200 136 | var sum: Int { 137 | return a + b 138 | } 139 | 140 | print(sum) // 300 141 | ``` 142 | 143 | # 프로퍼티 감시자 144 | 145 | 프로퍼티 감시자를 사용하면 프로퍼티 값이 변경될 때 원하는 동작을 수행할 수 있습니다. 값이 변경되기 직전에 `willSet`블럭이, 값이 변경된 직후에 `didSet`블럭이 호출됩니다. 둘 중 필요한 하나만 구현해 주어도 무관합니다. 변경되려는 값이 **기존 값과 똑같더라도** 프로퍼티 감시자는 항상 동작합니다. 146 | `willSet` 블럭에서 암시적 매개변수 `newValue`를 사용할 수 있고, `didSet` 블럭에서 암시적 매개변수 `oldValue`를 사용할 수 있습니다. 147 | 148 | 프로퍼티 감시자는 연산 프로퍼티에 사용할 수 없습니다. 149 | 150 | 151 | [![클릭하여 이동](http://img.youtube.com/vi/X7oreJGRuaU/0.jpg)](http://www.youtube.com/watch?v=X7oreJGRuaU "property_observer") 152 | 153 | [소스코드](closure.swift) 154 | 155 | 156 | ## 정의 및 사용 157 | 158 | ```swift 159 | struct Money { 160 | // 프로퍼티 감시자 사용 161 | var currencyRate: Double = 1100 { 162 | willSet(newRate) { 163 | print("환율이 \(currencyRate)에서 \(newRate)으로 변경될 예정입니다") 164 | } 165 | 166 | didSet(oldRate) { 167 | print("환율이 \(oldRate)에서 \(currencyRate)으로 변경되었습니다") 168 | } 169 | } 170 | 171 | // 프로퍼티 감시자 사용 172 | var dollar: Double = 0 { 173 | // willSet의 암시적 매개변수 이름 newValue 174 | willSet { 175 | print("\(dollar)달러에서 \(newValue)달러로 변경될 예정입니다") 176 | } 177 | 178 | // didSet의 암시적 매개변수 이름 oldValue 179 | didSet { 180 | print("\(oldValue)달러에서 \(dollar)달러로 변경되었습니다") 181 | } 182 | } 183 | 184 | // 연산 프로퍼티 185 | var won: Double { 186 | get { 187 | return dollar * currencyRate 188 | } 189 | set { 190 | dollar = newValue / currencyRate 191 | } 192 | 193 | /* 프로퍼티 감시자와 연산 프로퍼티 기능을 동시에 사용할 수 없습니다 194 | willSet { 195 | 196 | } 197 | */ 198 | } 199 | } 200 | 201 | var moneyInMyPocket: Money = Money() 202 | 203 | // 환율이 1100.0에서 1150.0으로 변경될 예정입니다 204 | moneyInMyPocket.currencyRate = 1150 205 | // 환율이 1100.0에서 1150.0으로 변경되었습니다 206 | 207 | // 0.0달러에서 10.0달러로 변경될 예정입니다 208 | moneyInMyPocket.dollar = 10 209 | // 0.0달러에서 10.0달러로 변경되었습니다 210 | 211 | print(moneyInMyPocket.won) 212 | // 11500.0 213 | 214 | ``` 215 | 216 | 217 | ## 관련문서 218 | 219 | * [The Swift Programming Language - Properties](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html) -------------------------------------------------------------------------------- /contents/13_property/property.swift: -------------------------------------------------------------------------------- 1 | /* 프로퍼티 */ 2 | 3 | /* 4 | 저장 프로퍼티(stored property) 5 | 연산 프로퍼티(computed property) 6 | 인스턴스 프로퍼티(instance property) 7 | 타입 프로퍼티(type property) 8 | */ 9 | 10 | import Swift 11 | 12 | //프로퍼티는 구조체, 클래스, 열거형 내부에 구현할 수 있습니다 13 | //다만 열거형 내부에는 연산 프로퍼티만 구현할 수 있습니다 14 | //연산 프로퍼티는 var로만 선언할 수 있습니다 15 | 16 | //MARK: - 정의 17 | 18 | struct Student { 19 | 20 | // 인스턴스 저장 프로퍼티 21 | var name: String = "" 22 | var `class`: String = "Swift" 23 | var koreanAge: Int = 0 24 | 25 | // 인스턴스 연산 프로퍼티 26 | var westernAge: Int { 27 | get { 28 | return koreanAge - 1 29 | } 30 | 31 | set(inputValue) { 32 | koreanAge = inputValue + 1 33 | } 34 | } 35 | 36 | // 타입 저장 프로퍼티 37 | static var typeDescription: String = "학생" 38 | 39 | /* 40 | // 인스턴스 메서드 41 | func selfIntroduce() { 42 | print("저는 \(self.class)반 \(name)입니다") 43 | } 44 | */ 45 | 46 | // 읽기전용 인스턴스 연산 프로퍼티 47 | var selfIntroduction: String { 48 | get { 49 | return "저는 \(self.class)반 \(name)입니다" 50 | } 51 | } 52 | 53 | /* 54 | // 타입 메서드 55 | static func selfIntroduce() { 56 | print("학생타입입니다") 57 | } 58 | */ 59 | 60 | // 읽기전용 타입 연산 프로퍼티 61 | // 읽기전용에서는 get을 생략할 수 있습니다 62 | static var selfIntroduction: String { 63 | return "학생타입입니다" 64 | } 65 | } 66 | 67 | //MARK: - 사용 68 | 69 | // 타입 연산 프로퍼티 사용 70 | print(Student.selfIntroduction) 71 | // 학생타입입니다 72 | 73 | // 인스턴스 생성 74 | var yagom: Student = Student() 75 | yagom.koreanAge = 10 76 | 77 | // 인스턴스 저장 프로퍼티 사용 78 | yagom.name = "yagom" 79 | print(yagom.name) 80 | // yagom 81 | 82 | // 인스턴스 연산 프로퍼티 사용 83 | print(yagom.selfIntroduction) 84 | // 저는 Swift반 yagom입니다 85 | 86 | print("제 한국나이는 \(yagom.koreanAge)살이고, 미쿡나이는 \(yagom.westernAge)살입니다.") 87 | // 제 한국나이는 10살이고, 미쿡나이는 9살입니다. 88 | 89 | 90 | //MARK: - 응용 91 | 92 | struct Money { 93 | var currencyRate: Double = 1100 94 | var dollar: Double = 0 95 | var won: Double { 96 | get { 97 | return dollar * currencyRate 98 | } 99 | set { 100 | dollar = newValue / currencyRate 101 | } 102 | } 103 | } 104 | 105 | var moneyInMyPocket = Money() 106 | 107 | moneyInMyPocket.won = 11000.0 108 | 109 | print(moneyInMyPocket.won) 110 | // 11000.0 111 | 112 | moneyInMyPocket.dollar = 10 113 | 114 | print(moneyInMyPocket.won) 115 | // 11000.0 116 | 117 | 118 | //MARK: - 지역변수, 전역변수 119 | 120 | //저장 프로퍼티와 연산 프로퍼티의 기능은 121 | //함수, 메서드, 클로저, 타입 등의 외부에 위치한 122 | //지역/전역 변수에도 모두 사용 가능합니다 123 | 124 | var a: Int = 100 125 | var b: Int = 200 126 | var sum: Int { 127 | return a + b 128 | } 129 | 130 | print(sum) // 300 131 | 132 | 133 | 134 | /* 프로퍼티 감시자 */ 135 | 136 | //프로퍼티 감시자를 사용하면 프로퍼티 값이 변경될 때 원하는 동작을 수행할 수 있습니다 137 | 138 | //MARK: - 정의 139 | 140 | struct MoneyAgain { 141 | // 프로퍼티 감시자 사용 142 | var currencyRate: Double = 1100 { 143 | willSet(newRate) { 144 | print("환율이 \(currencyRate)에서 \(newRate)으로 변경될 예정입니다") 145 | } 146 | 147 | didSet(oldRate) { 148 | print("환율이 \(oldRate)에서 \(currencyRate)으로 변경되었습니다") 149 | } 150 | } 151 | 152 | // 프로퍼티 감시자 사용 153 | var dollar: Double = 0 { 154 | // willSet의 암시적 매개변수 이름 newValue 155 | willSet { 156 | print("\(dollar)달러에서 \(newValue)달러로 변경될 예정입니다") 157 | } 158 | 159 | // didSet의 암시적 매개변수 이름 oldValue 160 | didSet { 161 | print("\(oldValue)달러에서 \(dollar)달러로 변경되었습니다") 162 | } 163 | } 164 | 165 | // 연산 프로퍼티 166 | var won: Double { 167 | get { 168 | return dollar * currencyRate 169 | } 170 | set { 171 | dollar = newValue / currencyRate 172 | } 173 | 174 | /* 프로퍼티 감시자와 연산 프로퍼티 기능을 동시에 사용할 수 없습니다 175 | willSet { 176 | 177 | } 178 | */ 179 | } 180 | } 181 | 182 | //MARK: - 사용 183 | 184 | var moneyInMyPocketAgain: MoneyAgain = MoneyAgain() 185 | 186 | // 환율이 1100.0에서 1150.0으로 변경될 예정입니다 187 | moneyInMyPocketAgain.currencyRate = 1150 188 | // 환율이 1100.0에서 1150.0으로 변경되었습니다 189 | 190 | // 0.0달러에서 10.0달러로 변경될 예정입니다 191 | moneyInMyPocketAgain.dollar = 10 192 | // 0.0달러에서 10.0달러로 변경되었습니다 193 | 194 | print(moneyInMyPocketAgain.won) 195 | // 11500.0 196 | 197 | 198 | //프로퍼티 감시자의 기능은 199 | //함수, 메서드, 클로저, 타입 등의 외부에 위치한 200 | //지역/전역 변수에도 모두 사용 가능합니다 201 | 202 | var c: Int = 100 { 203 | willSet { 204 | print("\(c)에서 \(newValue)로 변경될 예정입니다") 205 | } 206 | 207 | didSet { 208 | print("\(oldValue)에서 \(c)로 변경되었습니다") 209 | } 210 | } 211 | 212 | // 100에서 200로 변경될 예정입니다 213 | c = 200 214 | // 100에서 200로 변경되었습니다 215 | 216 | -------------------------------------------------------------------------------- /contents/14_inheritance/README.md: -------------------------------------------------------------------------------- 1 | # 상속 2 | 3 | 스위프트의 상속은 클래스, 프로토콜 등에서 가능합니다. 열거형, 구조체는 상속이 불가능합니다. 스위프트는 다중상속을 지원하지 않습니다. 4 | 이번 파트에서는 클래스의 상속에 대해서 알아봅니다 5 | 6 | 7 | [![클릭하여 이동](http://img.youtube.com/vi/T60mxyxxdys/0.jpg)](http://www.youtube.com/watch?v=T60mxyxxdys "inheritance") 8 | 9 | [소스코드](inheritance.swift) 10 | 11 | 12 | ## 클래스의 상속과 재정의 13 | 14 | ### 상속 문법 15 | 16 | ```swift 17 | class 이름: 상속받을 클래스 이름 { 18 | /* 구현부 */ 19 | } 20 | ``` 21 | 22 | ```swift 23 | // 기반 클래스 Person 24 | class Person { 25 | var name: String = "" 26 | 27 | func selfIntroduce() { 28 | print("저는 \(name)입니다") 29 | } 30 | 31 | // final 키워드를 사용하여 재정의를 방지할 수 있습니다 32 | final func sayHello() { 33 | print("hello") 34 | } 35 | 36 | // 타입 메서드 37 | // 재정의 불가 타입 메서드 - static 38 | static func typeMethod() { 39 | print("type method - static") 40 | } 41 | 42 | // 재정의 가능 타입 메서드 - class 43 | class func classMethod() { 44 | print("type method - class") 45 | } 46 | 47 | // 재정의 가능한 class 메서드라도 48 | // final 키워드를 사용하면 재정의 할 수 없습니다 49 | // 메서드 앞의 `static`과 `final class`는 똑같은 역할을 합니다 50 | final class func finalCalssMethod() { 51 | print("type method - final class") 52 | } 53 | } 54 | 55 | // Person을 상속받는 Student 56 | class Student: Person { 57 | var major: String = "" 58 | 59 | override func selfIntroduce() { 60 | print("저는 \(name)이고, 전공은 \(major)입니다") 61 | } 62 | 63 | override class func classMethod() { 64 | print("overriden type method - class") 65 | } 66 | 67 | // static을 사용한 타입 메서드는 재정의 할 수 없습니다 68 | // override static func typeMethod() { } 69 | 70 | // final 키워드를 사용한 메서드, 프로퍼티는 재정의 할 수 없습니다 71 | // override func sayHello() { } 72 | // override class func finalClassMethod() { } 73 | 74 | } 75 | 76 | ``` 77 | 78 | ## 동작 확인 79 | 80 | ```swift 81 | let yagom: Person = Person() 82 | let hana: Student = Student() 83 | 84 | yagom.name = "yagom" 85 | hana.name = "hana" 86 | hana.major = "Swift" 87 | 88 | yagom.selfIntroduce() 89 | // 저는 yagom입니다 90 | 91 | hana.selfIntroduce() 92 | // 저는 hana이고, 전공은 Swift입니다 93 | 94 | Person.classMethod() 95 | // type method - class 96 | 97 | Person.typeMethod() 98 | // type method - static 99 | 100 | Person.finalCalssMethod() 101 | // type method - final class 102 | 103 | 104 | Student.classMethod() 105 | // overriden type method - class 106 | 107 | Student.typeMethod() 108 | // type method - static 109 | 110 | Student.finalCalssMethod() 111 | // type method - final class 112 | ``` 113 | 114 | 115 | ## 관련문서 116 | 117 | * [The Swift Programming Language - Inheritance](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html) -------------------------------------------------------------------------------- /contents/14_inheritance/inheritance.swift: -------------------------------------------------------------------------------- 1 | /* 상속 */ 2 | 3 | // 스위프트의 상속은 클래스, 프로토콜 등에서 가능합니다 4 | // 열거형, 구조체는 상속이 불가능합니다 5 | // 스위프트는 다중상속을 지원하지 않습니다 6 | // 이번 파트에서는 클래스의 상속에 대해서 알아봅니다 7 | 8 | import Swift 9 | 10 | //MARK: - 클래스의 상속과 재정의 11 | 12 | //class <#이름#>: <#상속받을 클래스 이름#> { 13 | // /* <#구현부#> */ 14 | //} 15 | 16 | class Person { 17 | var name: String = "" 18 | 19 | func selfIntroduce() { 20 | print("저는 \(name)입니다") 21 | } 22 | 23 | // final 키워드를 사용하여 재정의를 방지할 수 있습니다 24 | final func sayHello() { 25 | print("hello") 26 | } 27 | 28 | // 타입 메서드 29 | // 재정의 불가 타입 메서드 - static 30 | static func typeMethod() { 31 | print("type method - static") 32 | } 33 | 34 | // 재정의 가능 타입 메서드 - class 35 | class func classMethod() { 36 | print("type method - class") 37 | } 38 | 39 | // 재정의 가능한 class 메서드라도 40 | // final 키워드를 사용하면 재정의 할 수 없습니다 41 | // 메서드 앞의 `static`과 `final class`는 똑같은 역할을 합니다 42 | final class func finalClassMethod() { 43 | print("type method - final class") 44 | } 45 | } 46 | 47 | class Student: Person { 48 | var major: String = "" 49 | 50 | override func selfIntroduce() { 51 | print("저는 \(name)이고, 전공은 \(major)입니다") 52 | } 53 | 54 | override class func classMethod() { 55 | print("overriden type method - class") 56 | } 57 | 58 | // static을 사용한 타입 메서드는 재정의 할 수 없습니다 59 | // override static func typeMethod() { } 60 | 61 | // final 키워드를 사용한 메서드, 프로퍼티는 재정의 할 수 없습니다 62 | // override func sayHello() { } 63 | // override class func finalClassMethod() { } 64 | 65 | } 66 | 67 | let yagom: Person = Person() 68 | let hana: Student = Student() 69 | 70 | yagom.name = "yagom" 71 | hana.name = "hana" 72 | hana.major = "Swift" 73 | 74 | yagom.selfIntroduce() 75 | // 저는 yagom입니다 76 | 77 | hana.selfIntroduce() 78 | // 저는 hana이고, 전공은 Swift입니다 79 | 80 | Person.classMethod() 81 | // type method - class 82 | 83 | Person.typeMethod() 84 | // type method - static 85 | 86 | Person.finalClassMethod() 87 | // type method - final class 88 | 89 | 90 | Student.classMethod() 91 | // overriden type method - class 92 | 93 | Student.typeMethod() 94 | // type method - static 95 | 96 | Student.finalClassMethod() 97 | // type method - final class 98 | -------------------------------------------------------------------------------- /contents/15_init_deinit/README.md: -------------------------------------------------------------------------------- 1 | # 인스턴스의 생성과 소멸 2 | 3 | 인스턴스를 생성하는 이니셜라이저와 클래스의 인스턴스가 소멸될 때 호출되는 디이니셜라이저 그리고 그와 관련된 것들에 대해 알아봅니다. 4 | 5 | * 프로퍼티 초기값 6 | * 이니셜라이저 `init` 7 | * 디이니셜라이저 `deinit` 8 | 9 | [![클릭하여 이동](http://img.youtube.com/vi/E2Yy3gp9_Nk/0.jpg)](http://www.youtube.com/watch?v=E2Yy3gp9_Nk "init_deinit") 10 | 11 | [소스코드](init_deinit.swift) 12 | 13 | 14 | ## 프로퍼티 기본값 15 | 16 | 스위프트의 모든 인스턴스는 초기화와 동시에 **모든 프로퍼티에** 유효한 값이 할당되어 있어야 합니다. 프로퍼티에 미리 기본값을 할당해두면 인스턴스가 생성됨과 동시에 초기값을 지니게 됩니다. 17 | 18 | ```swift 19 | class PersonA { 20 | // 모든 저장 프로퍼티에 기본값 할당 21 | var name: String = "unknown" 22 | var age: Int = 0 23 | var nickName: String = "nick" 24 | } 25 | 26 | // 인스턴스 생성 27 | let jason: PersonA = PersonA() 28 | 29 | // 기본값이 인스턴스가 지녀야 할 값과 맞지 않다면 30 | // 생성된 인스턴스의 프로퍼티에 각각 값 할당 31 | jason.name = "jason" 32 | jason.age = 30 33 | jason.nickName = "j" 34 | ``` 35 | 36 | ## 이니셜라이저 37 | 프로퍼티 기본값을 지정하기 어려운 경우에는 이니셜라이저 `init`을 통해 인스턴스가 가져야 할 초기값을 전달할 수 있습니다. 38 | 39 | ```swift 40 | class PersonB { 41 | var name: String 42 | var age: Int 43 | var nickName: String 44 | 45 | // 이니셜라이저 46 | init(name: String, age: Int, nickName: String) { 47 | self.name = name 48 | self.age = age 49 | self.nickName = nickName 50 | } 51 | } 52 | 53 | let hana: PersonB = PersonB(name: "hana", age: 20, nickName: "하나") 54 | ``` 55 | 56 | > 프로퍼티의 초기값이 꼭 필요 없을 때 57 | 58 | * 옵셔널을 사용! 59 | 60 | ```swift 61 | class PersonC { 62 | var name: String 63 | var age: Int 64 | var nickName: String? 65 | 66 | init(name: String, age: Int, nickName: String) { 67 | self.name = name 68 | self.age = age 69 | self.nickName = nickName 70 | } 71 | 72 | init(name: String, age: Int) { 73 | self.name = name 74 | self.age = age 75 | } 76 | } 77 | 78 | let jenny: PersonC = PersonC(name: "jenny", age: 10) 79 | let mike: PersonC = PersonC(name: "mike", age: 15, nickName: "m") 80 | ``` 81 | 82 | * 암시적 추출 옵셔널은 인스턴스 사용에 꼭 필요하지만 초기값을 할당하지 않고자 할 때 사용 83 | 84 | ```swift 85 | class Puppy { 86 | var name: String 87 | var owner: PersonC! 88 | 89 | init(name: String) { 90 | self.name = name 91 | } 92 | 93 | func goOut() { 94 | print("\(name)가 주인 \(owner.name)와 산책을 합니다") 95 | } 96 | } 97 | 98 | let happy: Puppy = Puppy(name: "happy") 99 | // 강아지는 주인없이 산책하면 안돼요! 100 | //happy.goOut() // 주인이 없는 상태라 오류 발생 101 | happy.owner = jenny 102 | happy.goOut() 103 | // happy가 주인 jenny와 산책을 합니다 104 | ``` 105 | 106 | ### 실패가능한 이니셜라이저 107 | 108 | 이니셜라이저 매개변수로 전달되는 초기값이 잘못된 경우 인스턴스 생성에 실패할 수 있습니다. 인스턴스 생성에 실패하면 nil을 반환합니다. 그래서 실패가능한 이니셜라이저의 반환타입은 옵셔널 타입입니다. `init?`을 사용합니다. 109 | 110 | ```swift 111 | class PersonD { 112 | var name: String 113 | var age: Int 114 | var nickName: String? 115 | 116 | init?(name: String, age: Int) { 117 | if (0...120).contains(age) == false { 118 | return nil 119 | } 120 | 121 | if name.characters.count == 0 { 122 | return nil 123 | } 124 | 125 | self.name = name 126 | self.age = age 127 | } 128 | } 129 | 130 | //let john: PersonD = PersonD(name: "john", age: 23) 131 | let john: PersonD? = PersonD(name: "john", age: 23) 132 | let joker: PersonD? = PersonD(name: "joker", age: 123) 133 | let batman: PersonD? = PersonD(name: "", age: 10) 134 | 135 | print(joker) // nil 136 | print(batman) // nil 137 | ``` 138 | 139 | ## 디이니셜라이저 140 | 141 | `deinit`은 클래스의 인스턴스가 메모리에서 해제되는 시점에 호출됩니다. 인스턴스가 해제되는 시점에 해야할 일을 구현할 수 있습니다. 자동으로 호출되므로 직접 호출할 수 없습니다. 인스턴스가 메모리에서 해제되는 시점은 __ARC(Automatic Reference Counting)__ 의 규칙에 따라 결정됩니다. ARC에 대해 더 자세한 사항은 [ARC 문서](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html)를 참고하세요. 142 | 디이니셜라이저는 **클래스 타입에만** 구현할 수 있습니다. 143 | 144 | ```swift 145 | class PersonE { 146 | var name: String 147 | var pet: Puppy? 148 | var child: PersonC 149 | 150 | init(name: String, child: PersonC) { 151 | self.name = name 152 | self.child = child 153 | } 154 | 155 | // 인스턴스가 메모리에서 해제되는 시점에 자동 호출 156 | deinit { 157 | if let petName = pet?.name { 158 | print("\(name)가 \(child.name)에게 \(petName)를 인도합니다") 159 | self.pet?.owner = child 160 | } 161 | } 162 | } 163 | 164 | var donald: PersonE? = PersonE(name: "donald", child: jenny) 165 | donald?.pet = happy 166 | donald = nil // donald 인스턴스가 더이상 필요없으므로 메모리에서 해제됩니다 167 | // donald가 jenny에게 happy를 인도합니다 168 | ``` 169 | 170 | 171 | ## 관련문서 172 | 173 | * [The Swift Programming Language - Initialization](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html) 174 | * [The Swift Programming Language - Deinitialization](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Deinitialization.html) 175 | * [The Swift Programming Language - Automatic Reference Counting](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html) -------------------------------------------------------------------------------- /contents/15_init_deinit/init_deinit.swift: -------------------------------------------------------------------------------- 1 | /* 인스턴스 생성과 소멸 */ 2 | 3 | // 이니셜라이저와 디이니셜라이저 4 | // init, deinit 5 | 6 | import Swift 7 | 8 | //MARK: - 프로퍼티 기본값 9 | 10 | // 스위프트의 모든 인스턴스는 초기화와 동시에 11 | // 모든 프로퍼티에 유효한 값이 할당되어 있어야 합니다 12 | // 프로퍼티에 미리 기본값을 할당해두면 13 | // 인스턴스가 생성됨과 동시에 초기값을 지니게 됩니다 14 | 15 | class PersonA { 16 | // 모든 저장 프로퍼티에 기본값 할당 17 | var name: String = "unknown" 18 | var age: Int = 0 19 | var nickName: String = "nick" 20 | } 21 | 22 | let jason: PersonA = PersonA() 23 | jason.name = "jason" 24 | jason.age = 30 25 | jason.nickName = "j" 26 | 27 | //MARK: - 이니셜라이저 28 | 29 | // 프로퍼티 기본값을 지정하기 어려운 경우에는 30 | // 이니셜라이저를 통해 31 | // 인스턴스가 가져야 할 초기값을 전달할 수 있습니다 32 | 33 | class PersonB { 34 | var name: String 35 | var age: Int 36 | var nickName: String 37 | 38 | // 이니셜라이저 39 | init(name: String, age: Int, nickName: String) { 40 | self.name = name 41 | self.age = age 42 | self.nickName = nickName 43 | } 44 | } 45 | 46 | let hana: PersonB = PersonB(name: "hana", age: 20, nickName: "하나") 47 | 48 | //let hana: PersonB = PersonB(name: "hana", age: 20, nickName: "") 49 | 50 | 51 | //MARK: 프로퍼티의 초기값이 꼭 필요 없을 때 52 | // 옵셔널을 사용! 53 | 54 | class PersonC { 55 | var name: String 56 | var age: Int 57 | var nickName: String? 58 | 59 | init(name: String, age: Int, nickName: String) { 60 | self.name = name 61 | self.age = age 62 | self.nickName = nickName 63 | } 64 | 65 | init(name: String, age: Int) { 66 | self.name = name 67 | self.age = age 68 | } 69 | } 70 | 71 | let jenny: PersonC = PersonC(name: "jenny", age: 10) 72 | let mike: PersonC = PersonC(name: "mike", age: 15, nickName: "m") 73 | 74 | // 암시적 추출 옵셔널은 75 | // 인스턴스 사용에 꼭 필요하지만 76 | // 초기값을 할당하지 않고자 할 때 사용 77 | 78 | class Puppy { 79 | var name: String 80 | var owner: PersonC! 81 | 82 | init(name: String) { 83 | self.name = name 84 | } 85 | 86 | func goOut() { 87 | print("\(name)가 주인 \(owner.name)와 산책을 합니다") 88 | } 89 | } 90 | 91 | let happy: Puppy = Puppy(name: "happy") 92 | // 강아지는 주인없이 산책하면 안돼요! 93 | //happy.goOut() // 주인이 없는 상태라 오류 발생 94 | happy.owner = jenny 95 | happy.goOut() 96 | // happy가 주인 jenny와 산책을 합니다 97 | 98 | 99 | //MARK: 실패가능한 이니셜라이저 100 | // 이니셜라이저 매개변수로 전달되는 초기값이 잘못된 경우 101 | // 인스턴스 생성에 실패할 수 있습니다 102 | // 인스턴스 생성에 실패하면 nil을 반환합니다 103 | // 그래서 실패가능한 이니셜라이저의 반환타입은 옵셔널 타입입니다 104 | 105 | class PersonD { 106 | var name: String 107 | var age: Int 108 | var nickName: String? 109 | 110 | init?(name: String, age: Int) { 111 | if (0...120).contains(age) == false { 112 | return nil 113 | } 114 | 115 | if name.characters.count == 0 { 116 | return nil 117 | } 118 | 119 | self.name = name 120 | self.age = age 121 | } 122 | } 123 | 124 | //let john: PersonD = PersonD(name: "john", age: 23) 125 | let john: PersonD? = PersonD(name: "john", age: 23) 126 | let joker: PersonD? = PersonD(name: "joker", age: 123) 127 | let batman: PersonD? = PersonD(name: "", age: 10) 128 | 129 | print(joker) // nil 130 | print(batman) // nil 131 | 132 | //MARK: - 디이니셜라이저 133 | 134 | // deinit은 클래스의 인스턴스가 135 | // 메모리에서 해제되는 시점에 호출됩니다 136 | // 인스턴스가 해제되는 시점에 해야할 일을 구현할 수 있습니다 137 | 138 | class PersonE { 139 | var name: String 140 | var pet: Puppy? 141 | var child: PersonC 142 | 143 | init(name: String, child: PersonC) { 144 | self.name = name 145 | self.child = child 146 | } 147 | 148 | deinit { 149 | if let petName = pet?.name { 150 | print("\(name)가 \(child.name)에게 \(petName)를 인도합니다") 151 | self.pet?.owner = child 152 | } 153 | } 154 | } 155 | 156 | var donald: PersonE? = PersonE(name: "donald", child: jenny) 157 | donald?.pet = happy 158 | donald = nil // donald 인스턴스가 더이상 필요없으므로 메모리에서 해제됩니다 159 | // donald가 jenny에게 happy를 인도합니다 160 | -------------------------------------------------------------------------------- /contents/16_optional_chaining/README.md: -------------------------------------------------------------------------------- 1 | # 옵셔널 체이닝 2 | 3 | 옵셔널 체이닝은 옵셔널의 내부의 내부의 내부로 옵셔널이 연결되어 있을 때 유용하게 활용할 수 있습니다. 매 번 nil 확인을 하지 않고 최종적으로 원하는 값이 있는지 없는지 확인할 수 있습니다. 4 | 5 | [![클릭하여 이동](http://img.youtube.com/vi/UANvklNnDeg/0.jpg)](http://www.youtube.com/watch?v=UANvklNnDeg "optional_chaining_nil") 6 | 7 | [소스코드](optional_chaining.swift) 8 | 9 | 10 | ## 예제 클래스 11 | 12 | ```swift 13 | class Person { 14 | var name: String 15 | var job: String? 16 | var home: Apartment? 17 | 18 | init(name: String) { 19 | self.name = name 20 | } 21 | } 22 | 23 | class Apartment { 24 | var buildingNumber: String 25 | var roomNumber: String 26 | var `guard`: Person? 27 | var owner: Person? 28 | 29 | init(dong: String, ho: String) { 30 | buildingNumber = dong 31 | roomNumber = ho 32 | } 33 | } 34 | ``` 35 | 36 | ## 옵셔널 체이닝 사용 37 | 38 | ```swift 39 | let yagom: Person? = Person(name: "yagom") 40 | let apart: Apartment? = Apartment(dong: "101", ho: "202") 41 | let superman: Person? = Person(name: "superman") 42 | 43 | 44 | // 옵셔널 체이닝이 실행 후 결과값이 nil일 수 있으므로 45 | // 결과 타입도 옵셔널입니다 46 | 47 | // 만약 우리집의 경비원의 직업이 궁금하다면..? 48 | 49 | // 옵셔널 체이닝을 사용하지 않는다면... 50 | func guardJob(owner: Person?) { 51 | if let owner = owner { 52 | if let home = owner.home { 53 | if let `guard` = home.guard { 54 | if let guardJob = `guard`.job { 55 | print("우리집 경비원의 직업은 \(guardJob)입니다") 56 | } else { 57 | print("우리집 경비원은 직업이 없어요") 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | guardJob(owner: yagom) 65 | 66 | // 옵셔널 체이닝을 사용한다면 67 | func guardJobWithOptionalChaining(owner: Person?) { 68 | if let guardJob = owner?.home?.guard?.job { 69 | print("우리집 경비원의 직업은 \(guardJob)입니다") 70 | } else { 71 | print("우리집 경비원은 직업이 없어요") 72 | } 73 | } 74 | 75 | guardJobWithOptionalChaining(owner: yagom) 76 | // 우리집 경비원은 직업이 없어요 77 | 78 | 79 | 80 | yagom?.home?.guard?.job // nil 81 | 82 | yagom?.home = apart 83 | 84 | yagom?.home // Optional(Apartment) 85 | yagom?.home?.guard // nil 86 | 87 | yagom?.home?.guard = superman 88 | 89 | yagom?.home?.guard // Optional(Person) 90 | 91 | yagom?.home?.guard?.name // superman 92 | yagom?.home?.guard?.job // nil 93 | 94 | yagom?.home?.guard?.job = "경비원" 95 | 96 | ``` 97 | 98 | --- 99 | 100 | # nil 병합 연산자 101 | 중위 연산자입니다. `??` 102 | 103 | `Optional ?? Value` 104 | 105 | 옵셔널 값이 `nil`일 경우, 우측의 값을 반환합니다. **띄어쓰기에 주의하여야 합니다.** 106 | 107 | ```swift 108 | var guardJob: String 109 | 110 | guardJob = yagom?.home?.guard?.job ?? "슈퍼맨" 111 | print(guardJob) // 경비원 112 | 113 | yagom?.home?.guard?.job = nil 114 | 115 | guardJob = yagom?.home?.guard?.job ?? "슈퍼맨" 116 | print(guardJob) // 슈퍼맨 117 | 118 | ``` 119 | 120 | 121 | ## 관련문서 122 | 123 | * [The Swift Programming Language - Optional Chaining](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html) 124 | * [The Swift Programming Language - Basic Operators - Nil-Coalescing Operator](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html) -------------------------------------------------------------------------------- /contents/16_optional_chaining/optional_chaining.swift: -------------------------------------------------------------------------------- 1 | /* 옵셔널 체이닝 */ 2 | 3 | // 옵셔널 체이닝은 옵셔널 요소 내부의 프로퍼티로 4 | // 또다시 옵셔널이 연속적으로 연결되는 경우 유용하게 사용할 수 있습니다 5 | 6 | import Swift 7 | 8 | //MARK: - 옵셔널 체이닝 9 | 10 | //MARK: 예제 클래스 및 인스턴스 생성 11 | class Person { 12 | var name: String 13 | var job: String? 14 | var home: Apartment? 15 | 16 | init(name: String) { 17 | self.name = name 18 | } 19 | } 20 | 21 | class Apartment { 22 | var buildingNumber: String 23 | var roomNumber: String 24 | var `guard`: Person? 25 | var owner: Person? 26 | 27 | init(dong: String, ho: String) { 28 | buildingNumber = dong 29 | roomNumber = ho 30 | } 31 | } 32 | 33 | let yagom: Person? = Person(name: "yagom") 34 | let apart: Apartment? = Apartment(dong: "101", ho: "202") 35 | let superman: Person? = Person(name: "superman") 36 | 37 | 38 | // 옵셔널 체이닝이 실행 후 결과값이 nil일 수 있으므로 39 | // 결과 타입도 옵셔널입니다 40 | 41 | // 만약 우리집의 경비원의 직업이 궁금하다면..? 42 | 43 | // 옵셔널 체이닝을 사용하지 않는다면... 44 | func guardJob(owner: Person?) { 45 | if let owner = owner { 46 | if let home = owner.home { 47 | if let `guard` = home.guard { 48 | if let guardJob = `guard`.job { 49 | print("우리집 경비원의 직업은 \(guardJob)입니다") 50 | } else { 51 | print("우리집 경비원은 직업이 없어요") 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | guardJob(owner: yagom) 59 | 60 | //MARK: 옵셔널 체이닝 사용 61 | 62 | // 옵셔널 체이닝을 사용한다면 63 | func guardJobWithOptionalChaining(owner: Person?) { 64 | if let guardJob = owner?.home?.guard?.job { 65 | print("우리집 경비원의 직업은 \(guardJob)입니다") 66 | } else { 67 | print("우리집 경비원은 직업이 없어요") 68 | } 69 | } 70 | 71 | guardJobWithOptionalChaining(owner: yagom) 72 | // 우리집 경비원은 직업이 없어요 73 | 74 | 75 | 76 | yagom?.home?.guard?.job // nil 77 | 78 | yagom?.home = apart 79 | 80 | yagom?.home // Optional(Apartment) - apart instance 81 | yagom?.home?.guard // nil 82 | 83 | yagom?.home?.guard = superman 84 | 85 | yagom?.home?.guard // Optional(Person) - superman instance 86 | 87 | yagom?.home?.guard?.name // Optional(String) - "superman" 88 | yagom?.home?.guard?.job // nil 89 | 90 | yagom?.home?.guard?.job = "경비원" 91 | 92 | 93 | 94 | //MARK: - nil 병합 연산자 95 | 96 | var guardJob: String 97 | 98 | guardJob = yagom?.home?.guard?.job ?? "슈퍼맨" 99 | print(guardJob) // 경비원 100 | 101 | yagom?.home?.guard?.job = nil 102 | 103 | guardJob = yagom?.home?.guard?.job ?? "슈퍼맨" 104 | print(guardJob) // 슈퍼맨 105 | -------------------------------------------------------------------------------- /contents/17_type_casting/README.md: -------------------------------------------------------------------------------- 1 | # 타입캐스팅 2 | 3 | 스위프트의 타입캐스팅은 **인스턴스의 타입을 확인** 하는 용도 또는 클래스의 인스턴스를 **부모 혹은 자식 클래스의 타입으로 사용할 수 있는지 확인** 하는 용도로 사용합니다. `is`, `as`를 사용합니다. 4 | 5 | [![클릭하여 이동](http://img.youtube.com/vi/XGgaCHNH3AE/0.jpg)](http://www.youtube.com/watch?v=XGgaCHNH3AE "type_casting") 6 | 7 | [소스코드](type_casting.swift) 8 | 9 | 10 | ## 타입 캐스팅 예제를 위한 클래스 정의 11 | 12 | ```swift 13 | class Person { 14 | var name: String = "" 15 | func breath() { 16 | print("숨을 쉽니다") 17 | } 18 | } 19 | 20 | class Student: Person { 21 | var school: String = "" 22 | func goToSchool() { 23 | print("등교를 합니다") 24 | } 25 | } 26 | 27 | class UniversityStudent: Student { 28 | var major: String = "" 29 | func goToMT() { 30 | print("멤버쉽 트레이닝을 갑니다 신남!") 31 | } 32 | } 33 | ``` 34 | 35 | ```swift 36 | // 인스턴스 생성 37 | var yagom: Person = Person() 38 | var hana: Student = Student() 39 | var jason: UniversityStudent = UniversityStudent() 40 | ``` 41 | 42 | ### 타입 확인 43 | 44 | `is`를 사용하여 타입을 확인합니다. 45 | 46 | ```swift 47 | var result: Bool 48 | 49 | result = yagom is Person // true 50 | result = yagom is Student // false 51 | result = yagom is UniversityStudent // false 52 | 53 | result = hana is Person // true 54 | result = hana is Student // true 55 | result = hana is UniversityStudent // false 56 | 57 | result = jason is Person // true 58 | result = jason is Student // true 59 | result = jason is UniversityStudent // true 60 | 61 | if yagom is UniversityStudent { 62 | print("yagom은 대학생입니다") 63 | } else if yagom is Student { 64 | print("yagom은 학생입니다") 65 | } else if yagom is Person { 66 | print("yagom은 사람입니다") 67 | } // yagom은 사람입니다 68 | 69 | switch jason { 70 | case is Person: 71 | print("jason은 사람입니다") 72 | case is Student: 73 | print("jason은 학생입니다") 74 | case is UniversityStudent: 75 | print("jason은 대학생입니다") 76 | default: 77 | print("jason은 사람도, 학생도, 대학생도 아닙니다") 78 | } // jason은 사람입니다 79 | 80 | switch jason { 81 | case is UniversityStudent: 82 | print("jason은 대학생입니다") 83 | case is Student: 84 | print("jason은 학생입니다") 85 | case is Person: 86 | print("jason은 사람입니다") 87 | default: 88 | print("jason은 사람도, 학생도, 대학생도 아닙니다") 89 | } // jason은 대학생입니다 90 | ``` 91 | 92 | ## 업 캐스팅 93 | `as`를 사용하여 부모클래스의 인스턴스로 사용할 수 있도록 컴파일러에게 타입정보를 전환해줍니다. `Any` 혹은 `AnyObject`로도 타입정보를 변환할 수 있습니다. 암시적으로 처리되므로 꼭 필요한 경우가 아니라면 생략해도 무방합니다. 94 | 95 | ```swift 96 | // UniversityStudent 인스턴스를 생성하여 Person 행세를 할 수 있도록 업 캐스팅 97 | var mike: Person = UniversityStudent() as Person 98 | 99 | var jenny: Student = Student() 100 | //var jina: UniversityStudent = Person() as UniversityStudent // 컴파일 오류 101 | 102 | // UniversityStudent 인스턴스를 생성하여 Any 행세를 할 수 있도록 업 캐스팅 103 | var jina: Any = Person() // as Any 생략가능 104 | ``` 105 | 106 | ## 다운 캐스팅 107 | 108 | `as?` 또는 `as!`를 사용하여 자식 클래스의 인스턴스로 사용할 수 있도록 컴파일러에게 인스턴스의 타입정보를 전환해줍니다. 109 | 110 | ### 조건부 다운 캐스팅 111 | `as?`를 사용합니다. 캐스팅에 실패하면, 즉 캐스팅하려는 타입에 부합하지 않는 인스턴스라면 `nil`을 반환하기 때문에 결과의 타입은 옵셔널 타입입니다. 112 | 113 | ```swift 114 | var optionalCasted: Student? 115 | 116 | optionalCasted = mike as? UniversityStudent 117 | optionalCasted = jenny as? UniversityStudent // nil 118 | optionalCasted = jina as? UniversityStudent // nil 119 | optionalCasted = jina as? Student // nil 120 | ``` 121 | 122 | ### 강제 다운 캐스팅 123 | `as!`를 사용합니다. 캐스팅에 실패하면, 즉 캐스팅하려는 타입에 부합하지 않는 인스턴스라면 런타임 오류가 발생합니다. 캐스팅에 성공하면 옵셔널이 아닌 일반 타입을 반환합니다. 124 | 125 | ```swift 126 | var forcedCasted: Student 127 | 128 | optionalCasted = mike as! UniversityStudent 129 | //optionalCasted = jenny as! UniversityStudent // 런타임 오류 130 | //optionalCasted = jina as! UniversityStudent // 런타임 오류 131 | //optionalCasted = jina as! Student // 런타임 오류 132 | ``` 133 | 134 | ## 활용 135 | ```swift 136 | func doSomethingWithSwitch(someone: Person) { 137 | switch someone { 138 | case is UniversityStudent: 139 | (someone as! UniversityStudent).goToMT() 140 | case is Student: 141 | (someone as! Student).goToSchool() 142 | case is Person: 143 | (someone as! Person).breath() 144 | } 145 | } 146 | 147 | doSomethingWithSwitch(someone: mike as Person) // 멤버쉽 트레이닝을 갑니다 신남! 148 | doSomethingWithSwitch(someone: mike) // 멤버쉽 트레이닝을 갑니다 신남! 149 | doSomethingWithSwitch(someone: jenny) // 등교를 합니다 150 | doSomethingWithSwitch(someone: yagom) // 숨을 쉽니다 151 | 152 | 153 | func doSomething(someone: Person) { 154 | if let universityStudent = someone as? UniversityStudent { 155 | universityStudent.goToMT() 156 | } else if let student = someone as? Student { 157 | student.goToSchool() 158 | } else if let person = someone as? Person { 159 | person.breath() 160 | } 161 | } 162 | 163 | doSomething(someone: mike as Person) // 멤버쉽 트레이닝을 갑니다 신남! 164 | doSomething(someone: mike) // 멤버쉽 트레이닝을 갑니다 신남! 165 | doSomething(someone: jenny) // 등교를 합니다 166 | doSomething(someone: yagom) // 숨을 쉽니다 167 | ``` 168 | 169 | ## 관련문서 170 | 171 | * [The Swift Programming Language - Type Casting](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html) -------------------------------------------------------------------------------- /contents/17_type_casting/type_casting.swift: -------------------------------------------------------------------------------- 1 | /* 타입캐스팅 */ 2 | 3 | // 스위프트의 타입캐스팅은 4 | // 인스턴스의 타입을 확인하는 용도 5 | // 또는 클래스의 인스턴스를 6 | // 부모 혹은 자식 클래스의 타입으로 사용할 수 있는지 7 | // 확인하는 용도로 사용합니다 8 | // is, as를 사용합니다 9 | 10 | 11 | import Swift 12 | 13 | // 타입 캐스팅을 위한 클래스 정의 14 | 15 | class Person { 16 | var name: String = "" 17 | func breath() { 18 | print("숨을 쉽니다") 19 | } 20 | } 21 | 22 | class Student: Person { 23 | var school: String = "" 24 | func goToSchool() { 25 | print("등교를 합니다") 26 | } 27 | } 28 | 29 | class UniversityStudent: Student { 30 | var major: String = "" 31 | func goToMT() { 32 | print("멤버쉽 트레이닝을 갑니다 신남!") 33 | } 34 | } 35 | 36 | var yagom: Person = Person() 37 | var hana: Student = Student() 38 | var jason: UniversityStudent = UniversityStudent() 39 | 40 | 41 | //MARK: - 타입 확인 42 | // is를 사용하여 타입을 확인합니다 43 | 44 | var result: Bool 45 | 46 | result = yagom is Person // true 47 | result = yagom is Student // false 48 | result = yagom is UniversityStudent // false 49 | 50 | result = hana is Person // true 51 | result = hana is Student // true 52 | result = hana is UniversityStudent // false 53 | 54 | result = jason is Person // true 55 | result = jason is Student // true 56 | result = jason is UniversityStudent // true 57 | 58 | if yagom is UniversityStudent { 59 | print("yagom은 대학생입니다") 60 | } else if yagom is Student { 61 | print("yagom은 학생입니다") 62 | } else if yagom is Person { 63 | print("yagom은 사람입니다") 64 | } // yagom은 사람입니다 65 | 66 | switch jason { 67 | case is Person: 68 | print("jason은 사람입니다") 69 | case is Student: 70 | print("jason은 학생입니다") 71 | case is UniversityStudent: 72 | print("jason은 대학생입니다") 73 | default: 74 | print("jason은 사람도, 학생도, 대학생도 아닙니다") 75 | } // jason은 사람입니다 76 | 77 | switch jason { 78 | case is UniversityStudent: 79 | print("jason은 대학생입니다") 80 | case is Student: 81 | print("jason은 학생입니다") 82 | case is Person: 83 | print("jason은 사람입니다") 84 | default: 85 | print("jason은 사람도, 학생도, 대학생도 아닙니다") 86 | } // jason은 대학생입니다 87 | 88 | //MARK: - 업 캐스팅 89 | // as를 사용하여 부모클래스의 인스턴스로 사용할 수 있도록 90 | // 컴파일러에게 타입정보를 전환해줍니다 91 | // Any 혹은 AnyObject로도 타입정보를 변환할 수 있습니다 92 | // 암시적으로 처리되므로 생략해도 무방합니다 93 | 94 | var mike: Person = UniversityStudent() as Person 95 | var jenny: Student = Student() 96 | //var jina: UniversityStudent = Person() as UniversityStudent // 컴파일 오류 97 | var jina: Any = Person() // as Any 생략가능 98 | 99 | 100 | //MARK: - 다운 캐스팅 101 | // as? 또는 as!를 사용하여 102 | // 자식 클래스의 인스턴스로 사용할 수 있도록 103 | // 컴파일러에게 인스턴스의 타입정보를 전환해줍니다 104 | 105 | //MARK: 조건부 다운 캐스팅 106 | // as? 107 | 108 | var optionalCasted: Student? 109 | 110 | optionalCasted = mike as? UniversityStudent 111 | optionalCasted = jenny as? UniversityStudent // nil 112 | optionalCasted = jina as? UniversityStudent // nil 113 | optionalCasted = jina as? Student // nil 114 | 115 | 116 | //MARK: 강제 다운 캐스팅 117 | // as! 118 | 119 | var forcedCasted: Student 120 | 121 | optionalCasted = mike as! UniversityStudent 122 | //optionalCasted = jenny as! UniversityStudent // 런타임 오류 123 | //optionalCasted = jina as! UniversityStudent // 런타임 오류 124 | //optionalCasted = jina as! Student // 런타임 오류 125 | 126 | 127 | // 활용 128 | func doSomethingWithSwitch(someone: Person) { 129 | switch someone { 130 | case is UniversityStudent: 131 | (someone as! UniversityStudent).goToMT() 132 | case is Student: 133 | (someone as! Student).goToSchool() 134 | case is Person: 135 | (someone as! Person).breath() 136 | } 137 | } 138 | 139 | doSomethingWithSwitch(someone: mike as Person) // 멤버쉽 트레이닝을 갑니다 신남! 140 | doSomethingWithSwitch(someone: mike) // 멤버쉽 트레이닝을 갑니다 신남! 141 | doSomethingWithSwitch(someone: jenny) // 등교를 합니다 142 | doSomethingWithSwitch(someone: yagom) // 숨을 쉽니다 143 | 144 | 145 | func doSomething(someone: Person) { 146 | if let universityStudent = someone as? UniversityStudent { 147 | universityStudent.goToMT() 148 | } else if let student = someone as? Student { 149 | student.goToSchool() 150 | } else if let person = someone as? Person { 151 | person.breath() 152 | } 153 | } 154 | 155 | doSomething(someone: mike as Person) // 멤버쉽 트레이닝을 갑니다 신남! 156 | doSomething(someone: mike) // 멤버쉽 트레이닝을 갑니다 신남! 157 | doSomething(someone: jenny) // 등교를 합니다 158 | doSomething(someone: yagom) // 숨을 쉽니다 159 | -------------------------------------------------------------------------------- /contents/18_assert_guard/README.md: -------------------------------------------------------------------------------- 1 | # `assert`와 `guard` 2 | 3 | 애플리케이션이 동작 도중에 생성하는 다양한 연산 결과값을 동적으로 확인하고 안전하게 처리할 수 있도록 확인하고 빠르게 처리할 수 있습니다. 4 | 5 | [![클릭하여 이동](http://img.youtube.com/vi/heEKIxLs5Sc/0.jpg)](http://www.youtube.com/watch?v=heEKIxLs5Sc "assert_guard") 6 | 7 | [소스코드](assert_guard.swift) 8 | 9 | ## Assertion 10 | 11 | `assert(_:_:file:line:)` 함수를 사용합니다. assert 함수는 **디버깅 모드에서만** 동작합니다. 배포하는 애플리케이션에서는 제외됩니다. 주로 디버깅 중 **조건의 검증을 위하여** 사용합니다. 12 | 13 | ```swift 14 | var someInt: Int = 0 15 | 16 | // 검증 조건에 부합하므로 지나갑니다 17 | assert(someInt == 0, "someInt != 0") 18 | 19 | someInt = 1 20 | //assert(someInt == 0) // 동작 중지, 검증 실패 21 | //assert(someInt == 0, "someInt != 0") // 동작 중지, 검증 실패 22 | // assertion failed: someInt != 0: file guard_assert.swift, line 26 23 | 24 | 25 | func functionWithAssert(age: Int?) { 26 | 27 | assert(age != nil, "age == nil") 28 | 29 | assert((age! >= 0) && (age! <= 130), "나이값 입력이 잘못되었습니다") 30 | print("당신의 나이는 \(age!)세입니다") 31 | } 32 | 33 | functionWithAssert(age: 50) 34 | //functionWithAssert(age: -1) // 동작 중지, 검증 실패 35 | //functionWithAssert(age: nil) // 동작 중지, 검증 실패 36 | ``` 37 | 38 | > `assert(_:_:file:line:)`와 같은 역할을 하지만 실제 배포 환경에서도 동작하는 `precondition(_:_:file:line:)` 함수도 있습니다. 함께 살펴보세요. 39 | 40 | ## 빠른 종료 41 | 42 | Early Exit 43 | 44 | `guard`를 사용하여 잘못된 값의 전달 시 특정 실행구문을 빠르게 종료합니다. 디버깅 모드 뿐만 아니라 어떤 조건에서도 동작합니다. `guard`의 `else` 블럭 내부에는 특정 코드블럭을 종료하는 지시어(`return`, `break` 등)가 꼭 있어야 합니다. [타입 캐스팅](https://yagom.github.io/swift_basic/contents/17_type_casting/), [옵셔널](https://yagom.github.io/swift_basic/contents/07_optional/)과도 자주 사용됩니다. 그 외에도 단순 조건 판단 후 빠르게 종료할 때도 용이합니다. 45 | 46 | ```swift 47 | func functionWithGuard(age: Int?) { 48 | 49 | guard let unwrappedAge = age, 50 | unwrappedAge < 130, 51 | unwrappedAge >= 0 else { 52 | print("나이값 입력이 잘못되었습니다") 53 | return 54 | } 55 | 56 | print("당신의 나이는 \(unwrappedAge)세입니다") 57 | } 58 | 59 | var count = 1 60 | 61 | while true { 62 | guard count < 3 else { 63 | break 64 | } 65 | print(count) 66 | count += 1 67 | } 68 | // 1 69 | // 2 70 | 71 | 72 | func someFunction(info: [String: Any]) { 73 | guard let name = info["name"] as? String else { 74 | return 75 | } 76 | 77 | guard let age = info["age"] as? Int, age >= 0 else { 78 | return 79 | } 80 | 81 | print("\(name): \(age)") 82 | 83 | } 84 | 85 | someFunction(info: ["name": "jenny", "age": "10"]) 86 | someFunction(info: ["name": "mike"]) 87 | someFunction(info: ["name": "yagom", "age": 10]) // yagom: 10 88 | ``` 89 | 90 | ## 관련문서 91 | 92 | * [The Swift Programming Language - Control Flow - Early Exit](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html) 93 | * [The Swift Programming Language - The Basics - Assertion](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html) 94 | -------------------------------------------------------------------------------- /contents/18_assert_guard/assert_guard.swift: -------------------------------------------------------------------------------- 1 | /* assert와 guard */ 2 | 3 | // 애플리케이션이 동작 도중에 생성하는 다양한 결과값을 4 | // 동적으로 확인하고 안전하게 처리할 수 있도록 5 | // 확인하고 빠르게 처리할 수 있습니다 6 | 7 | import Swift 8 | 9 | //MARK: - Assertion 10 | // assert(_:_:file:line:) 함수를 사용합니다 11 | // assert 함수는 디버깅 모드에서만 동작합니다 12 | // 배포하는 애플리케이션에서는 제외됩니다 13 | // 주로 디버깅 중 조건의 검증을 위하여 사용합니다 14 | 15 | var someInt: Int = 0 16 | 17 | // 검증 조건에 부합하므로 지나갑니다 18 | assert(someInt == 0, "someInt != 0") 19 | 20 | someInt = 1 21 | //assert(someInt == 0) // 동작 중지, 검증 실패 22 | //assert(someInt == 0, "someInt != 0") // 동작 중지, 검증 실패 23 | // assertion failed: someInt != 0: file guard_assert.swift, line 26 24 | 25 | 26 | func functionWithAssert(age: Int?) { 27 | 28 | assert(age != nil, "age == nil") 29 | 30 | assert((age! >= 0) && (age! <= 130), "나이값 입력이 잘못되었습니다") 31 | print("당신의 나이는 \(age!)세입니다") 32 | } 33 | 34 | functionWithAssert(age: 50) // 당신의 나이는 50세입니다 35 | //functionWithAssert(age: -1) // 동작 중지, 검증 실패 36 | //functionWithAssert(age: nil) // 동작 중지, 검증 실패 37 | 38 | 39 | //MARK: - Early Exit 40 | // guard를 사용하여 잘못된 값의 전달 시 41 | // 특정 실행구문을 빠르게 종료합니다 42 | // 디버깅 모드 뿐만 아니라 어떤 조건에서도 동작합니다 43 | // guard의 else 블럭 내부에는 44 | // 특정 코드블럭을 종료하는 지시어(return, break 등)가 45 | // 꼭 있어야 합니다 46 | // 타입 캐스팅, 옵셔널과도 자주 사용됩니다 47 | // 그 외 단순 조건 판단후 빠르게 종료할 때도 용이합니다 48 | 49 | func functionWithGuard(age: Int?) { 50 | 51 | guard let unwrappedAge = age, 52 | unwrappedAge < 130, 53 | unwrappedAge >= 0 else { 54 | print("나이값 입력이 잘못되었습니다") 55 | return 56 | } 57 | 58 | print("당신의 나이는 \(unwrappedAge)세입니다") 59 | } 60 | 61 | var count = 1 62 | 63 | while true { 64 | guard count < 3 else { 65 | break 66 | } 67 | print(count) 68 | count += 1 69 | } 70 | // 1 71 | // 2 72 | 73 | 74 | func someFunction(info: [String: Any]) { 75 | guard let name = info["name"] as? String else { 76 | return 77 | } 78 | 79 | guard let age = info["age"] as? Int, age >= 0 else { 80 | return 81 | } 82 | 83 | print("\(name): \(age)") 84 | 85 | } 86 | 87 | someFunction(info: ["name": "jenny", "age": "10"]) 88 | someFunction(info: ["name": "mike"]) 89 | someFunction(info: ["name": "yagom", "age": 10]) // yagom: 10 90 | -------------------------------------------------------------------------------- /contents/19_protocol/README.md: -------------------------------------------------------------------------------- 1 | # 프로토콜 2 | 3 | _프로토콜(Protocol)_ 은 특정 역할을 수행하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의합니다. 구조체, 클래스, 열거형은 프로토콜을 __채택(Adopted)__ 해서 특정 기능을 수행하기 위한 프로토콜의 요구사항을 실제로 구현할 수 있습니다. 어떤 프로토콜의 요구사항을 모두 따르는 타입은 그 __프로토콜을 준수한다(Conform)__ 고 표현합니다. 타입에서 프로토콜의 요구사항을 충족시키려면 프로토콜이 제시하는 청사진의 기능을 모두 구현해야 합니다. 즉, 프로토콜은 기능을 정의하고 제시 할 뿐이지 스스로 기능을 구현하지는 않습니다. 4 | 5 | [![클릭하여 이동](http://img.youtube.com/vi/cjCgJPo3VM4/0.jpg)](http://www.youtube.com/watch?v=cjCgJPo3VM4 "protocol") 6 | 7 | [소스코드](protocol.swift) 8 | 9 | ## 정의 문법 10 | 11 | `protocol` 키워드를 사용하여 정의합니다. 12 | 13 | ```swift 14 | protocol 프로토콜 이름 { 15 | /* 정의부 */ 16 | } 17 | ``` 18 | 19 | ## 프로토콜 구현 20 | 21 | ```swift 22 | protocol Talkable { 23 | 24 | // 프로퍼티 요구 25 | // 프로퍼티 요구는 항상 var 키워드를 사용합니다 26 | // get은 읽기만 가능해도 상관 없다는 뜻이며 27 | // get과 set을 모두 명시하면 28 | // 읽기 쓰기 모두 가능한 프로퍼티여야 합니다 29 | var topic: String { get set } 30 | var language: String { get } 31 | 32 | // 메서드 요구 33 | func talk() 34 | 35 | // 이니셜라이저 요구 36 | init(topic: String, language: String) 37 | } 38 | ``` 39 | 40 | ## 프로토콜 채택 및 준수 41 | 42 | ```swift 43 | 44 | // Person 구조체는 Talkable 프로토콜을 채택했습니다 45 | struct Person: Talkable { 46 | // 프로퍼티 요구 준수 47 | var topic: String 48 | let language: String 49 | 50 | // 읽기전용 프로퍼티 요구는 연산 프로퍼티로 대체가 가능합니다 51 | // var language: String { return "한국어" } 52 | 53 | // 물론 읽기, 쓰기 프로퍼티도 연산 프로퍼티로 대체할 수 있습니다 54 | // var subject: String = "" 55 | // var topic: String { 56 | // set { 57 | // self.subject = newValue 58 | // } 59 | // get { 60 | // return self.subject 61 | // } 62 | // } 63 | 64 | // 메서드 요구 준수 65 | func talk() { 66 | print("\(topic)에 대해 \(language)로 말합니다") 67 | } 68 | 69 | // 이니셜라이저 요구 준수 70 | init(topic: String, language: String) { 71 | self.topic = topic 72 | self.language = language 73 | } 74 | } 75 | ``` 76 | 77 | > 프로퍼티 요구는 다양한 방법으로 해석, 구현할 수 있습니다. 78 | 79 | ```swift 80 | struct Person: Talkable { 81 | var subject: String = "" 82 | 83 | // 프로퍼티 요구는 연산 프로퍼티로 대체가 가능합니다 84 | var topic: String { 85 | set { 86 | self.subject = newValue 87 | } 88 | get { 89 | return self.subject 90 | } 91 | } 92 | 93 | var language: String { return "한국어" } 94 | 95 | func talk() { 96 | print("\(topic)에 대해 \(language)로 말합니다") 97 | } 98 | 99 | init(topic: String, language: String) { 100 | self.topic = topic 101 | } 102 | } 103 | ``` 104 | 105 | 106 | ## 프로토콜 상속 107 | 108 | 프로토콜은 하나 이상의 프로토콜을 상속받아 기존 프로토콜의 요구사항보다 더 많은 요구사항을 추가할 수 있습니다. 프로토콜 상속 문법은 클래스의 상속 문법과 유사하지만, 프로토콜은 클래스와 다르게 다중상속이 가능합니다. 109 | ```swift 110 | protocol 프로토콜 이름: 부모 프로토콜 이름 목록 { 111 | /* 정의부 */ 112 | } 113 | ``` 114 | 115 | ```swift 116 | protocol Readable { 117 | func read() 118 | } 119 | protocol Writeable { 120 | func write() 121 | } 122 | protocol ReadSpeakable: Readable { 123 | func speak() 124 | } 125 | protocol ReadWriteSpeakable: Readable, Writeable { 126 | func speak() 127 | } 128 | 129 | struct SomeType: ReadWriteSpeakable { 130 | func read() { 131 | print("Read") 132 | } 133 | 134 | func write() { 135 | print("Write") 136 | } 137 | 138 | func speak() { 139 | print("Speak") 140 | } 141 | } 142 | ``` 143 | 144 | ### 클래스 상속과 프로토콜 145 | 146 | 클래스에서 상속과 프로토콜 채택을 동시에 하려면 상속받으려는 클래스를 먼저 명시하고 그 뒤에 채택할 프로토콜 목록을 작성합니다. 147 | 148 | ```swift 149 | class SuperClass: Readable { 150 | func read() { } 151 | } 152 | 153 | class SubClass: SuperClass, Writeable, ReadSpeakable { 154 | func write() { } 155 | func speak() { } 156 | } 157 | ``` 158 | 159 | ## 프로토콜 준수 확인 160 | 161 | is, as 연산자를 사용해서 인스턴스가 특정 프로토콜을 준수하는지 확인할 수 있습니다. 162 | 163 | ```swift 164 | let sup: SuperClass = SuperClass() 165 | let sub: SubClass = SubClass() 166 | 167 | var someAny: Any = sup 168 | someAny is Readable // true 169 | someAny is ReadSpeakable // false 170 | 171 | someAny = sub 172 | someAny is Readable // true 173 | someAny is ReadSpeakable // true 174 | 175 | someAny = sup 176 | 177 | if let someReadable: Readable = someAny as? Readable { 178 | someReadable.read() 179 | } // read 180 | 181 | if let someReadSpeakable: ReadSpeakable = someAny as? ReadSpeakable { 182 | someReadSpeakable.speak() 183 | } // 동작하지 않음 184 | 185 | someAny = sub 186 | 187 | if let someReadable: Readable = someAny as? Readable { 188 | someReadable.read() 189 | } // read 190 | ``` 191 | 192 | 193 | ## 관련문서 194 | 195 | * [The Swift Programming Language - Protocols](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html) 196 | -------------------------------------------------------------------------------- /contents/19_protocol/protocol.swift: -------------------------------------------------------------------------------- 1 | /* 프로토콜 */ 2 | 3 | //프로토콜은 특정 역할을 수행하기 위한 4 | //메서드, 프로퍼티, 이니셜라이저 등의 요구사항을 정의합니다. 5 | 6 | //구조체, 클래스, 열거형은 프로토콜을 채택(Adopted)해서 7 | //프로토콜의 요구사항을 실제로 구현할 수 있습니다. 8 | //어떤 프로토콜의 요구사항을 모두 따르는 타입은 9 | //그 ‘프로토콜을 준수한다(Conform)’고 표현합니다. 10 | //프로토콜의 요구사항을 충족시키려면 프로토콜이 제시하는 기능을 11 | //모두 구현해야 합니다. 12 | 13 | import Swift 14 | 15 | //MARK: - 정의 문법 16 | 17 | /* 18 | protocol <#프로토콜 이름#> { 19 | /* 정의부 */ 20 | } 21 | */ 22 | 23 | protocol Talkable { 24 | 25 | // 프로퍼티 요구 26 | // 프로퍼티 요구는 항상 var 키워드를 사용합니다 27 | // get은 읽기만 가능해도 상관 없다는 뜻이며 28 | // get과 set을 모두 명시하면 29 | // 읽기 쓰기 모두 가능한 프로퍼티여야 합니다 30 | var topic: String { get set } 31 | var language: String { get } 32 | 33 | // 메서드 요구 34 | func talk() 35 | 36 | // 이니셜라이저 요구 37 | init(topic: String, language: String) 38 | } 39 | 40 | //MARK: - 프로토콜 채택 및 준수 41 | 42 | // Person 구조체는 Talkable 프로토콜을 채택했습니다 43 | struct Person: Talkable { 44 | // 프로퍼티 요구 준수 45 | var topic: String 46 | let language: String 47 | 48 | // 메서드 요구 준수 49 | func talk() { 50 | print("\(topic)에 대해 \(language)로 말합니다") 51 | } 52 | 53 | // 이니셜라이저 요구 준수 54 | init(topic: String, language: String) { 55 | self.topic = topic 56 | self.language = language 57 | } 58 | } 59 | 60 | 61 | //MARK: - 프로토콜 상속 62 | 63 | // 프로토콜은 클래스와 다르게 다중상속이 가능합니다 64 | /* 65 | protocol <#프로토콜 이름#>: <#부모 프로토콜 이름 목록#> { 66 | /* 정의부 */ 67 | } 68 | */ 69 | 70 | protocol Readable { 71 | func read() 72 | } 73 | protocol Writeable { 74 | func write() 75 | } 76 | protocol ReadSpeakable: Readable { 77 | // func read() 78 | func speak() 79 | } 80 | protocol ReadWriteSpeakable: Readable, Writeable { 81 | // func read() 82 | // func write() 83 | func speak() 84 | } 85 | 86 | struct SomeType: ReadWriteSpeakable { 87 | func read() { 88 | print("Read") 89 | } 90 | 91 | func write() { 92 | print("Write") 93 | } 94 | 95 | func speak() { 96 | print("Speak") 97 | } 98 | } 99 | 100 | //MARK: 클래스 상속과 프로토콜 101 | 102 | // 클래스에서 상속과 프로토콜 채택을 동시에 하려면 103 | // 상속받으려는 클래스를 먼저 명시하고 104 | // 그 뒤에 채택할 프로토콜 목록을 작성합니다 105 | class SuperClass: Readable { 106 | func read() { } 107 | } 108 | 109 | class SubClass: SuperClass, Writeable, ReadSpeakable { 110 | func write() { } 111 | func speak() { } 112 | } 113 | 114 | //MARK:- 프로토콜 준수 확인 115 | // 인스턴스가 특정 프로토콜을 준수하는지 확인할 수 있습니다 116 | // is, as 연산자 사용 117 | 118 | let sup: SuperClass = SuperClass() 119 | let sub: SubClass = SubClass() 120 | 121 | var someAny: Any = sup 122 | someAny is Readable // true 123 | someAny is ReadSpeakable // false 124 | 125 | someAny = sub 126 | 127 | someAny is Readable // true 128 | someAny is ReadSpeakable // true 129 | 130 | someAny = sup 131 | 132 | if let someReadable: Readable = someAny as? Readable { 133 | someReadable.read() 134 | } // read 135 | 136 | if let someReadSpeakable: ReadSpeakable = someAny as? ReadSpeakable { 137 | someReadSpeakable.speak() 138 | } // 동작하지 않음 139 | 140 | someAny = sub 141 | 142 | if let someReadable: Readable = someAny as? Readable { 143 | someReadable.read() 144 | } // read 145 | -------------------------------------------------------------------------------- /contents/20_extension/README.md: -------------------------------------------------------------------------------- 1 | # 익스텐션 2 | 3 | 4 | _익스텐션(Extension)_ 은 스위프트의 __강력한__ 기능 중 하나입니다. 익스텐션은 구조체, 클래스, 열거형, 프로토콜 타입에 새로운 __기능을 추가__할 수 있는 기능입니다. 5 | 6 | 기능을 추가하려는 타입의 구현된 소스 코드를 알지 못하거나 볼 수 없다 해도, 타입만 알고 있다면 그 타입의 기능을 확장할 수도 있습니다. 7 | 8 | > 스위프트의 익스텐션이 타입에 추가할 수 있는 기능 9 | 10 | * 연산 타입 프로퍼티 / 연산 인스턴스 프로퍼티 11 | * 타입 메서드 / 인스턴스 메서드 12 | * 이니셜라이저 13 | * 서브스크립트 14 | * 중첩 타입 15 | * 특정 프로토콜을 준수할 수 있도록 기능 추가 16 | 17 | 익스텐션은 타입에 새로운 기능을 추가할 수는 있지만, 기존에 존재하는 기능을 __재정의할 수는 없습니다.__ 18 | 19 | 클래스의 상속과 익스텐션을 비교해보겠습니다. 이 둘은 비슷해보이지만 실제 성격은 많이 다릅니다. 20 | 21 | 클래스의 상속은 클래스 타입에서만 가능하지만 익스텐션은 구조체, 클래스, 프로토콜 등에 적용이 가능합니다. 또 클래스의 상속은 특정 타입을 물려받아 하나의 새로운 타입을 정의하고 추가 기능을 구현하는 수직 확장이지만, 익스텐션은 기존의 타입에 기능을 추가하는 수평 확장입니다. 또, 상속을 받으면 기존 기능을 재정의할 수 있지만, 익스텐션은 재정의할 수 없다는 것도 큰 차이 중 하나입니다. 상황과 용도에 맞게 상속과 익스텐션을 선택하여 사용하면 됩니다. 22 | 23 | | | 상속 | 익스텐션 | 24 | |:---:|:---:|:---:| 25 | | 확장 | 수직 확장 | 수평 확장 | 26 | | 사용 | 클래스 타입 | 클래스, 구조체, 프로토콜, 제네릭 등 모든 타입 | 27 | | 재정의 | 가능 | 불가능 | 28 | 29 | 익스텐션을 사용하는 대신 원래 타입을 정의한 소스에 기능을 추가하는 방법도 있겠지만, 외부 라이브러리나 프레임워크를 가져다 썼다면 원본 소스를 수정하지 못합니다. 이처럼 외부에서 가져온 타입에 내가 원하는 기능을 추가하고자 할 때 익스텐션을 사용합니다. 따로 상속을 받지 않아도 되며, 구조체와 열거형에도 기능을 추가할 수 있으므로 익스텐션은 매우 편리한 기능입니다. 30 | 익스텐션은 모든 타입에 적용할 수 있습니다. 모든 타입이라 함은 구조체, 열거형, 클래스, 프로토콜, 제네릭 타입 등을 뜻합니다. 즉, 익스텐션을 통해 모든 타입에 연산 프로퍼티, 메서드, 이니셜라이저, 서브스크립트, 중첩 데이터 타입 등을 추가할 수 있습니다. 31 | 더불어 익스텐션은 __프로토콜과 함께 사용__하면 굉장히 강력한 기능을 선사합니다. 이 부분은 __프로토콜 중심 프로그래밍(Protocol Oriented Programming)__에 대해 더 알아보면 좋습니다. 32 | 33 | [![클릭하여 이동](http://img.youtube.com/vi/VRkCPbN_XPs/0.jpg)](http://www.youtube.com/watch?v=VRkCPbN_XPs "extension") 34 | 35 | [소스코드](extension.swift) 36 | 37 | ## 정의 문법 38 | 39 | `extension` 키워드를 사용하여 정의합니다. 40 | 41 | ```swift 42 | extension 확장할 타입 이름 { 43 | /* 타입에 추가될 새로운 기능 구현 */ 44 | } 45 | ``` 46 | 47 | 익스텐션은 기존에 존재하는 타입이 추가적으로 다른 프로토콜을 채택할 수 있도록 확장할 수도 있습니다. 이런 경우에는 클래스나 구조체에서 사용하던 것과 똑같은 방법으로 프로토콜 이름을 나열해줍니다. 48 | 49 | ```swift 50 | extension 확장할 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3... { 51 | /* 프로토콜 요구사항 구현 */ 52 | } 53 | ``` 54 | 55 | 스위프트 라이브러리를 살펴보면 실제로 익스텐션이 굉장히 많이 사용되고 있음을 알 수 있습니다. `Double` 타입에는 수많은 프로퍼티와 메서드, 이니셜라이저가 정의되어 있으며 수많은 프로토콜을 채택하고 있을 것이라고 예상되지만, 실제로 `Double` 타입의 정의를 살펴보면 그 모든것이 다 정의되어 있지는 않습니다. 56 | 그러면 `Double` 타입이 채택하고 준수해야 하는 수많은 프로토콜은 어디로 갔을까요? 어디에서 채택하고 어디에서 준수하도록 정의되어 있을까요? 당연히 답은 익스텐션입니다. 이처럼 스위프트 표준 라이브러리 타입의 기능은 대부분 익스텐션으로 구현되어 있습니다. `Double` 외에도 다른 타입들의 정의와 익스텐션을 찾아보면 더 많은 예를 보실 수 있습니다. 꼭 한 번 찾아보세요! 57 | 58 | 59 | 60 | 61 | ## 익스텐션 구현 62 | 63 | ### 연산 프로퍼티 추가 64 | ```swift 65 | extension Int { 66 | var isEven: Bool { 67 | return self % 2 == 0 68 | } 69 | var isOdd: Bool { 70 | return self % 2 == 1 71 | } 72 | } 73 | 74 | print(1.isEven) // false 75 | print(2.isEven) // true 76 | print(1.isOdd) // true 77 | print(2.isOdd) // false 78 | 79 | var number: Int = 3 80 | print(number.isEven) // false 81 | print(number.isOdd) // true 82 | 83 | number = 2 84 | print(number.isEven) // true 85 | print(number.isOdd) // false 86 | ``` 87 | 88 | 위 코드의 익스텐션은 `Int` 타입에 두 개의 **연산 프로퍼티**를 추가한 것입니다. `Int` 타입의 인스턴스가 홀수인지 짝수인지 판별하여 `Bool` 타입으로 알려주는 연산 프로퍼티입니다. 익스텐션으로 `Int` 타입에 추가해준 연산 프로퍼티는 `Int` 타입의 어떤 인스턴스에도 사용이 가능합니다. 위의 코드처럼 인스턴스 연산 프로퍼티를 추가할 수도 있으며, `static` 키워드를 사용하여 타입 연산 프로퍼티도 추가할 수 있습니다. 89 | 90 | 91 | 92 | ### 메서드 추가 93 | ```swift 94 | extension Int { 95 | func multiply(by n: Int) -> Int { 96 | return self * n 97 | } 98 | } 99 | print(3.multiply(by: 2)) // 6 100 | print(4.multiply(by: 5)) // 20 101 | 102 | number = 3 103 | print(number.multiply(by: 2)) // 6 104 | print(number.multiply(by: 3)) // 9 105 | ``` 106 | 107 | 위 코드의 익스텐션을 통해 `Int` 타입에 **인스턴스 메서드**인 `multiply(by:)` 메서드를 추가했습니다. 여러 기능을 여러 익스텐션 블록으로 나눠서 구현해도 전혀 문제가 없습니다. 관련된 기능별로 하나의 익스텐션 블록에 묶어주는 것도 좋습니다. 108 | 109 | 110 | ### 이니셜라이저 추가 111 | ```swift 112 | extension String { 113 | init(int: Int) { 114 | self = "\(int)" 115 | } 116 | 117 | init(double: Double) { 118 | self = "\(double)" 119 | } 120 | } 121 | 122 | let stringFromInt: String = String(int: 100) 123 | // "100" 124 | 125 | let stringFromDouble: String = String(double: 100.0) 126 | // "100.0" 127 | ``` 128 | 129 | 인스턴스를 초기화(이니셜라이즈)할 때 인스턴스 초기화에 필요한 다양한 데이터를 전달받을 수 있도록 여러 종류의 이니셜라이저를 만들 수 있습니다. 타입의 정의부에 이니셜라이저를 추가하지 않더라도 익스텐션을 통해 이니셜라이저를 추가할 수 있습니다. 130 | 하지만 익스텐션으로 클래스 타입에 편의 이니셜라이저는 추가할 수 있지만, 지정 이니셜라이저는 추가할 수 없습니다. 지정 이니셜라이저와 디이니셜라이저는 반드시 클래스 타입의 구현부에 위치해야 합니다(값 타입은 상관없습니다). 131 | 132 | 133 | ## 관련문서 134 | 135 | * [The Swift Programming Language - Extensions](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Extensions.html) 136 | -------------------------------------------------------------------------------- /contents/20_extension/extension.swift: -------------------------------------------------------------------------------- 1 | /* 익스텐션 */ 2 | 3 | //익스텐션은 구조체, 클래스, 열거형, 프로토콜 타입에 4 | //새로운 기능을 추가할 수 있는 기능입니다. 5 | //기능을 추가하려는 타입의 구현된 소스 코드를 6 | //알지 못하거나 볼 수 없다 해도, 7 | //타입만 알고 있다면 그 타입의 기능을 확장할 수도 있습니다. 8 | 9 | //익스텐션으로 추가할 수 있는 기능 10 | //연산 타입 프로퍼티 / 연산 인스턴스 프로퍼티 11 | //타입 메서드 / 인스턴스 메서드 12 | //이니셜라이저 13 | //서브스크립트 14 | //중첩 타입 15 | //특정 프로토콜을 준수할 수 있도록 기능 추가 16 | 17 | //기존에 존재하는 기능을 재정의할 수는 없습니다 18 | 19 | import Swift 20 | 21 | //MARK: - 정의 문법 22 | 23 | /* 24 | extension <#확장할 타입 이름#> { 25 | /* 타입에 추가될 새로운 기능 구현 */ 26 | } 27 | */ 28 | 29 | //익스텐션은 기존에 존재하는 타입이 30 | //추가적으로 다른 프로토콜을 채택할 수 있도록 31 | //확장할 수도 있습니다. 32 | 33 | /* 34 | extension <#확장할 타입 이름#>: <#프로토콜1#>, <#프로토콜2#>, <#프로토콜3#>... { 35 | /* 프로토콜 요구사항 구현 */ 36 | } 37 | */ 38 | 39 | //MARK: - 익스텐션 구현 40 | 41 | //MARK: 연산 프로퍼티 추가 42 | 43 | extension Int { 44 | var isEven: Bool { 45 | return self % 2 == 0 46 | } 47 | var isOdd: Bool { 48 | return self % 2 == 1 49 | } 50 | } 51 | 52 | print(1.isEven) // false 53 | print(2.isEven) // true 54 | print(1.isOdd) // true 55 | print(2.isOdd) // false 56 | 57 | var number: Int = 3 58 | print(number.isEven) // false 59 | print(number.isOdd) // true 60 | 61 | number = 2 62 | print(number.isEven) // true 63 | print(number.isOdd) // false 64 | 65 | 66 | 67 | //MARK: 메서드 추가 68 | 69 | extension Int { 70 | func multiply(by n: Int) -> Int { 71 | return self * n 72 | } 73 | } 74 | print(3.multiply(by: 2)) // 6 75 | print(4.multiply(by: 5)) // 20 76 | 77 | number = 3 78 | print(number.multiply(by: 2)) // 6 79 | print(number.multiply(by: 3)) // 9 80 | 81 | 82 | //MARK: 이니셜라이저 추가 83 | 84 | extension String { 85 | init(int: Int) { 86 | self = "\(int)" 87 | } 88 | 89 | init(double: Double) { 90 | self = "\(double)" 91 | } 92 | } 93 | 94 | let stringFromInt: String = String(int: 100) 95 | // "100" 96 | 97 | let stringFromDouble: String = String(double: 100.0) 98 | // "100.0" 99 | -------------------------------------------------------------------------------- /contents/21_error_handling/README.md: -------------------------------------------------------------------------------- 1 | # 오류처리 2 | 3 | 스위프트에서 **오류(Error)**는 `Error`라는 프로토콜을 준수하는 타입의 값을 통해 표현됩니다. `Error` 프로토콜은 사실상 요구사항이 없는 빈 프로토콜일 뿐이지만, 오류를 표현하기 위한 타입(주로 열거형)은 이 프로토콜을 채택합니다. 스위프트의 열거형은 오류의 종류를 나타내기에 아주 적합한 기능입니다. 연관 값을 통해 오류에 관한 부가 정보를 제공할 수도 있습니다. 4 | 5 | 이번 예제에는 프로그램 내에서 자판기를 작동시키려고 할 때 발생하는 오류상황을 구현해 보았습니다. 6 | 7 | 8 | [![클릭하여 이동](http://img.youtube.com/vi/l0fGtNnNsJg/0.jpg)](http://www.youtube.com/watch?v=l0fGtNnNsJg "error_handling") 9 | 10 | [소스코드](error_handling.swift) 11 | 12 | ## 오류표현 13 | 14 | `Error` 프로토콜과 (주로)열거형을 통해서 오류를 표현합니다 15 | 16 | ```swift 17 | enum 오류종류이름: Error { 18 | case 종류1 19 | case 종류2 20 | case 종류3 21 | //... 22 | } 23 | ``` 24 | 25 | > 자판기 동작 오류의 종류를 표현한 VendingMachineError 열거형 26 | 27 | ```swift 28 | enum VendingMachineError: Error { 29 | case invalidInput 30 | case insufficientFunds(moneyNeeded: Int) 31 | case outOfStock 32 | } 33 | 34 | ``` 35 | 36 | 37 | ## 함수에서 발생한 오류 던지기 38 | 39 | 자판기 동작 도중 발생한 오류를 던지는 메서드를 구현해봅니다. 40 | 오류 발생의 여지가 있는 메서드는 `throws`를 사용하여 오류를 내포하는 함수임을 표시합니다. 41 | 42 | ```swift 43 | class VendingMachine { 44 | let itemPrice: Int = 100 45 | var itemCount: Int = 5 46 | var deposited: Int = 0 47 | 48 | // 돈 받기 메서드 49 | func receiveMoney(_ money: Int) throws { 50 | 51 | // 입력한 돈이 0이하면 오류를 던집니다 52 | guard money > 0 else { 53 | throw VendingMachineError.invalidInput 54 | } 55 | 56 | // 오류가 없으면 정상처리를 합니다 57 | self.deposited += money 58 | print("\(money)원 받음") 59 | } 60 | 61 | // 물건 팔기 메서드 62 | func vend(numberOfItems numberOfItemsToVend: Int) throws -> String { 63 | 64 | // 원하는 아이템의 수량이 잘못 입력되었으면 오류를 던집니다 65 | guard numberOfItemsToVend > 0 else { 66 | throw VendingMachineError.invalidInput 67 | } 68 | 69 | // 구매하려는 수량보다 미리 넣어둔 돈이 적으면 오류를 던집니다 70 | guard numberOfItemsToVend * itemPrice <= deposited else { 71 | let moneyNeeded: Int 72 | moneyNeeded = numberOfItemsToVend * itemPrice - deposited 73 | 74 | throw VendingMachineError.insufficientFunds(moneyNeeded: moneyNeeded) 75 | } 76 | 77 | // 구매하려는 수량보다 요구하는 수량이 많으면 오류를 던집니다 78 | guard itemCount >= numberOfItemsToVend else { 79 | throw VendingMachineError.outOfStock 80 | } 81 | 82 | // 오류가 없으면 정상처리를 합니다 83 | let totalPrice = numberOfItemsToVend * itemPrice 84 | 85 | self.deposited -= totalPrice 86 | self.itemCount -= numberOfItemsToVend 87 | 88 | return "\(numberOfItemsToVend)개 제공함" 89 | } 90 | } 91 | 92 | // 자판기 인스턴스 93 | let machine: VendingMachine = VendingMachine() 94 | 95 | // 판매 결과를 전달받을 변수 96 | var result: String? 97 | ``` 98 | 99 | 100 | ## 오류처리 101 | 102 | 오류를 던질 수도 있지만 오류가 던져지는 것에 대비하여 던져진 오류를 처리하기 위한 코드도 작성해야 합니다. 예를 들어 던져진 오류가 무엇인지 판단하여 다시 문제를 해결한다든지, 다른 방법으로 시도해 본다든지, 사용자에게 오류를 알리고 사용자에게 선택 권한을 넘겨주어 다음에 어떤 동작을 하게 할 것인지 결정하도록 유도하는 등의 코드를 작성해야 합니다. 103 | 104 | 오류발생의 여지가 있는 `throws` 함수(메서드)는 `try`를 사용하여 호출해야합니다. 105 | `try`와 `do-catch`, `try?`와 `try!` 등에 대해 알아봅니다. 106 | 107 | ### `do-catch` 108 | 109 | 오류발생의 여지가 있는 `throws` 함수(메서드)는 `do-catch` 구문을 활용하여 오류발생에 대비합니다. 110 | 111 | > 가장 정석적인 방법으로 모든 오류 케이스에 대응합니다 112 | ```swift 113 | do { 114 | try machine.receiveMoney(0) 115 | } catch VendingMachineError.invalidInput { 116 | print("입력이 잘못되었습니다") 117 | } catch VendingMachineError.insufficientFunds(let moneyNeeded) { 118 | print("\(moneyNeeded)원이 부족합니다") 119 | } catch VendingMachineError.outOfStock { 120 | print("수량이 부족합니다") 121 | } // 입력이 잘못되었습니다 122 | ``` 123 | 124 | > 하나의 catch 블럭에서 switch 구문을 사용하여 오류를 분류해봅니다. 굳이 위의 것과 크게 다를 것이 없습니다. 125 | 126 | ```swift 127 | do { 128 | try machine.receiveMoney(300) 129 | } catch /*(let error)*/ { 130 | 131 | switch error { 132 | case VendingMachineError.invalidInput: 133 | print("입력이 잘못되었습니다") 134 | case VendingMachineError.insufficientFunds(let moneyNeeded): 135 | print("\(moneyNeeded)원이 부족합니다") 136 | case VendingMachineError.outOfStock: 137 | print("수량이 부족합니다") 138 | default: 139 | print("알수없는 오류 \(error)") 140 | } 141 | } // 300원 받음 142 | ``` 143 | 144 | > 딱히 케이스별로 오류처리 할 필요가 없으면 catch 구문 내부를 간략화해도 무방합니다. 145 | ```swift 146 | do { 147 | result = try machine.vend(numberOfItems: 4) 148 | } catch { 149 | print(error) 150 | } // insufficientFunds(100) 151 | ``` 152 | 153 | > 딱히 케이스별로 오류처리 할 필요가 없으면 do 구문만 써도 무방합니다 154 | ```swift 155 | do { 156 | result = try machine.vend(numberOfItems: 4) 157 | } 158 | ``` 159 | 160 | ### `try?` 와 `try!` 161 | 162 | #### `try?` 163 | 164 | 별도의 오류처리 결과를 통보받지 않고 오류가 발생했으면 결과값을 `nil`로 돌려받을 수 있습니다. 정상동작 후에는 옵셔널 타입으로 정상 반환값을 돌려 받습니다. 165 | 166 | ```swift 167 | result = try? machine.vend(numberOfItems: 2) 168 | result // Optional("2개 제공함") 169 | 170 | result = try? machine.vend(numberOfItems: 2) 171 | result // nil 172 | ``` 173 | #### `try!` 174 | 175 | 오류가 발생하지 않을 것이라는 강력한 확신을 가질 때 `try!`를 사용하면 정상동작 후에 바로 결과값을 돌려받습니다. 오류가 발생하면 런타임 오류가 발생하여 애플리케이션 동작이 중지됩니다. 176 | 177 | ```swift 178 | result = try! machine.vend(numberOfItems: 1) 179 | result // 1개 제공함 180 | 181 | //result = try! machine.vend(numberOfItems: 1) 182 | // 런타임 오류 발생! 183 | ``` 184 | 185 | ## 더 알아보기 186 | 187 | 추가적으로 더 알아보면 좋은 개념입니다. 188 | 189 | * `rethrows` 190 | * `defer` 191 | 192 | ## 관련문서 193 | 194 | * [The Swift Programming Language - Error Handling](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) 195 | * [The Swift Programming Language - Enumerations](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html) 196 | -------------------------------------------------------------------------------- /contents/21_error_handling/error_handling.swift: -------------------------------------------------------------------------------- 1 | /* 오류처리 */ 2 | 3 | import Swift 4 | 5 | //MARK: - 오류표현 6 | //Error 프로토콜과 (주로) 열거형을 통해서 오류를 표현합니다 7 | 8 | /* 9 | enum <#오류종류이름#>: Error { 10 | case <#종류1#> 11 | case <#종류2#> 12 | case <#종류3#> 13 | //... 14 | } 15 | */ 16 | 17 | 18 | // 자판기 동작 오류의 종류를 표현한 VendingMachineError 열거형 19 | enum VendingMachineError: Error { 20 | case invalidInput 21 | case insufficientFunds(moneyNeeded: Int) 22 | case outOfStock 23 | } 24 | 25 | //MARK:- 함수에서 발생한 오류 던지기 26 | 27 | // 자판기 동작 도중 발생한 오류 던지기 28 | // 오류 발생의 여지가 있는 메서드는 throws를 사용하여 29 | // 오류를 내포하는 함수임을 표시합니다 30 | class VendingMachine { 31 | let itemPrice: Int = 100 32 | var itemCount: Int = 5 33 | var deposited: Int = 0 34 | 35 | // 돈 받기 메서드 36 | func receiveMoney(_ money: Int) throws { 37 | 38 | // 입력한 돈이 0이하면 오류를 던집니다 39 | guard money > 0 else { 40 | throw VendingMachineError.invalidInput 41 | } 42 | 43 | // 오류가 없으면 정상처리를 합니다 44 | self.deposited += money 45 | print("\(money)원 받음") 46 | } 47 | 48 | // 물건 팔기 메서드 49 | func vend(numberOfItems numberOfItemsToVend: Int) throws -> String { 50 | 51 | // 원하는 아이템의 수량이 잘못 입력되었으면 오류를 던집니다 52 | guard numberOfItemsToVend > 0 else { 53 | throw VendingMachineError.invalidInput 54 | } 55 | 56 | // 구매하려는 수량보다 미리 넣어둔 돈이 적으면 오류를 던집니다 57 | guard numberOfItemsToVend * itemPrice <= deposited else { 58 | let moneyNeeded: Int 59 | moneyNeeded = numberOfItemsToVend * itemPrice - deposited 60 | 61 | throw VendingMachineError.insufficientFunds(moneyNeeded: moneyNeeded) 62 | } 63 | 64 | // 구매하려는 수량보다 요구하는 수량이 많으면 오류를 던집니다 65 | guard itemCount >= numberOfItemsToVend else { 66 | throw VendingMachineError.outOfStock 67 | } 68 | 69 | // 오류가 없으면 정상처리를 합니다 70 | let totalPrice = numberOfItemsToVend * itemPrice 71 | 72 | self.deposited -= totalPrice 73 | self.itemCount -= numberOfItemsToVend 74 | 75 | return "\(numberOfItemsToVend)개 제공함" 76 | } 77 | } 78 | 79 | // 자판기 인스턴스 80 | let machine: VendingMachine = VendingMachine() 81 | 82 | // 판매 결과를 전달받을 변수 83 | var result: String? 84 | 85 | 86 | //MARK:- 오류처리 87 | 88 | //오류발생의 여지가 있는 throws 함수(메서드)는 89 | //try를 사용하여 호출해야합니다 90 | //try, try?, try! 91 | 92 | 93 | //MARK: do-catch 94 | 95 | //오류발생의 여지가 있는 throws 함수(메서드)는 96 | //do-catch 구문을 활용하여 97 | //오류발생에 대비합니다 98 | 99 | // 가장 정석적인 방법으로 모든 오류 케이스에 대응합니다 100 | do { 101 | try machine.receiveMoney(0) 102 | } catch VendingMachineError.invalidInput { 103 | print("입력이 잘못되었습니다") 104 | } catch VendingMachineError.insufficientFunds(let moneyNeeded) { 105 | print("\(moneyNeeded)원이 부족합니다") 106 | } catch VendingMachineError.outOfStock { 107 | print("수량이 부족합니다") 108 | } // 입력이 잘못되었습니다 109 | 110 | 111 | // 하나의 catch 블럭에서 switch 구문을 사용하여 112 | // 오류를 분류해봅니다 113 | // 굳이 위의 것과 크게 다를 것이 없습니다 114 | do { 115 | try machine.receiveMoney(300) 116 | } catch /*(let error)*/ { 117 | 118 | switch error { 119 | case VendingMachineError.invalidInput: 120 | print("입력이 잘못되었습니다") 121 | case VendingMachineError.insufficientFunds(let moneyNeeded): 122 | print("\(moneyNeeded)원이 부족합니다") 123 | case VendingMachineError.outOfStock: 124 | print("수량이 부족합니다") 125 | default: 126 | print("알수없는 오류 \(error)") 127 | } 128 | } // 300원 받음 129 | 130 | 131 | // 딱히 케이스별로 오류처리 할 필요가 없으면 132 | // catch 구문 내부를 간략화해도 무방합니다 133 | do { 134 | result = try machine.vend(numberOfItems: 4) 135 | } catch { 136 | print(error) 137 | } // insufficientFunds(100) 138 | 139 | // 딱히 케이스별로 오류처리 할 필요가 없으면 do 구문만 써도 무방합니다 140 | do { 141 | result = try machine.vend(numberOfItems: 4) 142 | } 143 | 144 | 145 | //MARK: try? 와 try! 146 | 147 | //try? 148 | //별도의 오류처리 결과를 통보받지 않고 149 | //오류가 발생했으면 결과값을 nil로 돌려받을 수 있습니다 150 | //정상동작 후에는 옵셔널 타입으로 정상 반환값을 돌려 받습니다 151 | 152 | result = try? machine.vend(numberOfItems: 2) 153 | result // Optional("2개 제공함") 154 | 155 | result = try? machine.vend(numberOfItems: 2) 156 | result // nil 157 | 158 | //try! 159 | //오류가 발생하지 않을 것이라는 강력한 확신을 가질 때 160 | //try!를 사용하면 정상동작 후에 바로 결과값을 돌려받습니다 161 | //오류가 발생하면 런타임 오류가 발생하여 162 | //애플리케이션 동작이 중지됩니다 163 | 164 | result = try! machine.vend(numberOfItems: 1) 165 | result // 1개 제공함 166 | 167 | //result = try! machine.vend(numberOfItems: 1) 168 | // 런타임 오류 발생! 169 | 170 | 171 | /* 172 | 더 알아보기 : rethrows, defer 173 | */ 174 | -------------------------------------------------------------------------------- /contents/22_higher_order_function/README.md: -------------------------------------------------------------------------------- 1 | # 고차함수 2 | 3 | **고차함수(Higher-order function)**은 '다른 함수를 전달인자로 받거나 함수실행의 결과를 함수로 반환하는 함수'를 뜻합니다. 4 | 5 | 스위프트의 함수(클로저)는 일급시민이기 때문에 함수의 전달인자로 전달할 수 있으며, 함수의 결과값으로 반환할 수 있습니다. 6 | 7 | 이번 파트에서는 스위프트 표준라이브러리에서 제공하는 유용한 고차함수에 대해 알아봅니다. 8 | 9 | * `map` 10 | * `filter` 11 | * `reduce` 12 | 13 | `map`, `filter`, `reduce` 함수는 스위프트 표준 라이브러리의 컨테이너 타입(`Array`, `Set`, `Dictionary` 등)에 구현되어 있습니다. 14 | 15 | [![클릭하여 이동](http://img.youtube.com/vi/HmabXrK2tRo/0.jpg)](http://www.youtube.com/watch?v=HmabXrK2tRo "higher_order_function") 16 | 17 | [소스코드](higher_order_function.swift) 18 | 19 | 20 | ## `map` 21 | 22 | `map`함수는 컨테이너 내부의 **기존 데이터를 변형(transform)하여 새로운 컨테이너를 생성**합니다. 23 | 24 | > 변형하고자 하는 numbers와 변형 결과를 받을 doubledNumbers, strings 25 | 26 | ```swift 27 | let numbers: [Int] = [0, 1, 2, 3, 4] 28 | var doubledNumbers: [Int] 29 | var strings: [String] 30 | ``` 31 | 32 | > 기존의 for 구문 사용 33 | 34 | ```swift 35 | doubledNumbers = [Int]() 36 | strings = [String]() 37 | 38 | for number in numbers { 39 | doubledNumbers.append(number * 2) 40 | strings.append("\(number)") 41 | } 42 | 43 | print(doubledNumbers) // [0, 2, 4, 6, 8] 44 | print(strings) // ["0", "1", "2", "3", "4"] 45 | ``` 46 | 47 | > map 메서드 사용 48 | 49 | ```swift 50 | // numbers의 각 요소를 2배하여 새로운 배열 반환 51 | doubledNumbers = numbers.map({ (number: Int) -> Int in 52 | return number * 2 53 | }) 54 | 55 | // numbers의 각 요소를 문자열로 변환하여 새로운 배열 반환 56 | strings = numbers.map({ (number: Int) -> String in 57 | return "\(number)" 58 | }) 59 | 60 | print(doubledNumbers) // [0, 2, 4, 6, 8] 61 | print(strings) // ["0", "1", "2", "3", "4"] 62 | 63 | // 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저 64 | doubledNumbers = numbers.map { $0 * 2 } 65 | print(doubledNumbers) // [0, 2, 4, 6, 8] 66 | ``` 67 | 68 | ## `filter` 69 | 70 | `filter`함수는 컨테이너 내부의 **값을 걸러서 새로운 컨테이너로 추출**합니다. 71 | 72 | > 기존의 for 구문 사용 73 | 74 | ```swift 75 | // 변수 사용에 주목하세요 76 | var filtered: [Int] = [Int]() 77 | 78 | for number in numbers { 79 | if number % 2 == 0 { 80 | filtered.append(number) 81 | } 82 | } 83 | 84 | print(filtered) // [0, 2, 4] 85 | ``` 86 | 87 | > filter 메서드 사용 88 | 89 | ```swift 90 | // numbers의 요소 중 짝수를 걸러내어 새로운 배열로 반환 91 | let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in 92 | return number % 2 == 0 93 | } 94 | print(evenNumbers) // [0, 2, 4] 95 | 96 | // 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저 97 | let oddNumbers: [Int] = numbers.filter { 98 | $0 % 2 != 0 99 | } 100 | print(oddNumbers) // [1, 3] 101 | ``` 102 | 103 | 104 | ## `reduce` 105 | 106 | `reduce`함수는 컨테이너 내부의 **콘텐츠를 하나로 통합**합니다. 107 | 108 | > 통합하고자 하는 someNumbers 109 | 110 | ```swift 111 | let someNumbers: [Int] = [2, 8, 15] 112 | ``` 113 | 114 | > 기존의 for 구문 사용 115 | 116 | ```swift 117 | // 변수 사용에 주목하세요 118 | var result: Int = 0 119 | 120 | // someNumbers의 모든 요소를 더합니다 121 | for number in someNumbers { 122 | result += number 123 | } 124 | 125 | print(result) // 25 126 | ``` 127 | 128 | > reduce 메서드 사용 129 | 130 | ```swift 131 | // 초깃값이 0이고 someNumbers 내부의 모든 값을 더합니다. 132 | let sum: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in 133 | //print("\(first) + \(second)") //어떻게 동작하는지 확인해보세요 134 | return first + second 135 | }) 136 | 137 | print(sum) // 25 138 | 139 | // 초깃값이 0이고 someNumbers 내부의 모든 값을 뺍니다. 140 | var subtract: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in 141 | //print("\(first) - \(second)") //어떻게 동작하는지 확인해보세요 142 | return first - second 143 | }) 144 | 145 | print(subtract) // -25 146 | 147 | // 초깃값이 3이고 someNumbers 내부의 모든 값을 더합니다. 148 | let sumFromThree = someNumbers.reduce(3) { $0 + $1 } 149 | 150 | print(sumFromThree) // 28 151 | ``` 152 | 153 | 154 | ----- 155 | 156 | `reduce` 메서드에 전달하는 클로저의 매개변수 이름을 `first`, `second` 보다는 `result`, `currentItem`과 같은 이름으로 정정하는 것이 좋겠습니다. 첫 번째 매개변수는 초깃값으로부터 출발하여 마지막 요소까지 순회하는 내내의 결괏값입니다. `currentItem`은 현재 순회하고 있는 요소의 값을 뜻합니다. 결국 `return result + currentItem`이라고 표현한다면 이제까지 더해진 결괏값에 이번 요소의 값을 더한다는 뜻이 되겠습니다. 157 | 158 | ------ 159 | 160 | ## 더 알아보기 161 | `flatMap` 162 | -------------------------------------------------------------------------------- /contents/22_higher_order_function/higher_order_function.swift: -------------------------------------------------------------------------------- 1 | /* 고차함수 */ 2 | 3 | import Swift 4 | 5 | //전달인자로 함수를 전달받거나 6 | //함수실행의 결과를 함수로 반환하는 함수 7 | 8 | //스위프트 표준라이브러리에서 제공하는 9 | //유용한 고차함수에 대해 알아봅니다 10 | 11 | //map, filter, reduce 12 | //컨테이너 타입(Array, Set, Dictionary 등)에 구현되어 있습니다 13 | 14 | //MARK:- map 15 | //컨테이너 내부의 기존 데이터를 변형(transform)하여 새로운 컨테이너 생성 16 | 17 | let numbers: [Int] = [0, 1, 2, 3, 4] 18 | var doubledNumbers: [Int] 19 | var strings: [String] 20 | 21 | // for 구문 사용 22 | doubledNumbers = [Int]() 23 | strings = [String]() 24 | 25 | for number in numbers { 26 | doubledNumbers.append(number * 2) 27 | strings.append("\(number)") 28 | } 29 | 30 | print(doubledNumbers) // [0, 2, 4, 6, 8] 31 | print(strings) // ["0", "1", "2", "3", "4"] 32 | 33 | // map 메서드 사용 34 | // numbers의 각 요소를 2배하여 새로운 배열 반환 35 | doubledNumbers = numbers.map({ (number: Int) -> Int in 36 | return number * 2 37 | }) 38 | 39 | // numbers의 각 요소를 문자열로 변환하여 새로운 배열 반환 40 | strings = numbers.map({ (number: Int) -> String in 41 | return "\(number)" 42 | }) 43 | 44 | print(doubledNumbers) // [0, 2, 4, 6, 8] 45 | print(strings) // ["0", "1", "2", "3", "4"] 46 | 47 | // 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저 48 | doubledNumbers = numbers.map { $0 * 2 } 49 | print(doubledNumbers) // [0, 2, 4, 6, 8] 50 | 51 | 52 | //MARK:- filter 53 | //컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출 54 | 55 | // for 구문 사용 56 | 57 | // 변수 사용에 주목하세요 58 | var filtered: [Int] = [Int]() 59 | 60 | for number in numbers { 61 | if number % 2 == 0 { 62 | filtered.append(number) 63 | } 64 | } 65 | 66 | print(filtered) // [0, 2, 4] 67 | 68 | // filter 메서드 사용 69 | // numbers의 요소 중 짝수를 걸러내어 새로운 배열로 반환 70 | let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in 71 | return number % 2 == 0 72 | } 73 | print(evenNumbers) // [0, 2, 4] 74 | 75 | // 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저 76 | let oddNumbers: [Int] = numbers.filter { 77 | $0 % 2 != 0 78 | } 79 | print(oddNumbers) // [1, 3] 80 | 81 | 82 | 83 | //MARK:- reduce 84 | // 컨테이너 내부의 콘텐츠를 하나로 통합 85 | 86 | let someNumbers: [Int] = [2, 8, 15] 87 | 88 | // for 구문 사용 89 | // 변수 사용에 주목하세요 90 | var result: Int = 0 91 | 92 | // someNumbers의 모든 요소를 더합니다 93 | for number in someNumbers { 94 | result += number 95 | } 96 | 97 | print(result) // 25 98 | 99 | 100 | // reduce 메서드 사용 101 | 102 | // 초깃값이 0이고 someNumbers 내부의 모든 값을 더합니다. 103 | let sum: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in 104 | //print("\(first) + \(second)") //어떻게 동작하는지 확인해보세요 105 | return first + second 106 | }) 107 | 108 | print(sum) // 25 109 | 110 | // 초깃값이 0이고 someNumbers 내부의 모든 값을 뺍니다. 111 | var subtract: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in 112 | //print("\(first) - \(second)") //어떻게 동작하는지 확인해보세요 113 | return first - second 114 | }) 115 | 116 | print(subtract) // -25 117 | 118 | // 초깃값이 3이고 someNumbers 내부의 모든 값을 더합니다. 119 | let sumFromThree = someNumbers.reduce(3) { $0 + $1 } 120 | 121 | print(sumFromThree) // 28 122 | 123 | /* 124 | 더 알아보기 : flatMap 125 | */ 126 | -------------------------------------------------------------------------------- /contents/23_more/README.md: -------------------------------------------------------------------------------- 1 | # 더 알아보기 2 | 3 | 추가적으로 알아가야 할 문법과 개념들을 모아봤습니다. 4 | 5 | * 제네릭(Generics) 6 | * 서브스크립트(Subscript) 7 | * 접근수준(Access Control) 8 | * ARC(Automatic Reference Counting) 9 | * 중첩타입(Nested Types) 10 | * 사용자정의 연산자(Custom Operators) 11 | * 오류 처리(Error Handling) 12 | * 불명확 타입(Opaque Types) 13 | * 프로토콜 지향 프로그래밍(Protocol Oriented Programming) 14 | 15 | 스위프트 기본문법 강좌는 여기서 마칩니다. 많은 도움이 되었길 소망합니다. 16 | 17 | 18 | ## yagom 19 | 20 | * [Blog](http://blog.yagom.net) 21 | * [Facebook](https://fb.com/yagomsoft) 22 | * [Facebook Page](https://fb.com/yagompage) 23 | * [Facebook Group](https://fb.com/groups/yagom) 24 | * [Contacts](https://yagom.github.io/contacts) 25 | 26 | ## 관련저서 27 | 28 | ![스위프트 프로그래밍](http://www.hanbit.co.kr/data/books/B9421379018_l.jpg) 29 | 30 | [스위프트 프로그래밍](http://www.hanbit.co.kr/media/books/book_view.html?p_code=B9421379018) -------------------------------------------------------------------------------- /images/yagom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yagom/swift_basic/4db5b6c463c6a513fd2edb93fa760280155344a4/images/yagom.png -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | # Swift 기본문법 2 | 3 | ## 수강대상 4 | * 프로그래밍에 대한 기초지식이 있는 컴퓨터관련 전공생 5 | * 다른 언어를 사용해 본 프로그래머 6 | * (객체지향) 프로그래밍 유경험자 7 | 8 | ## 강의개요 9 | 기존의 프로그래머 또는 프로그래밍 개념이 있는 사람이 빠르게 스위프트 문법을 익힐 수 있도록 스위프트의 기초적인 핵심문법을 제공 10 | 11 | ### 사전숙지사항 12 | 스위프트는 문법표현의 다양성이 매우 높은 언어입니다. 그래서 스위프트 문법의 모든 형태를 알기는 꽤 오랜 시간이 걸립니다. 그렇지만 최소한의 핵심 문법을 통해 무리없이 스위프트 문법을 익힐 수 있도록 간단한 예제와 함께 설명합니다. 13 | 스위프트 **문법의 모든 내용을 포함하지는 않으며**, 깊은 내용보다는 **핵심적인 내용**만을 전달합니다. 14 | 먼저 핵심적인 기초문법을 익힌 후 Apple의 [Swift Programming Language Guide](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html), [Swift Programming Language Guide - iBooks](https://itunes.apple.com/kr/book/swift-programming-language/id881256329?mt=11) 또는 [스위프트 관련 서적](http://book.naver.com/search/search.nhn?sm=sta_hty.book&sug=&where=nexearch&query=스위프트+프로그래밍)을 참고하면 좋습니다. 15 | 16 | 해당 강의는 **Swift 5.1 버전이 기준**입니다. 17 | 다른 버전의 문법은 상이할 수 있으니 [Swift 가이드 문서 변경 내역](https://docs.swift.org/swift-book/RevisionHistory/RevisionHistory.html)을 참고하면 좋습니다. 18 | 19 | 또, **스위프트의 [API 가이드라인](https://swift.org/documentation/api-design-guidelines/)** 에 따라 이름을 짓는 것이 좋습니다. 20 | 21 | **스위프트 언어 자체의 문법과 활용만을 다룹니다.** iOS / macOS 등 애플리케이션 제작을 위한 프레임워크 관련한 내용은 다루지 않습니다. 22 | 23 | ## 지식공유자 24 | 25 | ### yagom 26 | 27 | * [Blog](http://blog.yagom.net) 28 | * [Facebook](https://fb.com/yagomsoft) 29 | * [Facebook Page](https://fb.com/yagompage) 30 | * [Facebook Group](https://fb.com/groups/yagom) 31 | * [Contacts](https://yagom.github.io/contacts) 32 | 33 | 야곰의 콘텐츠가 유익한가요? 여러분의 후원은 더 나은 콘텐츠의 자양분입니다 :D 34 | **후원하기** : 35 | 36 | 37 | 38 | ## 강의 목록 39 | 40 | > 강의 영상은 [유투브 채널](https://www.youtube.com/channel/UCkwWWEv3C-3ToeO57r5LCHQ)을 구독하시면 업데이트 때마다 받아보실 수 있습니다 41 | 42 | 43 | 44 |
45 | 46 | 또, [야곰닷넷 - yagom.net](https://yagom.net/courses/swift-basic/), [구름EDU](https://edu.goorm.io/lecture/1141/%EC%95%BC%EA%B3%B0%EC%9D%98-%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D), [인프런](https://www.inflearn.com/course/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95/)에서도 수강하실 수 있습니다. 47 | 48 | ### 목차 49 | 50 | * [강좌소개](contents/00_introduction/README.md) 51 | * [이름짓기, 콘솔로그, 문자열 보간법](contents/00_introduction/console_log/README.md) 52 | * [상수와 변수](contents/01_let_var/README.md) 53 | * [기본 데이터 타입](contents/02_data_types/README.md) 54 | * [Any, AnyObject, nil](contents/02_data_types/README.md) 55 | * [컬렉션 타입](contents/03_collection_types/README.md) 56 | * [함수 기본](contents/04_function/README.md) 57 | * [함수 고급](contents/04_function/README.md) 58 | * [조건문](contents/05_conditional/README.md) 59 | * [반복문](contents/06_loop/README.md) 60 | * [옵셔널](contents/07_optional/README.md) 61 | * [구조체](contents/08_struct/README.md) 62 | * [클래스](contents/09_class/README.md) 63 | * [열거형](contents/10_enum/README.md) 64 | * [값 타입과 참조 타입](contents/11_value_reference/README.md) 65 | * [클로저](contents/12_closure/README.md) 66 | * [프로퍼티](contents/13_property/README.md) 67 | * [상속](contents/14_inheritance/README.md) 68 | * [인스턴스의 생성과 소멸](contents/15_init_deinit/README.md) 69 | * [옵셔널 체이닝과 nil 병합 연산자](contents/16_optional_chaining/README.md) 70 | * [타입캐스팅](contents/17_type_casting/README.md) 71 | * [assert와 guard](contents/18_assert_guard/README.md) 72 | * [프로토콜](contents/19_protocol/README.md) 73 | * [익스텐션](contents/20_extension/README.md) 74 | * [오류처리](contents/21_error_handling/README.md) 75 | * [고차함수](contents/22_higher_order_function/README.md) 76 | * [더 알아보기](contents/23_more/README.md) 77 | --------------------------------------------------------------------------------