Swift 기본 문법 #3

원본 : https://www.notion.so/Week-3-53f19dfd2bab4d17b2d58cde89ac464a

학습목표

1️⃣ 타입과 인스턴스의 관계를 이해하고 코드로 나타낼 수 있다.

2️⃣ 타입을 설계하는 방법을 알고 응용할 수 있다.

3️⃣ 타입의 인스턴스를 초기화하는 방법을 알고 응용할 수 있다.

4️⃣ 옵셔널 체이닝을 이해하고 응용할 수 있다.

 01. 타입

: 데이터와 기능을 용도에 맞게 묶어 구조화해놓은 것 → 객체, 어떤 객체의 설꼐도

: 메모리에 생설될 타입의 인스턴스가 수행할 수 있는 기능과 동작을 상세하게 표현한 것

ex) 컴퓨터라고 정의된 것(명사) → 타입, 내 집에 있는 실체화된 컴퓨터 → 인스턴스

💡 인스턴스 : 클래스, 구조체, 열거형 등의 타입에서 생성된 실체(객체) → 타입은 이 실체를 위한 설계도임 셈

타입의 정의

  • 열거형(Enumeration)
  • 구조체(Struct)
  • 클래스(Class)

타입을 정의할 때 타입명은 대문자로 시작 !

  • Computer라는 이름의 구조체(Struct)타입을 정의해보자
import Foundation

//Computer라는 이름의 구조체(Struct) 타입 정의
struct Computer {
    var battery: Int = 100
    let manufacturer: String = "Apple"
    
    func saveData() {
        print("데이터를 저장합니다.")
    }
    
    func playVideo() {
        print("영상을 재생합니다.")
    }
}

컴퓨터의 설계도처럼 Computer라는 객체가 가져가야할 값들과 동작을 정의하는 거지롱.

같은 모듈 내에서 타입의 이름은 유일해야 하므로, 만약 같은 기능을 하는 타입을 Class로 구현하고자 한다면 이름을 다르게 해줘야함!

타입은 설계도일 뿐 → 설계도를 통한 실체를 생성해서 사용해야 함 = 인스턴스

타입의 설계 내부를 보면 우리에게 익숙한 변수들과 함수들이 보임.

  • 타입 내부
    • 타입과 연관된 값 : 속성(Property)
    • 타입과 연관된 함수 : 메소드(Method)

병아리인 우리들은 인스턴스 프로퍼티, 인스턴스 메소드만 다룹시당(타입 프로퍼티, 타입 메소드는 나중에)

  • 타입은 중첩 타입(Nested Type)으로도 설계할 수 있다
import Foundation

struct SoccerPlayer {
    enum Position {
        case goalKeeper, wing, back, forward
    }
    
    let name: String
    var position: SoccerPlayer.Position
}

struct BasketballPlayer {
    enum Position {
        case center, forward, `gaurd`
    }
    
    let name: String
    var posrition: BasketballPlayer.Position
}
💡 Structures and Classes : [https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html) 💡 Properties : [https://docs.swift.org/swift-book/LanguageGuide/Properties.html](https://docs.swift.org/swift-book/LanguageGuide/Properties.html) 💡 Methods : [https://docs.swift.org/swift-book/LanguageGuide/Methods.html](https://docs.swift.org/swift-book/LanguageGuide/Methods.html)

 02. 인스턴스

인스턴스(Instance)

타입은 설계도일 뿐이라, 설계도를 통한 실체(인스턴스)를 생성해 사용해아함

  • Computer 타입으로부터 인스턴스를 생성해 프로퍼티 값을 변경하거나 메소드로 동작을 실행하는 코드 예시
import Foundation

struct Computer {
    var battery: Int = 100
    let manufacturer: String = "Apple"
    
    func saveData() {
        print("데이터를 저장합니다")
    }
    
    func playVideo() {
        print("영상을 재생합니다")
    }
}

//Computer라는 설계도를 통해 실제 컴퓨터를 만들어내는 과정
// 등호 오른쪽은 Computer.init()과 동일한 표현
var myComputer: Computer = Computer()
myComputer.battery = 93
//오류 발생
myComputer.manufacturer = "eelijus LAB" //Error! 상수값 변경 불가
//인스턴스 myComputer의 saveData()메소드를 호출(call)
myComputer.saveData()
//OUTPUT : 테이터를 저장합니다

var sujiComputer: Computer = Computer()
sujiComputer.battery = 92
sujiComputer.playVideo()
//OUTPUT : 영상을 재생합니다

 03. 인스턴스의 초기화

이니셜라이저(Initializer)

: 각 타입의 이니셜러이저를 통해 인스턴스의 생성과 초기화 가능

ex) let computer = Computer()

