-
yagom 의 iOS Swift 홈페이지 강좌기술/iOS 2022. 2. 19. 14:00
기초 개념
이름 짓기
CamelCase 를 사용함 EASY EASY~
콘솔 로그
- print: 단순 문자열 출력
- dump: 인스턴스의 자세한 요소들까지 전부 출력 해줌
보간법
\()
상수와 개념
함수형 패러다임 답게 상수표현이 많이 등장함
띄워쓰기 잘못쓰면 문제가 생김 ( 에바쎄바 )
- let: 상수
- let 이름: 타입 = 값
- 상수를 먼저 선언하고 나중에 한번 값을 넣어줄 수 있음
- var: 변수
- var 이름: 타입 = 값
- 변수도 선언하고 나중에 사용 가능
데이터 타입
기본 데이터 타입
데이터 타입은 아주 정확히 할당해야한다.
- Bool
- 0, 1 사용 가능 ( 에바쎄바... )
- Int, UInt
- Int: 64bit
- UnsingedInt: 음수 안됨, Int 도 못넣음
- Float, Double
- Float: 32bit, 정수를 넣어도 됨 ( 또라이 아니냐 )
- Double: 64bit, 정수를 넣어도 됨, Float 못넣음
- Character, String
- Character: Unicode 를 사용하는 한 문자
- String: Unicode 를 사용하는 문자열
Any, AnyObject, nil
- Any: Swift 의 모든 타입을 지칭하는 키워드
- Any 타입은 다른 변수에 할당이 불가하다.
- AnyObject: Swift 의 모든 클래스를 지칭하는 키워드
- 클래스의 인스턴스만 가질 수 있음
- nil: 없음을 표현하는 키워드
- Any, AnyObject 에 할당 불가
컬렉션 타입
- Array: 순서가 있는 리스트
- Dictionary: 키와 값으로 이루어진 컬렉션
Dict.removeValue(forKey: “Key”) == Dict[”Key”] = nil
**let someValue: String = Dict[”name”]
이 문법은 name 이 있을지 없을 지 몰라 불가하다.**
- Set: 순서가 없고, 멤버가 유일한 컬렉션
함수
함수 기본
func 함수이름(매개변수: 타입) -> 반환타입 { 함수 구현부 return 구현값 }
반환 값이 없는 경우 Void, 없다(nil)를 표현하는 타입, 생략 가능
함수를 호출할 때 반드시 인자들과 값을 모두 작성해야한다.
함수 고급
- 매개변수 기본값
- 전달인자 레이블
- 함수 중복 정의가 가능함
- 전달인자를 사용하면 인자에서는 꼭 전달인자 레이블을 사용해야 함
- 가변 매개변수
- ... : 함수 당 하나, 맨 뒤에 위치
- 당연하지만 인자로 nil 은 안됨
- 함수 타입 표현
- 일급 객체이기 때문에 변수에 할당 가능
- (타입) → 반환 타입
조건문
if-else
condition 소괄호 생략 가능
조건문에는 항상 Bool 타입이 들어와야 함 ( int 안돼 )
if condition { statements } else if condition { statements }
switch
- 범위 연산자
switch someInteger { case 1..<100: case 101..Int.max: }
- 정수 외의 대부분의 기본 타입을 사용할 수 있다.
- break 없어도 무조건 Break
- fallthrough 를 쓰면 break 를 무효화할 수 있음
- Default 가 없으면 에러
반복문
for-in
for item in items { code }
for (name, age) in people { code }
while
조건문의 소괄호는 선택사항
반드시 Bool 값 ( Int 멈춰! )
while condition { }
repeat-while
- do-while 과 같음, do 를 사용하지 않는 이유는 오류 처리에 사용하기 때문
repeat { code } while condition
옵셔널
- 값이 있을수도 없을수도 있음
func someFunction(someOptionalParam: Int?){}
암시적 추출 옵셔널
Int!
- 기존 변수처럼 사용 가능
- 런타임 오류 발생 가능
명시적 추출 옵셔널
Int?
- 기존 변수처럼 사용 불가
옵셔널 추출
Optional Binding - if-let
- nil 체크 + 안전한 꺼냄
if let name: String = myName { print(name) } else { print(it's nil!!) }
Force Unwrapping
- 강제 추출하는 방법
- 런타임 오류 발생 가능
- 암시적 추출 옵셔널을 사용하면 강제로 이 방법으로 변수가 사용됨
var myName: String? = "yagom" print(myName!)
구조체
struct 이름 { var property: Int = 100 // 가변 프로퍼티 let property: Int = 100 // 불변 프로퍼티 static var typeProperty: Int = 100 // 타입 프로퍼티 // 메서드 func method(){ } static func method() {} // 타입 메서드 }
- 인스턴스화를
let
으로 했을 때 가변 프로퍼티도 변경 불가 - 타입 메서드, 프로퍼티는 구조체만 사용 가능하고, 인스턴스는 사용 불가
- 타입 메서드, 인스턴스 메서드는 동일한 이름 사용 가능
클래스
- 구조체는 값, 클래스는 참조
- 인스턴스화를
let
으로 했을 때 가변 프로퍼티도 변경 가능
class 이름 { var property: Int = 100 // 가변 프로퍼티 let property: Int = 100 // 불변 프로퍼티 static var typeProperty: Int = 100 // 타입 프로퍼티 // 메서드 func method(){ } static func method() {} // 타입 메서드 ( 상속 시 재정의 불가 ) class func method() {} // 타입 메서드 ( 상속 시 재정의 가능 ) }
열거형
enum 이름 { case 이름 case 이름2 case 이름3, 이름4, 이름5 }
- enum 은 타입이기때문에 대문자 정의
- 선언 시 타입 명시, 이후 생략 가능
- switch 문과 같이 사용 가능, Case 를 모두 정의하면 Default 생략 가능
- 원시값 사용 가능
- 자동으로 1씩 증가함
- 문자열의 경우 케이스의 이름 고대로 가져옴
enum Fruit: Int { case apple = 0 case grape = 1 case peach }
- Optional 타입임
- 메서드를 가질 수 있음
값 / 참조 타입
구조체는 언제 사용하나?
- 연관된 데이터를 모아 하나의 데이터 타입으로 표현하고 싶을 때
- 다른 객체 또는 함수 등으로 전달될 때 참조가 아닌 복사를 원할 때
- 자신을 상속할 필요가 없거나, 자신이 다른 타입을 상속받을 필요가 없을 때
- Apple 프레임워크에서 프로그래밍할때 주로 클래스 사용
- Swift 프레임워크에서 프로그래밍할 때 주로 구조체 사용
값 / 참조
- Value
- 데이터를 복사해서 전달
- Reference
- 참조를 전달
클로저
- 일급시민으로 전달인자, 변수, 상수 등으로 전달 가능
- 함수는 클로저의 일종으로, 이름이 있는 클로저
- 함수의 Callback 으로서도 많이 사용됨
클로저 고급
후행 클로저
result = calculate(a: 10, b: 10) { (left: Int, right: Int) -> Int in return left + right }
반환 타입 생략
result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) in return left + right }) print(result) // 20 // 후행클로저와 함께 사용할 수도 있습니다 result = calculate(a: 10, b: 10) { (left: Int, right: Int) in return left + right }
단축 인자이름
result = calculate(a: 10, b: 10, method: { return $0 + $1 }) print(result) // 20 // 당연히 후행 클로저와 함께 사용할 수 있습니다 result = calculate(a: 10, b: 10) { return $0 + $1 } print(result) // 20
암시적 반환 표현
result = calculate(a: 10, b: 10) { $0 + $1 } print(result) // 20 // 간결하게 한 줄로 표현해 줄 수도 있습니다 result = calculate(a: 10, b: 10) { $0 + $1 }
프로퍼티
인스턴스 저장 프로퍼티
struct Student { var age: Int = 0 }
타입 저장 프로퍼티
struct Student { static var age: Int = 0 }
인스턴스 연산 프로퍼티
- get 만 있으면 읽기 전용
- 읽기 전용에서는 get 을 생략할 수 있음
- set 은 매개변수를 newValue 로 생략할 수 있음
- 변수에서 사용 가능
struct Student { var age: Int { get { return age - 1 } set { age = newValue + 1 } } }
타입 연산 프로퍼티
struct Student { static var age: Int { return 0 } } var yagom: Student = Student() print(yagom.age)
프로퍼티 감시자
프로퍼티 값이 변경될 때 원하는 동작을 수행할 수 있음
- 암시적으로 newValue, oldValue 로 생략 할 수 있음
- 연산 프로퍼티와 같이 사용할 수 없음
struct Money { // 프로퍼티 감시자 사용 var currencyRate: Double = 1100 { willSet(newRate) { print("환율이 \(currencyRate)에서 \(newRate)으로 변경될 예정입니다") } didSet(oldRate) { print("환율이 \(oldRate)에서 \(currencyRate)으로 변경되었습니다") } } }
상속
- 클래스, 프로토콜에서 가능
- 열거형, 구조체 불가
- 다중 상속 지원 불가
class 부모 클래스 { func method() {} // 재정의 가능 final func finalMethod() {} // final 을 사용하여 재정의 불가하도록 할 수 있음 static func typeMethod() {} // 재정의 불가 class func tyclassMethod() {} // 재정의 가능 final class func finalcClassMethod() {} // 재정의 불가 } class 이름: 부모 클래스 { override func method( super.method() ) {} // 재정의 및 부모 클래스 메서드 호출 override class func classMethod() {} // 재정의 }
인스턴스 생성과 소멸
모든 인스턴스는 초기화와 동시에 모든 프로퍼티에 유효한 값이 할당되어 있어야 한다.
init
- Optional 프로퍼티 사용 가능
- initalizer 여러개 중복 가능
- 자신의 init 을 호출할 때는
convenience
키워드를 사용 - 실패 가능한 이니셜라이저 (
init?
)- 실패하면 nil 을 반환함
class PersonB { var name: String var age: Int var nickName: String convenience init(name: String, age: Int, nickName: String) { self.init(name: name, age: age) self.name = name self.age = age self.nickName = nickName } init(name: String, age: Int) { self.name = name self.age = age } init?(name: String, age: Int) { if (0...120).contains(age) == false { return nil } if name.characters.count == 0 { return nil } self.name = name self.age = age } } let hana: PersonB = PersonB(name: "hana", age: 20, nickName: "하나")
deinit
- 메모리에서 해제될 때 해야할 일을 구현할 수 있음
class PersonE { var name: String var pet: Puppy? var child: PersonC init(name: String, child: PersonC) { self.name = name self.child = child } // 인스턴스가 메모리에서 해제되는 시점에 자동 호출 deinit { if let petName = pet?.name { print("\(name)가 \(child.name)에게 \(petName)를 인도합니다") self.pet?.owner = child } } }
옵셔널 심화
옵셔널 체이닝
func chaining(owner: Person?) { if let guardJob = ownner?.home?.guard?job { } else { } }
nil 병합 연산자
guardJob = yagom?.home?.guard?.job ?? "슈퍼맨"
타입 캐스팅
- 인스턴스의 타입을 확인하는 용도
- 부모 혹은 자식 클래스의 타입으로 사용할 수 있는지
Double(value) 는 타입 캐스팅이 아님, Double 인스턴스를 새로 만든 것
is
let yagom: UniversityStudent: UniversityStudent() result = yagom is Person // true result = yagom is Student // true result = yagom is UniversityStudent // true
as
var mike: Person = UniversityStudent() as Person
다운 캐스팅
- as? - 조건부 다운캐스팅
mike as? UniversityStudent // 캐스팅 가능 yagom as? UniversityStudent // 캐스팅 불가, nil
- as! - 강제 다운캐스팅
mike as? UniversityStudent // 캐스팅 가능 yagom as? UniversityStudent // 캐스팅 불가, 런타임에러
- 자식 클래스의 인스턴스로 사용할 수 있도록 인스턴스 타입 정보를 전환한다.
assert 와 guard
- 애플리케이션 동작 도중 결과값을 동적으로 확인하고 안전하게 처리할 수 있도록 확인하고 빠르게 처리할 수 있다.
assert
- 디버깅중에만 사용됨
var someInt: Int = 1 assert(someInt == 0, "someInt != 0"). // 디버깅에서 동작을 중지함
guard
- 프로덕션에서도 사용됨
- 빠른 종료를 위해 사용됨
- if else 의 축약절정도로 사용됨
func guard(age: Int?) { guard let unwrappedAage = age, unwrappedAge < 130 unwrappedAge >= else { print("나이 값 입력이 잘못되었습니다.") return // 반드시 return 이 필요함 } }
프로토콜
- 인터페이스 정의
- 구현할거라 선언하면 채택(Adopted)
- 구현을 실행하면 준수(Conform)
- 프로토콜은 다중 상속 가능
struct Person: Talkable { // 프로토콜을 구현하여 준수 }
- 상속과 함께 쓰려면
상속, 프로토콜, 프로토콜
순으로 작성해야함 is
,as
사용 가능
익스텐션
- 타입을 알고있으면 새로운 기능을 추가할 수 있음
- 프로토콜 확장 가능
extension Int { var isEven: Bool { return self % 2 == 0 } }
오류처리
Error
- 주로 열거형을 통해 오류를 통해 표현함
- catch 에러를 생략 가능
- do 만 사용 가능
- try? 를 사용해서 결과 값을 nil 로 받음
- try! 를 사용하면 런타임 에러가 발생
enum VendingMachineError: Error { case invalidInput case insufficeintFunds(moneyNeeded: Int) case outOfStock }
func receiveMoney(_ money: Int) throws { guard money > 0 else { throw VendingMachinError.invalidInput } }
do { try receiveMoney(0) } catch VendingMachinError.invalidInput{ }
고차 함수
- map, filter ,reduce
- 인자 생략 가능 $0
- 후행 클로저로 전달 가능
접근 수준
- 상위 요소보다 하위 요소가 더 높은 접근 수준을 가질 수 없다.
private class AClasee { public func someMethod() {. // private 취급 됨 } }
- public: 어디서든, 상속 후 재정의 불가
- open: 클래스와 클래스 멤버에서만 사용 가능, 상속 후 재정의 가능
- internal: 기본 접근 수준, 모듈레벨
- filepriavte: 소스파일 내부에서만 사용 가능
- private: 구현한 범위 내
- 프로퍼티의 get, set 은 따로 접근 수준 부여가 가능하다.
프로토콜 지향 언어
- protocol, extension 을 활용하여 중복을 없앨 수 있음
- 프로토콜의 요구사항을 익스텐션으로 구현하는 것을 프로토콜 초기구혀닝라고 한다.
'기술 > iOS' 카테고리의 다른 글
Swift 5.6 Control Flow (0) 2022.02.26 Swift 5.6 Arrays (0) 2022.02.26 Swift 5.6 Strings and Characters (0) 2022.02.25 Swift 5.6 Basic Operators (0) 2022.02.25 Swift 5.6 The Basics (0) 2022.02.25