[iOS] 4주차 정리

Zoe·2022년 6월 29일
0

iOS

목록 보기
5/39

4주차 정리


✅ 클래스의 상속

1️⃣ 상속과 초기화

  • 기본적으로 상속은 데이터(저장 속성)을 추가하는 관점에서 생각
  • 저장 속성은 메모리 공간을 가짐
  • 메모리가 하나씩 더 추가되는 것
  • Base class : 어떤 것도 상속하지 않는 것
  • 다중 상속 : 불가능
  • 클래스만 상속 가능!! 구조체 불가
  • final : 상속이 불가능하다는 의미. 멤버 앞에 붙이면 재정의 불가능하다는 의미.
  • 재정의 : 상위 클래스에서 존재하는 멤버를 변형
  • 저장 속성은 재정의 불가. 왜냐면 가리키고 있는 메모리 구조이기 때문!
  • UIKit : Cocoa touch framework의 UI를 담당하는 녀석
  • 메서드 부분은 배열로 이루어져 있음!
  • 저장 속성과 메서드는 메모리 구조가 완전 다름

2️⃣ 상속과 재정의

  • 저장 속성만 재정의가 불가능! 데이터 구조이기 때문에. 상위의 데이터구조는 하위에서 절대 변형시킬 수 없음.
  • 재정의하지 않으면 자동으로 그 해당 함수를 가지고 있는 것
  • 부모클래스와 실제로 하는 일이 조금 다른 것
  • 메서드는 항상 배열로 존재
    🌟 저장 속성 vs 메서드 : 저장 속성은 메모리 주소를 가리키는 구조여서 재정의 불가. 메서드는 배열로 된 테이블을 다시 만들기 때문에 재정의가 가능. 메서드를 재정의 안 한다고 가정하면 메모리 주소를 그냥 가져오는 것. 외우지 말고 당연하게 받아들이기.

😎 정리

  • 대원칙 1 : 저장 속성 재정의 불가 -> 메모리 구조에서 상위 구현을 참조하기 때문
  • 대원칙 2 : 메서드는 재정의 가능(기능 확장만 가능)
  • 계산 속성 재정의 가능.

class SomeSuperclass {
    // 저장속성
    var aValue = 0
    
    // 메서드
    func doSomething() {
        print("Do something")
    }
}



class SomeSubclass: SomeSuperclass {
    // 저장속성의 재정의는 원칙적 불가
    //override var aValue = 3
    
    // 저장속성 ===> (재정의) 계산속성
    // 그러나, 메서드 형태로 부수적 추가는 가능
    override var aValue: Int {
        get {
            return 1
        }
        set {    // self로 쓰면 안됨
            super.aValue = newValue
        }
    }
    
    // 메서드는 (어떤 형태로든)재정의 가능 ===> 변형 / 대체
    override func doSomething() {
        super.doSomething()
        print("Do something 2")
        //super.doSomething()
    }
}

class Vehicle {
    var currentSpeed = 0.0

    var halfSpeed: Double {
        get {
            return currentSpeed / 2
        }
        set {
            currentSpeed = newValue * 2
        }
    }
}



class Bicycle: Vehicle {
    // 저장 속성 추가는 당연히 가능
    var hasBasket = false
    
    // 1) 저장속성 ===> ⭐️ 계산속성으로 재정의(메서드 추가) 가능
    override var currentSpeed: Double {
        // 상위 속성이기 때문에 super키워드 필요
        get {
            return super.currentSpeed       // 1.0
        }
        set {
            super.currentSpeed = newValue
        }
    }

    // 1) 저장속성 ===> ⭐️ 속성감시자를 추가하는 재정의(메서드 추가)는 가능
    override var currentSpeed: Double {
        // 상위 속성이기 때문에 super키워드 필요
        willSet {
            print("값이 \(currentSpeed)에서 \(newValue)로 변경 예정")
        }
        didSet {
            print("값이 \(oldValue)에서 \(currentSpeed)로 변경 예정")
        }
    }
    
    // ⭐️ 계산속성을 재정의 가능 (super키워드 주의)
    override var halfSpeed: Double {
        get {
            return super.currentSpeed / 2
        }
        set {
            super.currentSpeed = newValue * 2
        }
    }
    
    // ⭐️ 계산속성을 재정의 하면서, 속성감시자 추가 가능 (속성감시자 부분 참고)
    override var halfSpeed: Double {
        willSet {
            print("값이 \(halfSpeed)에서 \(newValue)로 변경 예정")
        }
        didSet {
            print("값이 \(oldValue)에서 \(halfSpeed)로 변경 예정")
        }
    }
    
}