축약표현 : () == init()

인스턴스는 메모리에 생성되고, 쓸모를 다하면 메모리에서 소멸됨. 그 소멸 시점에 특별한 처리가 필요하다면 deinit 메소드를 사용할 수 있다.

💡 스위프트는 안전함(Safe)를 지향하는 언어

변수 혹은 상수에 제대로 된 값이 들어있지 않은 상황에서 해당 변/상수에 접근하 → 런타임 에러

스위프트는 이를 미연에 방지하고 옵셔널, 인스턴스의 초기화 과정의 규칙 같은 안전장치를 만듦.

인스턴스 초기화의 규칙

  • 대전제 :

    인스턴스의 프로퍼티에 값이 있다는 것이 항상 보장되어야 한다

    → 이니셜라이저 내부에서 항상 인스턴스의 프로퍼티 값을 적절한 값으로 초기화해주거나, 인스턴스 프로퍼티가 기본값을 가지고 있어야 함

인스턴스의 생성과 소멸

스위프트의 모든 인스턴스는 초기화와 동시에 모든 프로퍼티에 유효값이 할당돼있어야 함.

프로퍼티에 미리 기본값을 할당해두면, 인스턴스가 생성됨과 동시에 초기값을 지니게 됨.

import Foundation
import Swift

//이니셜라이저와 디이니셜라이저
//   init   ,  deinit

class PersonA {
    //모든 저장 프로퍼티에 기본값 할당
    var name: String = "unknown"
    var age: Int = 0
    var nickName: String = "nick"
}

//인스턴스 생성 후,
let jason: PersonA = PersonA()
//프로퍼티에 적절한 값 할당
jason.name = "jason"
jason.age = 100
jason.nickName = "j"

//프로퍼티 기본값을 지정하기 어려운 경우에는 이니셜라이절르 통해 인스터가 가져야할 초기값을 전달 가능

class PersonB {
    var name: String
    var age: Int
    var nickName: String
    
    //이니셜라이저
    init(name: String, age: Int, nickName: String) {
        self.name = name
        self.age = age
        self.nickName = nickName
    }
}

//원하는 프로퍼티값으로 바로 인스턴스 생성 가능
let hana: PersonB = PersonB(name: "hana", age: 20, nickName: "하나")
//에러 -> 인자로 값이 안들어옴
//let hana: PersonB = PersonB(name: "hana", age: 20, nickName: "") Error!

//프로퍼티의 초기값이 꼭 필요없을때는 옵셔널을 사용!
class PersonC {
    var name: String
    var age: Int
    //이 프로퍼티는 꼭 필요한 프로퍼티는 아니야~
    var nickName: String?   //nickName 프로퍼티에는 nil값이 들어와도 돼~
    
    init(name: String, age: Int, nickName: String) {
        self.name = name
        self.nickName = nickName
        self.age = age
    }
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let jenny: PersonC = PersonC(name: "jenny", age: 10)
let mick: PersonC = PersonC(name: "mick", age: 15, nickName: "nn")

//암시적 추출 옵셔널은 인스턴스 사용에 꼭 필요하지만 초기값을 할당하지 않고자 할 때 사용
class Puppy {
    var name: String
    //꼭 필요한 프로퍼티지만 이니셜라이징 때는 할당못하고, 나중에 따로 세팅해주겠다?
    var owner: PersonC!
    
    init(name: String) {
        self.name = name
    }
    
