16 Jan 2020
|
swift
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.
오류처리(error handling)
Error 프로토콜과 (주로) 열거형을 통해서 오류를 표현한다.
enum 오류종류이름: Error {
case 종류1
case 종류2
case 종류3
case 종류4
...
}
예시는 아래와 같다.
enum VendingMachineError: Error {
case invalidInput
case insufficientFunds(moneyNeeded: Int)
case outOfStock
}
함수에서 발생한 오류 던지기
이러한 오류들은 특정 함수안에서 오류가 발생했다고 (자신을 호출한 곳에) 오류를 던져준다.
오류 발생의 여지가 있는 메서드는 throws
를 사용해 오류를 내포하는 함수임을 표시한다.
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: money)
}
}
}
오류 처리(try, do-catch)
- 오류발생의 여지가 있는 throws 함수는 try를 사용해 호출해야한다. > try, try?, try!
- 오류 발생의 여지가 있는 throws 함수는 do-catch 구문을 활용해 오류발생에 대비한다.
do {
try machine.receiveMoney(0)
} catch VendingMachineError.invalidInput {
print("입력이 잘못됐습니다")
} catch VendingMachine.insufficientFunds {
print("\(moneyNeeded)원이 부족합니다")
} catch VendingMachineError.outOfStock {
print("수량이 부족합니다")
}
반복된 catch가 별로라면 아래처럼 해도 된다.
do {
try machine.receiveMoney(300)
} catch // let error 는 생략 가능하다
switch error {
case VendingMachineError.invalidInput:
print("입력이 잘못되었습니다")
case ....
default:
print("알수없는 오류 \(error)")
}
}
그런데 이마저도 귀찮다.
do {
result = tru machine.vend(numberOfItems: 4)
} catch {
print(error)
}
혹은 아래로 한다
do {
result = try machine.vend(numberOfItems: 4)
}
이 깔끔하지 않은 구문을 좀 더 명확히 해주기 위해 try? try!가 있다!
try?
- 별도의 오류처리 결과를 통보받지 않는다.
- 오류가 발생했으면 결과값을 nil로 돌려받는다.
- 정상동작 후에는 옵셔널 타입으로 정상 반환값을 돌려받는다.
result = try? machine.vend(numberOfItems" 2)
result // Optional("2개 제공함")
result = try? machine.vend(numberOfItems" 2)
result // nil
try!
- 오류가 발생하지 않을 것이라는 강력한 확신을 가질 때
- 정상 동작 후에 바로 결과값을 돌려받는다.
- 오류가 발생하면 런타임 오류가 발생하여 애플리케이션 동작이 중지된다.
result = try! machine.vend(numberOfItems" 2)
result // 1개 제공함
result = try! machine.vend(numberOfItems" 2)
16 Jan 2020
|
swift
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.
익스텐션(extension)
구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가할 수 있는 기능이다.
기능을 추가하려는 타입의 구현된 소스코드를 알지 못하거나 볼수 없다해도 타입만 알고있다면 그 타입의 기능을 확장할 수 있다.
- 연산 타입 프로퍼티 / 연산 인스턴스 프로퍼티
- 타입 메서드 / 인스턴스 메서드
- 이니셜라이저
- 서브스크립트
- 중첩 타입
- 특정 프로토콜을 준수할 수 있도록 기능 추가
기존에 존재하는 기능을 재정의 할 수는 없다.
extension 확장할 타입 이름 {
타입에 추가될 새로운 기능 구현
}
익스텐션은 기존에 존재하는 타입이 추가적으로 다른 프로토콜을 채택할 수 있도록 확장할 수도 있다.
extension 확장할 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3... {
프로토콜 요구사항 구현
}
익스텐션 구현 / 연산 프로퍼티 추가
extension Int {
var isEven: Bool {
return self % 2 == 0
}
var isOdd: Bool {
return self % 2 == 1
}
}
// 그냥 숫자를 써주게 되더라도 정수 타입(리터럴 문법) >> 일반 숫자로도 표현 가능
print(1.isEven) // false
var number: Int = 3
익스텐션 구현 / 메서드 추가
extension Int {
func multiply(by n: Int) -> Int {
return self * n
}
}
// 3이라는 Int타입의 리터럴 문법, Int 타입의 인스턴스로 취급
print(3.multiply(by: 3)) // 9
익스텐션 구현 / 이니셜라이저 추가
extension String {
init(intTypeNumber: Int) {
self = "\(intTypeNumber)"
}
}
// 기존에 없던 이니셜라이저를 추가할 수도 있다.
16 Jan 2020
|
swift
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.
프로토콜(protocol)
특정 역할을 수행하기 위한 메서드, 프로퍼티, 이니셜라이저 등의 요구사항을 정의한다.
어떤 타입에 이 기능이 필요해! 그러니 꼭 그 기능을 구현해놨어야해! 라고 강요하는 것과 같다.
구조체, 클래스, 열거형은 프로토코를 채택(adopted)하여 프로토콜의 요구사항을 실제로 구현 가능하다.
어떤 프로토콜의 요구사항을 모두 따르는 타입은 그 프로토콜을 준수한다(Conform)
고 표현한다.
프로토콜의 요구사항을 충족시키려면 프로토콜이 제시하는 기능을 모두 구현해야 한다.
예시는 아래와 같다
protocol Talkable {
// 프로퍼티 요구 >> 항상 var 키워드를 사용한다.
var topic: String { get set } // 읽기 쓰기 모두 가능한 프로퍼티
var language: String { get } // 읽기만 가능한 프로퍼티
// 메서드 요구
func talk()
// 이니셜라이저 요구
init(topic: String, language: String)
}
직접 구현을 하는것은 아니고 요구만 한다.
프로토콜의 채택 및 준수
// Person 구조체는 Talkable 프로토콜을 채택했다
struct Person: Talkable {
// 저장 프로퍼티
var topic: String
var language: String
// 위 저장 프로퍼티는 연산프로퍼티로 대체가 가능하다.
var language: String { return "한국어" }
var subject: String = ""
var topic: String {
set {
self.subject = newValue
}
get {
return self.subject
}
}
}
프로토콜 상속
클래스와 다르게 다중 상속이 가능하다.
protocol 프로토콜 이름: 부모 프로토콜 이름 목록 {
정의부
}
프로토콜이 상속한 모든 메서드들을 구현하지 않으면 오류가 발생한다.
클래스 상속과 프로토콜
클래스에서 상속과 프로토콜 채택을 동시에 하려면 상속 받으려는 클래스를 먼저 명시하고 그 뒤에 채택할 프로토콜 목록을 작성한다.
순서가 바뀌면 안된다!
프로토콜 준수 확인
인스턴스가 특정 프로토콜을 준수하는 지 확인 가능하다 » is, as 연산자 사용
16 Jan 2020
|
swift
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.
assert & guard
애플리케이션이 동작하는 도중에 생성하는 다양한 결과값을 동적으로 확인하고 안전하게 처리할 수 있도록 도와준다.
Assert
어떤 결과들을 확인해보는데 사용한다.
디버깅 모드에서만 동작 하고, 배포하는 애플리케이션에서는 동작하지 않는다.
디버깅 중에서도 예상했던 조건들이 확실히 맞는가를 확인할 때 사용한다.
var someInt: Int = 0
// someInt가 0이 맞으면 그냥 지나가고 아니면 someInt != 0 메시지를 반환
assert(someInt == 0), "someInt != 0")
Early Exit
빠른 종료를 위해 사용한다.
디버깅 구문에서만 사용가능한 assert와는 다르게 guard는 어떤 조건에서도 동작 을 한다.
guard를 사용하여 잘못된 값이 전달됐을 시 특정 실행구문을 빠르게 종료시킨다.
guard의 else 블럭 내부 에는 특정 코드블럭을 종료하는 지시어(return, break 등)가 꼭 있어야 한다.
타입 캐스팅, 옵셔널과도 자주 사용되며 그 외 단순 조건 판단 후 빠르게 종료할 때도 용이다.
func functionWithGuard(age: Int?) {
guard let unwrapperdAge = age,
unwrapperdAge < 130,
unwrapperdAge >= 0 else {
print("나이값 입력이 잘못되었습니다.")
return
}
print("당신의 나이는 \(unwrapperdAge)세 입니다")
}
if let은 블럭 안에서만 사용가능했지만 guard let은 블럭 바깥에서도 계속 사용할 수 있다.
var count = 1
while true {
guard count < 3 else {
break
}
print(count)
count +=1
}
// 혹은
func someFunction(info: [String: Any]) {
guard let name = info["name"] as? String else {
return
}
guard let age = info["age"] as? Int, age >= 0 else {
return
}
print("\(name): \(age)")
}
- 딕셔너리에서 나오는 값은 모두 옵셔널(key에 해당하는 값이 없기 때문)
- info라는 딕셔너리에서 name이라는 값을 가져와 캐스팅을 시도함
- Any 타입이기 때문에 실질적으로 사용하기 위해서는 String으로 캐스팅을 시도함
16 Jan 2020
|
swift
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.
타입 캐스팅(type casting)
스위프트의 타입캐스팅은 인스턴스의 타입을 확인하는 용도로 사용한다.
뿐만 아니라 클래스의 인스턴스를, 부모 혹은 자식클래스의 타입으로 사용할 수 있는 지 확인하는 용도로 사용한다.
is, as
를 사용한다.
let someInt: Int = 100
let someDouble: Double = Double(someInt)
이는 스위프트에서 타입캐스팅이 아니다. 그저 Double타입의 인스턴스를 더 생성해준 것일 뿐이다.
스위프트의 타입캐스팅은 클래스의 인스턴스에서 사용할 있다.
class Person {
var name: String = ""
func breath() {
print("숨을 쉽니다")
}
}
class Student: Person {
var school: String = ""
func goToSchool() {
print("등교를 합니다")
}
}
class UniversityStudent: Student {
var major: String = ""
func goToMT() {
print("멤버쉽 트레이닝을 갑니다!")
}
}
var zehye: Person = Person()
var hana: Student = Student()
var jason: UniversityStudent = UniversityStudent()
타입확인 - is
var result: Bool
result = zehye is Person // true
result = zehye is Student // false
result = zehye is UniversityStudent // false
result = hana is Person // true
result = hana is Student // true
result = hana is UniversityStudent // false
result = jason is Person // true
result = jason is Student // true
result = jason is UniversityStudent // true
업 캐스팅 - as
as를 사용하여 부모클래스의 인스턴스로 사용할 수 있도록 컴파일러에게 타입정보를 전환해준다.
Any 혹은 AnyObject로도 타입 정보를 변환할 수 있다. > 암시적으로 처리되므로 생략해도 무방하다
var mike: Person = UniversityStudent() as Person
var jenny: Student = Student()
var jina: Any = Person() // as Any 생략가능
var jina: UniversityStudent = Person() as UniversityStudent // 컴파일 오류
- Person타입이라고 하더라도 UniversityStudent의 인스턴스가 들어올 수 있다 » 왜냐면 대학생은 사람이기 때문
다운 캐스팅 - as? 또는 as!
자식 클래스의 인스턴스로 사용할 수 있도록 컴파일러에게 인스턴스의 타입정보를 전환해준다.(사람으로 지정했는데 학생일수도 있느냐?)
- as? 조건부 다운캐스팅
- as! 강제 다운캐스팅
조건부 다운캐스팅 as?
var optionalCasted: Student?
optionalCasted = mike as? UniversityStudent
optionalCasted = jenny as? UniversityStudent // nil
optionalCasted = jina as? UniversityStudent // nil
optionalCasted = jina as? Student // nil
- mike: Person 타입으로 되어잇어도 UniversityStudent로 캐스팅될 수 있다.
- 나머지: 사람이거나 학생이었기 때문에 캐스팅이 될 수 없다. » nil이 반환
조건부 다운캐스팅을 하게 되면 결과값이 옵셔널값이 나온다.
강제 다운캐스팅 as!
var forcedCasted: Student
optionalCasted = mike as! UniversityStudent
optionalCasted = jenny as! UniversityStudent // 런타임 오류
optionalCasted = jina as! UniversityStudent // 런타임 오류
optionalCasted = jina as! Student // 런타임 오류
- mike: 원래 대학생 타입이었기 때문에 문제없이 진행된다.
- 나머지: 대학생이 될 수 없기 때문에 오류가 발생한다.
강제 다운캐스팅은 위험요소가 있지만 반환타입이 옵셔널이 아닌 일반 타입이기 떄문에 편하게 사용가능하다.
주로 함수로 전달되는 경우 확인해볼 수 있다.
func doSonethingWithSwitch(someone: Person) {
switch someone {
case is UniversityStudent:
(someone as! UniversityStudent).goToMT()
}
case is Student {
(someone as! Student).goToSchool()
}
case is Person {
(someone as! Person).breath()
}
}
switch구문을 할때는 확인만 할 뿐이지 실제 캐스팅을 하고싶다면 직접 실행을 해야한다.
그렇기 때문에 if let
구문을 사용한다. » 옵셔널 결과값을 받아와 옵셔널 추출을 한다.
func doSonethingWithSwitch(someone: Person) {
if let universityStudent = someone as? UniversityStudent {
universityStudent.goToMT()
} else if let student = someone as? Student {
student.goToSchool()
} else if let person = someone as? Person {
person.breath()
}
}