클래스의 상속
기본 개념
- 수직 확장
- 본질적으로 성격이 비슷한 타입을 새로 만들어
1). 데이터(저장속성)를 추가(즉, 메모리가 추가)하거나
- 저장 속성은 메모리 공간을 갖는다
2). 기능(메서드)를 변형시켜서 사용 
 
- 본질적으로 새로운 타입을 만드는 것이지만
 
- 기본적으로 데이터(저장 속성)을 추가하는 관점에서 생각한다
 
- 저장 속성 : 기본적으로 메모리 공간을 가지고 있음.
 
class Person {	
	var id = 0
    var name = "이름"
    var email = "abc@gmail.com"
}
class Student: Person {	
	var studentId = 0
}	
class Undergraduate: Student {
	var major = "전공"
}	
var person1 = Undergraduate()
person1.id
person1.studentId
- 위에 아무것도 상속하지 않은 클래스 (처음 클래스) : base class
 
- 상대적으로 child class/parent class, sub class/super class
 
final
- class 선언 시 final을 붙이면 상속할 수 없다
 
- 멤버 앞에 final을 붙이면, 재정의가 불가능하다
(재정의 : 상위 클래스에 존재하는 멤버를 변형하는 것) 
재정의 (Overriding)
- "재정의가 불가" <- 기존 메모리 공간을 그대로 유지해야 하기 때문
 
- overloading : 함수에서 하나의 이름에 여러 함수를 대응시켜서 사용
 
- overriding : 재정의. 클래스의 상속에서 상위클래스의 속성/메서드의 기능을 약간 변형하여 사용하는 것
 
- 재정의 :  sub class에서 super class의 동일한 멤버를 변형하여 구현한다
 
- 재정의 : 상위 클래스에서 존재하는 멤버를 변형하는 것
 
- 재정의가 가능한 대상 (각각 재정의 방식이 다르다)
- 속성 (저장 속성은 불가능 -> 데이터 구조의 변형 불가)
- 데이터(저장 속성)는 추가만 가능
 
- 계산 속성, ... 가능
- 계산 속성은 실질적으로 메서드이기 때문에 메서드쪽에 더 가까움
 
 
 
- 메서드 (메서드, 서브스크립트, 생성자 -> 기능의 변형 가능)
- 기능(메서드)는 추가 및 변형(대체) 가능
 
- 상위 구현을 호출하는 경우도 많다 (super.~)
 
 
- 메모리 구조가 다르기 때문. (공간 차지 vs 주소 저장) (강의자료 참고)
- 상속 과정에서 저장 속성은 이전 super class의 데이터를 가리킴
 
- 하지만 메서드들은 계속 새로운 배열을 생성
 
 
 
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 {		
        	super.aValue = newValue
        }
    }
    
    
    override func doSomething() {
    	super.doSomething()		
        print("Do something 2")	
        
    }
}
재정의 방식
속성의 재정의 (엄격)
- 실질적으로 타입 속성을 재정의하는 일은 매우 드물다. 일단 배제하고 생각하자
 
- 저장 속성의 재정의
- 원칙적으로 불가능 (고유의 메모리 공간 유지 필요)
- 하위 클래스에서 메모리 공간을 바꾸는 방식으로의 재정의는 불가능
 
 
- 메서드 형태로 추가하는 방식의 재정의 가능
- 읽기/쓰기 가능한 계산 속성으로 재정의 가능.
(기능 축소는 불가능. read-only로 불가능)
- 기존 저장 속성을 생각해보면, 읽기/쓰기가 모두 가능하다
 
- 따라서 이걸 읽기만 가능한 계산 속성으로 기능을 축소하는 건 불가능하다
 
 
- 속성 감시자 추가 가능 (메서드)
 
 
 
class Vehicle {
	
	var currentSpeed = 0.0
    
    
    var halfSpeed: Double {
    	get {
        	return currentSpeed / 2
        }
        set {
        	currentSpeed = newValue * 2
        }
    }
}
class Bicycle: Vehicle {
	
    
    var hasBasket = false
    
     저장 속성을 재정의 -> 계산 속성으로 재정의했다
    override var currentSpeed: Double {
    	
        
        get {
        	return super.currentSpeed
        }
        set {
        	super.currentSpeed = newValue
        }
    }
    
     저장 속성을 재정의 -> 속성 감시자를 추가하는 재정의
    