    func goOut() {
        print("\(name)가 보호자 \(owner.name)와 함께 산책을 하네요")
    }
}

let happy: Puppy = Puppy(name: "happy")
//happy.goOut()  :Error! 런타임 오류 발생 84번째 줄에 오류 메세지 뜸
//owner가 nil인 상태라 크러쉬 남
happy.owner = jenny //PersonC의 인스턴스인 jenny
happy.goOut()

//실패가능한 이니셜라이저

class PersonD {
    var name: String
    var age: Int
    var nickName: String?
    
    init?(name: String, age: Int) {
        if (0...120).contains(age) == false {
            return nil
        }
        
        if name.count == 0 {
            return nil
        }
        
        self.name = name
        self.age = age
    }
}

//에러 발생 -> 옵셔널이 아닌 타입으로 PersonD의 인스턴스 생성하려고 함
//let cock: PersonD = PersonD(name: "cock", age: 23)
let cock: PersonD? = PersonD(name: "cock", age: 23)
let jocker: PersonD? = PersonD(name: "jocker", age: 140)
let jin: PersonD? = PersonD(name: "", age: 30)

print(cock)
print(jocker) //OUTPUT : nil
print(jin)    //OUTPUT : nil

// deinitializer

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
        }
    }
}

var donald: PersonE? = PersonE(name: "donald", child: jenny)
donald?.pet = happy
//donald 인스턴스가 더이상 필요없으므로 메모리에서 해제됨
donald = nil
//donal가 jenny에게 happy를 인도함 -> 디이니셜라이저에서 구현돼있음
  • 이니셜라이저 중복
//self.name이나 self.age처럼 이니셜라이저 내부에 중복되는 부분이 있을 경우
		init(name: String, age: Int, nickName: String) {
        self.name = name
        self.nickName = nickName
        self.age = age
    }
    
		init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

//아래처럼 만들어둔 이니셜라이즈로 대체할 수 있다
//자신의 이니셜라이저를 호출해줄때는 convinience를 붙여서!
		convinience init(name: String, age: Int, nickName: String) {
				self.init(name: String, age: Int)
        self.age = age
    }

		init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
  • 실패 가능한 이니셜라이저

이니셜라이저 매개변수로 전달되는 초기값이 잘못된 경우 인스턴스 생성에 실패할 수 있음.

→ 실패 시 nil 반환

따라서 실패가능한 이니셜라이저의 반환타입은 옵셔널 타입.

//실패가능한 이니셜라이저

class PersonD {
    var name: String
    var age: Int
    var nickName: String?
    
    init?(name: String, age: Int) {
        if (0...120).contains(age) == false {
            return nil
        }
        
        if name.count == 0 {
            return nil
        }
        
        self.name = name
        self.age = age
    }
}

//에러 발생 -> 옵셔널이 아닌 타입으로 PersonD의 인스턴스 생성하려고 함
//let cock: PersonD = PersonD(name: "cock", age: 23)
let cock: PersonD? = PersonD(name: "cock", age: 23)
let jocker: PersonD? = PersonD(name: "jocker", age: 140)
let jin: PersonD? = PersonD(name: "", age: 30)

print(cock)
print(jocker) //OUTPUT : nil
print(jin)    //OUTPUT : nil
  • 디이니셜라이저

deinit은 클래스와 인스턴스가 메모리에서 해제되는 시점에 호출됨

인스턴스가 해제되는 시점에 해야할 일은 구현해주는 부분 → class 타입만 가능

// deinitializer

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
        }
    }
}

var donald: PersonE? = PersonE(name: "donald", child: jenny)
donald?.pet = happy
//donald 인스턴스가 더이상 필요없으므로 메모리에서 해제됨
donald = nil
//donal가 jenny에게 happy를 인도함 -> 디이니셜라이저에서 구현돼있음
💡 unwrap하다 → ?를 뒤에 붙여서
  • 에어컨을 타입으로 만들어보고 인스턴스를 생성해보자~
import Foundation
import Swift

//에어컨을 타입으로 만들어보고 인스턴스를 생성해보자!

//설계도(타입)
class Aircon {
    var brand: String
    var size: Int
    var color: String?
    
