26강 프로토콜
- 특정 역할을 수행하기 위한 메서드, 프로퍼티, 이니셜라이저 등 요구사항을 미리 정의
- 특정 프로토콜을 채택하는 경우 실제로 구현한 것을 준수한다고 표현
protocol Talkable {
var topic: String { get set }
var language: String { get }
func talk()
init(topic: String, language: String)
}
struct Person: Talkable {
//var topic: String
// 읽고쓰기 전용인 경우 let은 안됨
//let language: String
// 읽기전용 프로퍼티 요구는 연산 프로퍼티로 대체 가능
// var language: String { return "한국어" }
// var subject: String = ""
// var topic: String {
// set {
// self.subject = newValue
// }
// get {
// return self.subject
// }
// }
func talk() {
print("\(topic)에 대해 \(language)로 말합니다")
}
init(topic: String, language: String) {
self.topic = topic
self.language = language
}
}
// 프로토콜은 다중 상속이 가능
protocol Readable {
func read()
}
protocol Writeable {
func write()
}
protocol ReadSpeakable: Readable {
func speak()
}
protocol ReadWriteSpeakalbe: Readable, Writeable {
func speak()
}
// 클래스 상속과 프로토콜
class SuperClass: Readable {
func read() {
print("read")
}
}
// 순서를 꼭 지켜야함 - 상속받는 클래스가 가장 먼저
class SubClass: SuperClass, Writeable, ReadSpeakable {
func write() {
print("wirte")
}
func speak() {
print("speak")
}
}
let sup: SuperClass = SuperClass()
var someAny: Any = sup
someAny is Readable
someAny is Writeable
27강 익스텐션
- 구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가 할 수 있는 기능
- 코드를 몰라도 타입만 알고 있다면 그 타입의 기능을 확장 가능
- 익스텐션이 타입에 추가 할 수 있는 기능 → 익스텐션은 타입에 새로운 기능을 추가할 수는 있지만, 기존에 존재하는 기능을 재정의 할 수 없음.
- 연산 타입 프로퍼티 / 연산 인스턴스 프로퍼티
- 타입 메서드 / 인스턴스 메서드
- 이니셜라이저
- 서브스크립트
- 중첩 타입
- 특정 프로토콜을 준수할 수 있도록 기능 추가
- 클래스 상속과 익스텐션 비교
- 외부 라이브러리나 프레임워크를 가져다 썼다면 원본 소스를 수정하지 못함. 이처럼 외부에서 가져온 타입에 내가 원하는 기능을 추가하고자 할 때 익스텐션을 사용 → 따로 상속을 받지 않아도 되며, 구조체와 열거형에도 기능을 추가할 수 있으므로 익스텐션은 매우 편리한 기능
- 프로토콜 중심 프로그래밍 참고
extension Int {
var isEven: Bool {
return self % 2 == 0
}
var isOdd: Bool {
return self % 2 == 1
}
func multiply(by n: Int) -> Int {
return self * n
}
}
print(1.isEven) // false
print(2.isEven) // true
var number: Int = 3
print(number.isEven) // false
print(number.isOdd) // true
print(3.multiply(by: 2)) // 6
- 이니셜라이져
- 편의 이니셜라이저는 추가 가능하나, 지정 이니셜라이저는 추가할 수 없음. 지정 이니셜라이저와 디이니셜라이저는 반드시 클래스 타입의 구현부에 위치
extension String {
init(int: Int) {
self = "\(int)"
}
init(double: Double) {
self = "\(double)"
}
}
let stringFromInt: String = String(int: 100)
// "100"
28강 오류 처리
Error
라는 프로토콜을 사용 → 사실상 요구사항이 없는 빈 프로토콜- 열거형의 연관 값: 연관값은 열거 내의 하나의 사례에 대해 각 사례 인스턴스마다 다른 값들을 가지게 하고 싶을 때 사용 참고
enum VendingMachineError: Error {
case invalidInput
case insufficientFunds(moneyNeeded: Int) // 오류에 대한 부가정보를 제공
case outOfStock
}
- 함수에서 발생한 오류
class VendingMachine {
let itemPrice: Int = 100
var itemCount: Int = 5
var deposited: Int = 0
// 돈 받기 메서드
func receiveMoney(_ money: Int) throws {
// 입력한 돈이 0이하면 오류를 던집니다
guard money > 0 else {
throw VendingMachineError.invalidInput
}
// 오류가 없으면 정상처리를 합니다
self.deposited += money
print("\(money)원 받음")
}
// 물건 팔기 메서드
func vend(numberOfItems numberOfItemsToVend: Int) throws -> String {
// 원하는 아이템의 수량이 잘못 입력되었으면 오류를 던집니다
guard numberOfItemsToVend > 0 else {
throw VendingMachineError.invalidInput
}
// 구매하려는 수량보다 미리 넣어둔 돈이 적으면 오류를 던집니다
guard numberOfItemsToVend * itemPrice <= deposited else {
let moneyNeeded: Int
moneyNeeded = numberOfItemsToVend * itemPrice - deposited
throw VendingMachineError.insufficientFunds(moneyNeeded: moneyNeeded)
}
// 구매하려는 수량보다 요구하는 수량이 많으면 오류를 던집니다
guard itemCount >= numberOfItemsToVend else {
throw VendingMachineError.outOfStock
}
// 오류가 없으면 정상처리를 합니다
let totalPrice = numberOfItemsToVend * itemPrice
self.deposited -= totalPrice
self.itemCount -= numberOfItemsToVend
return "\(numberOfItemsToVend)개 제공함"
}
}
- do - catch
do {
try machine.receiveMoney(0)
} catch VendingMachineError.invalidInput {
print("입력이 잘못되었습니다")
} catch VendingMachineError.insufficientFunds(let moneyNeeded) {
print("\(moneyNeeded)원이 부족합니다")
} catch VendingMachineError.outOfStock {
print("수량이 부족합니다")
} // 입력이 잘못되었습니다
do {
try machine.receiveMoney(300)
} catch /*(let error)*/ {
switch error {
case VendingMachineError.invalidInput:
print("입력이 잘못되었습니다")
case VendingMachineError.insufficientFunds(let moneyNeeded):
print("\(moneyNeeded)원이 부족합니다")
case VendingMachineError.outOfStock:
print("수량이 부족합니다")
default:
print("알수없는 오류 \(error)")
}
}
do {
result = try machine.receiveMoney(0)
}
// do만 pass
- try? try!
- try? 정상적으로 처리되면 오류처리결과를 받지 않고, 오류가 발생하면 nil
- try! 오류가 발생하지 않을것이라는 강력한 확신 가질때 사용 만약 오류가 발생하면 런타임 오류 발생
result = try? machine.vend(numberOfItems: 2)
result // nil
result = try! machine.vend(numberOfItems: 1)
result // 1개 제공함
result = try! machine.vend(numberOfItems: 1) // 런타임 오류 발생!
rethrows
- call_back
enum MyError: Error {
case cannotDivide
}
func divideNumber(first: Float, second: Float) throws -> Float {
if second == 0 {
throw MyError.cannotDivide
}
return first/second
}
func calculateFunction(function: (Float, Float) throws -> Float) rethrows {
print(try function(2, 0))
}
do {
try calculateFunction(function: divideNumber)
} catch let error as MyError {
switch error {
case .cannotDivide:
print("0으로 나누었다.")
}
}
func calculateFunction(function: (Float, Float) throws -> Float) {
do {
print(try function(2, 0))
} catch let error as MyError {
// Error Handling..
}
}
defer
- 코드블럭에서 벗어날 때 defer 구문 안의 코드 블럭이 실행되는 특징, 에러가 있든, 없든 무조건 실행이 보장
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// Work with the file.
}
// close(file) is called here, at the end of the scope.
}
}
//파일을 정상적으로 닫아주어 메모리에서 해제해줘야 하기 때문에 defer 문을 통해 파일을 닫아줌
29강 고차함수
- 다른 함수를 전달인자로 받거나 함수실행의 결과를 함수로 반환하는 함수
- map
let numbers: [Int] = [0, 1, 2, 3, 4]
var doubledNumbers: [Int]
var strings: [String]
for number in numbers {
doubledNumbers.append(number * 2)
strings.append("\(number)")
}
print(doubledNumbers) // [0, 2, 4, 6, 8]
print(strings) // ["0", "1", "2", "3", "4"]
doubledNumbers = numbers.map({ (number: Int) -> Int in
return number * 2
})
// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저
doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers) // [0, 2, 4, 6, 8]
- filter
- 내부의 값을 걸러서 새로운 컨테이너로 추출
let numbers: [Int] = [0, 1, 2, 3, 4]
var filtered: [Int] = [Int]()
for number in numbers {
if number % 2 == 0 {
filtered.append(number)
}
}
print(filtered) // [0, 2, 4]
let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in
return number % 2 == 0
}
// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저
let oddNumbers: [Int] = numbers.filter {
$0 % 2 != 0
}
print(evenNumbers) // [0, 2, 4]
- reduce
- 하나로 통합
let someNumbers: [Int] = [2, 8, 15]
var result: Int = 0
// someNumbers의 모든 요소를 더합니다
for number in someNumbers {
result += number
}
print(result) // 25
// 초깃값이 0이고 someNumbers 내부의 모든 값을 더합니다.
let sum: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in
//print("\(first) + \(second)")
//0 + 2
//2 + 8
//10 + 15
return first + second // 25
})
let sumFromThree = someNumbers.reduce(0) { $0 + $1 }