    override var currentSpeed: Double {
    	
        
        willSet {
        	print("값이 \(currentSpeed)에서 \(newValue)로 변경 예정")
        }
        didSet {
        	print("값이 \(oldValue)에서 \(currentSpeed)로 변경 예정")
        }
    }
    
     계산 속성을 재정의
    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)로 변경 예정")
        }
}
메서드(계산 속성)의 재정의
- 기능의 범위 축소하는 재정의는 불가능
- 읽기 전용 -> 읽기/쓰기 가능한 속성으로 재정의 가능 (확장o)
- 속성 감시자 추가하는 재정의는 불가능 
- 읽기 전용 속성은 어차피 값이 변할 일이 없다
 
- 따라서 속성 감시자를 추가한다는 게 논리적으로 맞지 않음
 
 
 
- 읽기/쓰기 -> 읽기만 가능한 속성으로 제정의 불가능 (기능 제한x)
- 속성 감시자 추가하는 재정의 가능 (관찰 가능)
 
 
 
class Vehicle1 {
	
	var currentSpeed = 0.0
    
    var datas = ["1", "2", "3", "4", "5"]
    
    
    func makeNoise() {
        print("경적을 울린다.")
    }
    
    
    subscript(index: Int) -> String {
        get {
            if index > 4 {
                return "0"
            }
            return datas[index]
        }
        set {
            datas[index] = newValue
        }
    }
}
class Bicycle1: Vehicle1 {
	 상위 -> 하위 호출 가능
    override func makeNoise() {
    	super.makeNoise()
        print("자전거가 지나간다고 소리친다")
    }
    
    
     하위 -> 상위 호출 가능
    override func makeNoise() {
    	print("자전거가 지나간다고 소리친다")
        super.makeNoise()
    }
    
     상위 아예 무시 가능
    override func makeNoise() {
    	print("경적을 울리고, 자전거가 지나간다고 소리친다.")
    }
    
    
    
    overide subscript(index: Int) -> String {
    	get {
        	if index > 4 {
            	return "888"
            }
            return super[index]		
            
        }
        set {
        	supet[index] = newValue
        }
    }
}
let v = Bicycle1()
v.currentSpeed
v.makeNoise()
인스턴스 속성의 대원칙
- 저장 속성 재정의는 원칙적으로 불가능,
but 메서드 방식으로 추가는 가능 
- 계산 속성의 유지/확장은 가능, 축소는 불가능
 
- 속성 감시자(메서드)를 추가하는 재정의는 언제나 가능,
but 읽기전용 계산 속성을 관찰하는 건 의미 없으므로 불가능 
초기화 (Initialization)
- class, struct, enum에서 인스턴스를 생성하는 과정. 메모리에 찍어내는 과정
 
- 각 저장 속성에 대한 초기값 설정
-> 인스턴스를 사용가능한 상태로 만든다 
- 생성자의 실행이 완료되면, 인스턴스의 모든 저장속성이 초기값 가지도록 하는 것
-> 생성자의 역할 
- 초기화의 방법
- 저장 속성의 선언과 동시에 값 저장
 
- 저장 속성을 옵셔널로 선언 (초기값 없어도 nil로 초기화)
 
- 생성자에서 값 초기화
 
 
- 1, 2번 방법이면 생성자 구현하지 않아도 초기화 가능
 
생성자 구현
class Color {
    
    let red: Double
    let green: Double
    let blue: Double
    
    
    
    
    init() {      
        red = 0.0
        green = 0.0
        blue = 0.0
    }
    init(white: Double) {
        red   = white
        green = white
        blue  = white
    }
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
        self.blue  = blue
    }
    
    
}
var color = Color()   
var color2 = Color.init()
color = Color(white: 7.0)
color = Color.init(white: 7.0)
color = Color(red: 7.0, green: 7.0, blue: 7.0)
color = Color.init(red: 7.0, green: 7.0, blue: 7.0)
Memberwise Initializer
struct Color1 {	
    var red: Double
    var green: Double
    var blue: Double
    
    
    
}
color1 = Color1(red: 1.0, green: 1.0, blue: 1.0) 
- 생성자를 구현하지 않으면, 컴파일러가 기본 생성자를 자동으로 생성 -> "init()"
 
- 생성자를 구현하면, 기본 생성자를 자동으로 생성하지 않는다
 
- 구조체는 저장 속성들이 기본값을 가지고 있어도(다 가지고 있지 않아도),
추가적으로 Memberwise Initializer 자동으로 제공 
- 직접적으로 생성자 구현하면,
Memberwise Initializer 제공하지 않는다