    init(brand: String, size: Int)
    {
        self.brand = brand
        self.size = size
    }
    
    func turnOn(temp: Int) {
        print("온도를 \(temp)도로 설정합니다")
    }
}

let myAirCon: Aircon = Aircon(brand: "samsung", size: 180)

myAirCon.turnOn(temp: 23)
//OUTPUT : 온도를 23도로 설정합니다
💡 Swift Language - Initialization : [https://docs.swift.org/swift-book/LanguageGuide/Initialization.html](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html) 💡 Swift Language - Deinitialization: [https://docs.swift.org/swift-book/LanguageGuide/Deinitialization.html](https://docs.swift.org/swift-book/LanguageGuide/Deinitialization.html) 💡 Swift Languea - Automatic Reference Counting : [https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html)

 04. 구조체와 클래스

구조체와 클래스의 차이는 뭘까?

1. 초기화

import Foundation

//Error! 클래스 PersonA에 이니셜라이저 없다고 에러뜸
//인스턴스의 프로퍼티에 값이 있다는 것이 항상 보장되어야하기 때문
class PersonA {
    var name: String
    var age: Int
}

//구조체는 memberwise라는 기능을 제공해 에러가 발생하지 않는다.
struct PersonB {
    var name: String
    var age: Int
}

2. deinit

디이셜라이저는 클래스에만 제공되는 메서드

왜 클래스만? 구조체와 클래스는 메모리 위에서 관리되는 방식이 다르기 때문

3. 상속

클래스에서만 가능한 강력한 기능

4. mutating

구조체 : 자신의 프로퍼티값을 변경하는 메서드 앞에 mutating키워드를 붙여야함.

import Foundation

class PersonA {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func addOneToAge() {
        self.age += 1
    }
}

struct PersonB {
    var name: String
    var age: Int
    
		//mutating 키워드 없을 시 컴파일 에러!
    mutating func addOneToAge() {
        self.age += 1
    }
}

let sally: PersonA = PersonA(name: "sally", age: 10)
sally.addOneToAge()
print(sally.age)

왜 구조체에는 이런 키워드가 필요할까? 👇

값 타입 vs 참조 타입

상속과 더불어 구조체와 클래스의 아주 중요한 차이점 !

  • 구조체 : 값 타입(Value Type) → 인스턴스에 값을 전달 시 값을 복사하여 전달
  • 클래스 : 참조 타입(Reference Type) → 인스턴스에게 값 전달 시 값을 참조하여 전달

예시코드의 결과를 예상해봅시당

import Foundation

class CameraA {
    var color = "Black"
}

struct CameraB {
    var color = "Black"
}

func printCameraColor() {
    let str = """
    CameraA (my: \(myCameraA.color), yours: \(yourCameraA.color))
    CameraB (my: \(myCameraB.color), yours: \(yourCameraB.color))
    """
    print(str)
}

var myCameraA = CameraA()
var yourCameraA = myCameraA

var myCameraB = CameraB()
var yourCameraB = myCameraB

printCameraColor()

print("------- myCamera 색을 변경합니다 --------")

myCameraA.color = "White"
myCameraB.color = "White"

printCameraColor()

OUTPUT

CameraA (my: Black, yours: Black)
CameraB (my: Black, yours: Black)
------- myCamera 색을 변경합니다 --------
CameraA (my: White, yours: White)
CameraB (my: White, yours: Black)

블로그에 글을 작성해서 친구에게 공유한다고 생각해보자.

  1. 블로그의 링크(주소값)을 전달하는 방법
  2. 블로그의 글을 복사해서 전달하는 방법

이렇게 두 가지가 있을 것임.

1번 방법으로 주소값을 통해 내 글을 참조하고 있는

Class

  • 전통적인 OOP 관점에서의 클래스
  • 단일상속
  • (인스턴스/타입) 메서드
  • (인스턴스/타입) 프로퍼티
  • 참조 타입
  • Apple 프레임워크의 대대분의 뼈대는 모두 클래스로 구성

Struct

  • C언어 등의 구조체보다 다양한 기능
  • 상속 불가
  • (인스턴스/타입) 메서드
  • (인스턴스/타입) 프로퍼티
  • 값 타입
  • Swift의 대부분의 큰 뼈대는 모두 구조체로 구성

Enum

  • 다른 언어의 열거형과는 많이 다른 존재
  • 상속 불가
  • (인스턴스/타입) 메서드
  • (인스턴스/타입) 연산 프로퍼티
  • 값 타입
  • Enumeration
  • 유사한 종류의 여러 값을 유의미한 이름으로 한 곳에 모아 정의
  • 열거형 자체가 하나의 데이터 타입
  • 열거형의 case 하나하나 전부 유의미한 값으로 취급
  • 선언 키워드 - enum
ClassStructEnum
TypeReference(참조)ValueValue
Subclassing(상속)OXX
ExtensionOOO

구조체는 언제 사용하나?

  • 연관된 몇몇의 값들을 모아서 하나의 데이터타입으로 표현하고 싶을 때
  • 다른 객체 또는 함수 등으로 전달될 때 참조가 아닌 복사를 원할 때
  • 자신을 상속할 필요가 없거나, 자신이 다른 타입을 상속받을 필요가 없을때
  • Apple 프레임워크에서 프로그래밍을 할 때에는 주로 클래스를 많이 이용

Value vs Reference

  • Value

    데이터를 전달할 때 값을 복사하여 전달

  • Reference

    데이터를 전달할 때 값의 메모리 위치를 전달

import Foundation

struct ValueType {
    var property = 1
}

class ReferenceType {
    var property = 1
}

//******** Struct ********\\

//구조체 ValueType의 인스턴스 생성
let firstStructInstance = ValueType()

var secondStrunctInstance = firstStructInstance
secondStrunctInstance.property = 2

print("first struct instance property : \(firstStructInstance.property)")
//OUTPUT : 1
print("secod struct instance property : \(secondStrunctInstance.property)")
//OUTPUT : 2

//******** Class ********\\

let firstClassInstance = ReferenceType()
//첫번째 인스턴스의 참조값을 두번째 인스턴스에 할당해줌
var secondClassInstance = firstClassInstance
secondClassInstance.property = 2

print("first class instance property : \(firstClassInstance.property)")
//OUTPUT : 2
print("second class instance property : \(secondClassInstance.property)")
//OUTPUT : 2

위 코드에선 fistClassInstance가 가리키는 class의 프로퍼티를 secondClassInstance도 가리키게 된다.

아래와 같은 코드는 어떨까?

//******** Class ********\\

let firstClassInstance = ReferenceType()
var secondClassInstance = ReferenceType()
secondClassInstance.property = 2

print("first class instance property : \(firstClassInstance.property)")
//OUTPUT : 1
print("second class instance property : \(secondClassInstance.property)")
//OUTPUT : 2

둘 다 각각의 클래스의 인스턴스가 되기 때문에

fistClassInstance가 가리키는 Class와 secondClassInstance가 가리키는 Class가 다르다.

인자로 전달된 인스턴스의 프로퍼티 값은 어떨까?

  • 구조체의 경우
import Foundation

struct SomeStruct {
    var someProperty: String = "Property"
}

//디폴트 인스턴스 새성
var someStructInstance: SomeStruct = SomeStruct()

//someFunction이라는 함수 생성 -> structInstance라는 매개변수명으로 구조체 SomeStruct를 받음
func someFunction(structInstance: SomeStruct) {
    //내부 변수 생성 -> SomeStruct타입의 localVar에 structInstance 할당
    var localVar: SomeStruct = structInstance
    localVar.someProperty = "ABC"
}

someFunction(structInstance: someStructInstance)
print(someStructInstance.someProperty)
//OUTPUT : Property

위와 같은 결과가 나오는 이유는 뭘까?

someFunction에 SomeStruct가 인자로 넘어갈때, 그 값이 복사돼서 전달된다.

그러니 함수 스코프안에서 변경해주는 프로퍼티값은 SomeStruct의 원래 프로퍼티값이 아니라,

복사돼서 someFunction에 넘어간 프로퍼티 값이라는 것.

  • 클래스의 경우
import Foundation

class SomeClass {
    var someProperty: String = "Property"
}

var someClassInstance: SomeClass = SomeClass()

func someFunction(classInstance: SomeClass) {
    var localVar: SomeClass = someClassInstance
    localVar.someProperty = "ABC"
}

someFunction(classInstance: someClassInstance)
print(someClassInstance.someProperty)

Daty Types In Swift

  • 스위프트는 (클래스보다)구조체, 열거형 사용을 선호
  • Apple 프레임워크는 대부분 클래스 사용
  • Apple 프레임워크 사용시 구조체/클래스 선택은 우리의 몫

 05. 프로퍼티

프로퍼티 (Property)

: 타입(구조체, 클래스, 열거형) 내부 에 정의된 상수나 변수. 타입과 연관된 값 표현

프로퍼티의 역할에 따른 종류

우리가 여태까지 사용한 프로퍼티들은 모두 저장 프로퍼티

  • 저장 프로퍼티 (stored property)
  • 연산 프로퍼티 (computed property) → var로만 선언 가능
  • 인스턴스 프로퍼티 (instance property)
  • 타입 프로퍼티 (type property)

열거형 내부에는 연산 프로퍼티만 구현할 수 있다 !

ㅎㅏ는중.....

  • 코드

    import Foundation
    
    struct Student {
        
        //인스턴스 저장 프로퍼티 -> 값을 저장하기 위한 프로퍼티
        var name: String = ""
        var `class`: String = "Swift"
        var koreanAge: Int = 0
        
        //인스턴스 연산 프로퍼티 -> 특정한 연산을 수행해주기 위한 프로퍼티
        var westerAge: Int {
            get {
                return koreanAge - 1
            }
            
            set(inputValue) {
                koreanAge = inputValue + 1
            }
        }
        
        //타입 저장 프로퍼티 -> 타입과 연관돼서 저장되야 할 프로퍼티
        static var typeDescription: String = "학생"
    
        
        //인스턴스 메서드 -> 연산 프로퍼티가 대신해줄 수 있음(매개변수, 반환값 없는 경우)
    //    func selfIntroduce() -> String {
    //        print("저는 \(self.class)반 \(name)입니다")
    //    }
        
        
        //위 펑션과 같은 기능을 하는
        //읽기전용 인스턴스 연산 프로퍼티
        var selfIntroduce: String {
            get {
                return "저는 \(self.class)\(name)입니다"
            }
        }
        
        //타입 메서드
    //    static func selfIntroduce() {
    //        print("학생 타입입니다")
    //    }
        
        //읽기전용 타입 연산 프로퍼티
        //읽기전용에서는 get을 생략할 수 있음
        static var selfIntroduction: String {
            return "학생타입 입니다"
        }
        
    //타입 연산 프로퍼티 사용
        print(Student.selfIntroduction)
    //OUTPUT : 학생타입 입니다
    
    //인스턴스 저장 프로퍼티 사용
        
        
    }

 06. 상속

상속 : 클래스 / 프로토콜에서 가능

클래스의 상속

: 부모 클래스가 자식 클래스에게 프로퍼티와 메서드를 물려주는 것

import Foundation

class Bus {
    var passangerCapacity: Int = 40
    var numverOfWheels: Int = 4
    func go(to: String) {}
}

class Truck {
    var loadageCapacity: Int = 1000
    var numberOfWheels: Int = 4
    func go(to: String) {}
}

class SportCar {
    var maximumSpeed: Int = 360
    var numberOfWheels: Int = 4
    func go(to: String) {}
}

위의 코드를 보면 class들 간에 중복되는 코드들이 있다. 저걸 하나로 빼줄 수는 없을까?

아래와 같이 Vehicle 클래스를 만들어 상속받게 해보자.

class Vehicle {
    var numverOfWheels: Int = 4
    func go(to: String) {}
}
import Foundation

class Vehicle {
    var numberOfWheels: Int = 4
    func go(to: String) {}
}

class Bus: Vehicle {
    var passangerCapacity: Int = 40
//    var numberOfWheels: Int = 4
//    func go(to: String) {}
}

class Truck: Vehicle {
    var loadageCapacity: Int = 1000
//    var numberOfWheels: Int = 4
//    func go(to: String) {}
}

class SportCar: Vehicle {
    var maximumSpeed: Int = 360
//    var numberOfWheels: Int = 4
//    func go(to: String) {}
}

var myCar: SportCar = SportCar()

print(myCar.numberOfWheels)
//OUTPUT : 4

서브클래싱 - 상속의 상속

  • 예시 코드 1
import Foundation

class Vehicle {
    var numberOfWheels: Int = 4
    func go(to: String) {}
}

class Truck: Vehicle {
    var loadageCapacity: Int = 1000
}

class DumpTruck: Truck {
    func spillContents() {}
}

DumpTruckTrcuk의 모든 기능을 상속받는다. 물론 Vehicle의 기능도 상속받음.

💡 Swift는 단일 상속!
  • 예시 코드 2
import Foundation

//Person - Studenct - Kchoi 의 클래스 상속 구조를 만들어보자

class Person {
    var weight: Int = 60
    
    func eat() {
        weight  += 1
    }
}

class Student: Person {
    var skillStack: String = "programming language"
    
    func study() {
        skillStack += " better"
    }
}

var kchoi: Student = Student()
//var kchoi = Student()

print(kchoi.weight)
//OUTPUT : 60
kchoi.eat()
print(kchoi.weight)
//OUTPUT : 61

kchoi.skillStack = "flutter"
print(kchoi.skillStack)
//OUTPUT : flutter
kchoi.study()
print(kchoi.skillStack)
//OUTPUT : flutter better

override

  • override : 재정의 구문

상속받은 클래스의 기능을 꼭 그대로 받지 않고 자식 클래스가 부모 클래스의 기능을 변경해줄 수 있다.

파라미터 재정의는 노노. 코드 블록 부분만 재정의 가능.

프로퍼티 재정의도 가능

프로퍼티 오버라이딩 시에는 상위 클래스에서 저장 프로퍼티였건 연산 프로퍼티였건 무조건 get, set을 모두 제공하는 연산 프로퍼티로 override해야함

그러다가 다시 부모 클래스의 기능을 그대로 사용하고 싶으면?

  • super 키워드로 호출해주면 된다.
import Foundation

//Person - Studenct - Kchoi 의 클래스 상속 구조를 만들어보자

class Person {
    var weight: Int = 60
    
    func eat() {
        weight  += 1
    }
}

class Student: Person {
    var skillStack: String = "programming language"
    
    func study() {
        skillStack += " better"
    }
    
    //override 키워드로 메서드 재정의
    override func eat() {
        weight += 5
    }
    
    //super 키워드로 부모 클래스의 메서드 그대로 사용
    func eatAsPerson() {
        super.eat()
    }
}
  • final class : 재정의 불가 타입 메서드 - static과 동일한 기능

영상 봐야함.. https://www.youtube.com/watch?v=T60mxyxxdys

💡 Inheritance : [https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html](https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html)

 07. 옵셔널 체이닝과 nil 병합 연산자

옵셔널 체이닝

nil 병합 연산자

💡 Swift Language - Optional Chaining : [https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html](https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html) 💡 Swift Language - Basic Operators(Nil Coalescing Operator) : [https://docs.swift.org/swift-book/LanguageGuide/BasicOperators.html](https://docs.swift.org/swift-book/LanguageGuide/BasicOperators.html)

 08. 객체지향 프로그래밍

객체지향 프로그래밍 패러다임

OOP : Object Oriented Programming

패러다임 : 관점, 시각

프로그램을 만드는 것을 어떻게 바라볼까 - 프로그래밍 패러다임

그 중에 하나가 객체지향 프로그래밍 패러다임

ㅎ ㅏ는중....

 09. 타입의 설계

타입은 실제로 언제 사용하고, 어떤 과정을 거쳐서 설계할까?

프로그래밍에서 타입은 왜 필요할까?

좋은 웹페이지 즐겨찾기