[1. Swift]
Playground는 스위프트 코드를 빠르게 개발,점검하기 위한 인터렉티브 환경
단, Xcode의 모든 기능을 담고 있지 않으므로, 가벼운 테스트에 적합합니다.
import 를 통해 프레임 워크를 불러올 수 있습니다.
[2. Variable]
변수와 상수에는 데이터 타입이 있으며, 데이터 처리방식에 대한 정보를 컴파일러에 제공합니다.
변수에 엉뚱한 데이터 타입을 지정하지 않도록 Type Checking 기능을 제공합니다.
var numberOfStoplights: Int = 0
· let을 통해 상수를 선언하며 고정값의 인스턴스입니다.
· var을 통해 변수를 선언하며 가변적인 인스턴스입니다.
[3. Condition]
· if/else는 조건을 boolean값으로만 판단합니다.
· switch는 특정 값에 따라 경우의 수를 나눠 판단합니다.
Tuple
튜플은 논리적 연관성이 있는 둘 이상의 값을 묶은 일종의 유한 집합이며, 리턴값은 순서 리스트의 구조를 갖습니다.
이는 논리적 연관성을 드러내기 위해 사용됩니다.
let testType = (type:Name, number: Number)
switch testType {
case ("hi", _),("hello", 2):
message = "test"
...
}
( _ )는 빈칸의 뜻으로 아무 값이 와도 상관 없는 뜻을 의미합니다.
[4. Roof]
For Roof
인스턴스의 특정 요소들을 반복 처리해야 하는 상황에서 반복 횟수를 알수 있을 때 이상적으로 사용할 수 있습니다.
루프의 현재 반복 횟수를 나타내는 iterator는 루프안에서만 존재하며, where를 사용하여 조건에 만족할 때만 실행되도록 할 수 있습니다.
for i in 1...5 where i % 2 == 0 { print("Hello") }
While Roof
어떤 조건이 충족될 때까지 반복 처리하는데 iterator를 초기화하며, 조건이 유효할 때 시행합니다.
while i < 6 { i += 1 }
Repeat-While Roof
적어도 한 번 실행하고 조건을 판단합니다.
repeat {
print(index)
} while index > 0
Continue를 통해 다음 조건문으로 제어권을 전달할 수 있으며, Break를 통해서 제어문을 벗어날 수 있습니다.
[5. 문자열]
For Roof
문자열은 " " 안에 넣어 선언하는데 인덱스로 문자열에 접근할 수 없지만 다른 방법으로 구간추출이 가능합니다.
let sentence = "hi, Hello"
let start = sentence.startIndex
let end = sentence.index(start, offsetBy: 2)
let hi = sentence[start...end] // hi
[6. Optional]
옵셔널(Optional)은 어떤 인스턴스 값이 없을 수도 있다는 일종의 안내 입니다.
· 인스턴스 값이 지정되어 있고 언제든 사용될 수 있습니다.
· 인스턴스에 지정된 값이 없으며 없는 상태를 nil이라 합니다.
var Text: String?
if Text != nil { print("Exist Value") }
else { print("Error") }
Binding
어떤 옵셔널 값의 유무를 판단할 수 있는 유용한 패턴으로, 값이 있다면 상수나 변수에 그 값을 지정할 수 있습니다.
var Text: String? = "Hi"
if let obj = Text { print("\(obj)") }
else { print("nil") }
Chaining
옵셔널에 값의 유무를 판단할 수 있으며, 연달아 이어 조회가 가능합니다.
let man: Person? = Person(name: "man")
let apart: Apartment? = Apartment(dong: "101", ho: "202")
// 옵셔널 체이닝을 사용하지 않는다면
func job(owner: Person?) {
if let owner = owner {
if let home = owner.home {
if let ...
}
}
}
// 옵셔널 체이닝을 사용한다면
func jobOptionalChaining(owner: Person?) {
if let guardJob = owner?.home? {
...
} else { ... }
}
nil 결합 연산자
옵셔널을 처리할 때 기본 값을 사용할 수 있습니다.
let description = errorDescription ?? "No error"
[7. Collection]
Array
인덱스가 있는 컬렉션으로, 어떤 종류의 값이든 넣을 수 있습니다.
var list: [String]
Dictionary
Key, Value의 쌍으로 담아두는 컬렉션입니다.
Key 값은 고유한 값이여야 하며, Hashable 해야합니다.
var test = ["hi": 1 , "hello": 2]
test.count //3
test["hi"] // 1
for (key, value) in test { print(\(value)) }
Set
서로 연관성이 없는 인스턴스들의 컬렉션 타입으로, 순서가 없으며 중복되는 요소를 허용하지 않으며 Hashable 해야합니다.
var test = Set<String>()
var check = Set(["hello, hi"])
test.insert("hi")
test.contains("hello") //false
test.intersection(check) // 합집합
test.isDisjoint(with: check) // 서로소 // true
[8. Function]
구체적인 task를 수행하는 코드집합입니다.
파라미터는 함수 안에서 반드시 사용되어야 하며, 이름을 사용할 경우, 외부에서 호출 되었을때 반드시 참조해야 합니다.
· 인수(argument) : 함수가 호출되었을 때 사용되는 일련의 값 자체
· 파라미터(parameter) : 함수를 정의할 때, 외부로 부터 받아들이는 값
func service (to name: String) { print("\(name)" }
service(to: "test")
Inout Parameter
파라미터값 은 변경할 수 없는 상수(let)로 값이 복사되어 전달되는데 Inout 파라미터를 통해 실제 값을 변경가능합니다.
inout 파라미터가 붙은 매개변수는 일반 파라미터와 달리 인자값이 전달될 때 새로운 내부 상수를 만들어 복사하는 대신, 인자값 자체를 내부로 전달합니다. (저장된 메모리주소를 전달)
func inoutParam (param: inout Int) { param += 1 }
var count = 1
inoutParam(param: &count)
Guard
함수 시작부분에 검사하여 부적절할 경우 빠져나오게 합니다.
문법은 guard (조건) else 와 같이 쓰며, 조건이 true 이면 guard문은 그냥 지나쳐가고, false일 경우 else 구문을 수행한 뒤 종료합니다.
func say (name : String?) {
guard let name = name else { return }
print(name)
}
Advantage
1. 동일한 코드가 여러 곳에서 사용될 때 재작성할 필요 없이 함수 호출만으로 처리가 가능합니다.
2. 전체 프로세스를 기능 단위로 함수화하면 가독성이 좋아지고, 코드와 로직을 이해하기 쉬워집니다.
3. 로직을 변경해야할 때 함수 내부만 수정하면 되므로 유지보수가 용이합니다.
defer Block
메서드에서 코드의 흐름과 상관없이 가장 마지막에 실행되는 블록으로, 작성된 위치와 상관없이 함수의 종료 직전에 실행되며, 종료 시점에 맞추어 처리할 구문이 있다면 defer 구문에 넣어두면 됩니다.
· defer 구문을 읽기 전 함수구문이 종료될 경우 defer 구문은 실행되지 않습니다.
· 하나의 메서드에서 defer 블록을 여러번 사용가능하다. 단, 실행순서는 가장 아래서부터 역순으로 실행됩니다.
[9. Closure]
Application에서 특정 Task를 수행하기 위해 사용 될 수 있는 각종 기능들의 개별 묶음을 말합니다.
· 클로저는 문법 구조가 간결하며 일회횽 함수입니다.
· 온전한 선언 구조를 따르지 않아도 함수와 비슷한 구조를 만들 수 있습니다.
· 함수의 인수나 리턴값을 쉽게 전달할 수 있습니다
let f = { () -> Void in print("클로저") }
고차함수
map() : 배열의 내용물을 한 값에서 다른 값으로 매핑하고 새 값을 새 배열에 넣습니다.
let plusNum = num.map { (number: Int) -> Int in // 10, 20, 30
return number + 10 // 20, 30, 40
}
filter() : 기준에 해당하는 데이터만 거러내는 함수입니다.
let filterNum = num.filter { (number: Int) -> Bool in // 10, 20, 30
return number > 20 // 30
}
reduce() : 컬렉션 내의 값들을 함수로부터 리턴된 하나의 값으로 축소시킵니다.
let reduceNum = num.reduce(10) { (num: Int, sum: Int) -> Int in // 10, 20, 30
return num + sum // 10 + 10 + 20 + 30 = 70
}
[10. Enumeration]
리스트 형태로 정의된 여러 case로 인스턴스를 만들 수 있는 방법입니다.
열거형 타입의 이름은 대문자로 시작, 케멀케이스로 네이밍을 해주는게 관례입니다.
enum Month: Int {
case january = 1
case feburary
...
func toString() -> String {
switch self {
case .january:
return "1월"
...
}
}
}
[11. Structure & Class]
Structure
· 클래스 인스턴스가 전달될 때에는 복사에 의해 전달되며, 데이터를 어떤 하나의 타입에 두고 싶을 때 사용합니다. (캡슐화)
· 기본적으론 프로퍼티 변경이 불가하고, 인스턴스의 프로퍼티 값을 변경할 메서드에는 mutating 키워드가 붙어야 합니다.
· 참조 카운트가 없어서 메모리 관리에 안전하며, 레퍼런스 형태가 아니기 때문에 공유가 불가능합니다.
· 임의로 변경되지 않기 때문에 멀티스레딩에 안전합니다.
· 상속이 불가능하나 프로토콜은 사용 할 수 있습니다.
struct Product {
var price = 1000
var name = "watch"
mutating func change(raise: Int) { price += raise }
}
Class
· 클래스 인스턴스가 전달될 때에는 참조 형식으로 제공되고, 타입 캐스팅이 가능합니다.
· 소멸화 구문을 사용하여 인스턴스가 소멸 직전에 처리해야할 구문을 미리 등록해 놓을 수 있습니다.
· 구조체와는 다르게 상속이 가능하며, final 키워드를 통해 오버라이딩을 차단할 수 있다.
class Product {
var price = 1000
var name = "watch"
override func change(raise: Int) { super.change(raise: 0) }
}
[12. Property]
값과 타입을 연계 내지 연동하는 방식으로 타입이 나타내는 엔터티(entity) 특징을 구체화합니다.
저장형 프로퍼티
· 데이터를 저장하는 프로퍼티로 어떤 타입의 기능을 지원할 목적으로 사용됩니다.
지연 저장형 프로퍼티(lazy)
· 해당 프로퍼티의 인스턴스가 메모리를 많이 먹는다면 필요할 때 생성되도록 선언해두는 프로퍼티입니다.
계산형 프로퍼티
· 값을 저장하지 않으며 getter, setter를 제공합니다.
프로퍼티 관찰자
· 프로퍼티의 변화를 추적해서 실시간으로 대응할 때 사용합니다.
· willSet : 프로퍼티가 변경되기 직전에 실행됩니다.
· didSet : 프로퍼티가 변경된 후에 실행됩니다.
타입 프로퍼티(정적 프로퍼티)
· 클래스와 구조체 자체에 소속되어있는 프로퍼티입니다.
· 정적 프로퍼티에는 static func(override 불가능)와 class func(override 가능, class 프로퍼티만 가능)가 존재합니다.
엑세스 제어
· 필요에 따라 프로퍼티의 가시성을 제어하여 숨기거나 들어낼 수 있습니다.
· 프로퍼티의 데이터를 캡슐화하여 외부 접근을 막을 수 있습니다.
접근 제어
· Swift의 기본 제어 수준은 internal입니다.
· Getter, Setter에 가시성을 따로 제어할 수 있지만 Getter가 가시성이 무조건 높습니다.
[13. Initializer]
· 인스턴스를 설정하는 과정으로 init 키워드로 시작합니다.
· 인스턴스가 만들어질 때 저장형 프로퍼티가 값을 가지도록 하는 방법입니다.
클래스 초기화
지정형 이니셜라이저 : 클래스의 주 이니셜라이저 형태로, 모두 초기화 완료 전 값을 받는지 확인합니다.
편의성 이니셜라이저 : convenience 키워드를 사용하며, 저장형 프로퍼티가 초기값을 받는 방법을 정의합니다.
요구형 이니셜라이저 : required 키워드를 붙여 이 타입의 모든 서브클래스에서 해당 이니셜라이저를 제공할 수 있습니다.
Deinitializer
인스턴스가 더이상 필요하지 않을 때, 메모리에서 제거하는 과정입니다.
· deinit 키워드로 정의되며 메모리에서 제거되기 바로 직전에 호출됩니다.
[14. Value vs Reference]
Value
인스턴스에 대입될 때나 함수의 인수로 전달될 때 항상 복사되며, 얕은복사만 지원합니다.
Reference
같은 인스턴스를 가리키는 참조를 추가합니다.
[15. Protocol]
· 클래스나 구조체가 어떤 기준을 만족하거나 또는 특수한 목적을 달성하기 위해 구현해야 하는 메소드와 프로퍼티의 목록입니다.
· 구체적인 내용이 없는 프로퍼티나 메소드의 단순한 선언 형태로 구성됩니다.
· 구현한 객체의 메서드나 속성을 은닉하고 프로토콜에서 선언된 명세의 내용만 제공하는 기능을 하기도 합니다.
conform
· 타입은 모두 프로토콜을 준수할 수 있습니다.
· 타입이 여러 프로토콜을 준수학 수 있습니다.
· 외부 변수명은 준수해야 하지만 내부는 변경가능합니다.
mutating (가변 method)
· 구조체나 열거형은 내부 매서드가 프로퍼티 변경 시, 반드시 mutating 키워드를 붙여 표시해야합니다.
· 프로토콜은 자신을 구현하는 구조체가 마음대로 프로퍼티를 수정하지 못하도록 통제할 수 있습니다.
프로토콜 상속
· 여러 프로토콜을 상속받을 수 있습니다.
· 상속받은 프로토콜에서 준수 타입이 두 프로토콜에 필요한 프로퍼티와 메서드를 구현해야 합니다.
선택적 프로토콜
· @objc 속성을 부여하는데 열거형이나 구조체 등에서 채택은 불가합니다.
Delegate Pattern
· 메소드를 호출할 인스턴스 객체를 전달받게 해주며, 해당 인스턴스 객체가 구현하고 있는 프로토콜에 선언된 메소드를 호출합니다.
[16. Error Handling]
· 여러가지 오류를 정의할 수 있어야 하기에 보통 열거형으로 정의합니다.
protocol Error {}
enum ParsingError: Error {
case overSize
...
}
· 오류를 외부로 던질 때 throws 키워드를 명시적으로 추가해주어야 합니다.
func throwErrors() throws -> String
· 호출할 때에는 try를 붙여줘야 하며, 잡아낼 때는 catch를 사용해야 합니다.
do {
try throwErrors()
} catch ParseError.overSize {
print("Error")
}
[Generic & Extension]
Generic
재사용성 및 코드 중복을 줄일 수 있어서 추상적인 표현을 가능하게 해줍니다.
Extension
구조체, 클레스, 열거, 프로토콜 타입에 새로운 기능을 추가 시 사용합니다.
타입 제약
Generic 함수가 처리해야 할 기능이 특정 타입에 한정되어야 처리할 수 있는 등 특정 프로토콜을 따르는 타입만 사용할 수 있도록 제약을 두어야 하는 상황이 발생할 수 있어 이용합니다.
프로토콜 연관타입
Generic에서 어떤 타입이 들어올지 모를 때, 타입 매개변수를 통해 ‘어떤 타입을 사용할 것이다’ 라고 표현해주었다면, 연관 타입은 타입 매개변수의 그 역활을 프로토콜에서 실행할 수 있도록 만들어진 기능입니다.
protocol Test {
associatedtype TestType
mutating func append(_ item: TestType)
}