3️⃣ 메모리 구조를 통한 이해

  • 저장 속성과 메서드를 분리해서 생각.
  • 저장 속성 : 본인 고유의 데이터 메모리 영역을 가지고 있음.
  • 메서드 : 인스턴스에 존재하는 것이 아님. 메서드를 실행했을 때 원래 클래스(데이터 영역)를 찾아 감. 함수의 메모리 주소를 찾음. 코드 영역으로 가서 명령어 실행. 항상 단계 별로 새로운 배열을 가짐.

✅ 클래스의 초기화와 생성자

  • 생성자 오버로딩 가능
  • 오버로딩하는 이유 : 개발자에게 여러 가지 선택권을 줄 수 있음. 자유도 생김.

1️⃣ 멤버와이즈 이니셜라이저 - 구조체의 특별한 생성자

  • 생성자를 만들지 않아도 알아서 만들어줌. 일부, 전체 속성을 새로운 값으로 설정

2️⃣ 지정 생성자

  • init( .... ) 형태를 가지는 생성자
  • 지정생성자는 모든 저장 속성을 초기화해야함
  • 생성자를 1개이상 구현하면 기본 생성자를 제공하지 않음

3️⃣ 편의 생성자

  • 지정 생성자에 의존 및 호출
  • 실질적으로 가능한 지정생성자의 갯수를 줄이고, 편의생성자에서 지정생성자 호출하도록 하는 것이 바람직
  • 상속했을때, 편의생성자의 경우 서브클래스에서 재정의를 못함
class Aclass {
    var x: Int
    var y: Int
    
    init(x: Int, y: Int) {    // 지정생성자 - 모든 저장 속성 설정
        self.x = x
        self.y = y
    }
    
    convenience init() {     // 편의생성자 - (조금 편리하게 생성) 모든 저장 속성을 설정하지 않음
        self.init(x: 0, y: 0)
    }
}

// 상속이 일어나는 경우

class Bclass: Aclass {
    
    var z: Int
    
    init(x: Int, y: Int, z: Int) {    // 실제 메모리에 초기화 되는 시점
        self.z = z                 // (필수)
        //self.y = y               // 불가 (메모리 셋팅 전)
        super.init(x: x, y: y)     // (필수) 상위의 지정생성자 호출
        //self.z = 7
        //self.y = 7
        //self.doSomething()
    }
    
    convenience init(z: Int) {
        //self.z = 7     //==========> self에 접근불가
        self.init(x: 0, y: 0, z: z)
        //self.z = 7
    }
    
    convenience init() {
        self.init(z: 0)
    }
    
    func doSomething() {
        print("Do something")
    }
}

let a = Aclass(x: 1, y: 1)
let a1 = Aclass()

let b = Bclass(x: 1, y: 1, z: 1)
let b1 = Bclass(z: 2)
//b1.x
let b2 = Bclass()

4️⃣ 지정 생성자와 편의 생성자

// 음식

class Food {
    var name: String
    
    init(name: String) {     // 지정생성자
        self.name = name
    }
    
    convenience init() {     // 편의생성자 ===> 지정생성자 호출
        self.init(name: "[Unnamed]")
    }
}



let namedMeat = Food(name: "Bacon")   // namedMeat의 이름은 "Bacon"
namedMeat.name

let mysteryMeat = Food()      // mysteryMeat의 이름은 "[Unnamed]"
mysteryMeat.name


// 상위의 지정생성자 ⭐️
// init(name: String)    지정생성자
// convenience init()    편의생성자


// 레서피 재료

class RecipeIngredient: Food {
    var quantity: Int
    
    init(name: String, quantity: Int) {  // 모든 속성 초기화
        self.quantity = quantity
        super.init(name: name)
    }

    override convenience init(name: String) {    // 상위 지정생성자를 편의생성자로 재정의 ===> 지정생성자 호출
        self.init(name: name, quantity: 1)
    }
    // convenience init() { }      // 자동 상속 (예외 규칙)
}

✅ 확장(Extension)

1️⃣ 상속과 확장의 비교

  • 수직 확장 vs 수평 확장
  • 수직 확장 : 상속(클래스만 가능), 데이터 추가, 기능 변형.
  • 수평 확장 : 현재 존재하는 타입에 기능(메서드)을 추가하여 사용.
  • 확장은 본질적으로 기존의 메서드 테이블 이외에 메서드를 추가하는 것 -> direct dispatch
  • 확장한 메서드는 상속 시 재정의 불가.
  • 클래스의 경우 편의생성자만 추가 가능
  • 값타입(구조체, 열거형)의 경우, 지정 생성자 형태로도 (자유롭게) 생성자 구현 가능
profile
iOS 개발자😺

0개의 댓글