상속

- 클래스는 메서드나 프로퍼티 등을 다른 클래스로부터 상속받을 수 있습니다.

- Super/Sub Class로 구분된다

- Swift의 Struct는 상속을 받을 수 없다

- Java에서의 상속의 개념과 같다

 

메서드 재정의

//func aa() 라는 메소드를 재정의
override func aa(){
	//...
}

 

프로퍼티 재정의

- 부모로부터 상속받은 인스턴스 프로퍼티나 타입 프로퍼티를 재정의할 수 있다

- 프로퍼티를 재정의하는 것은 프로퍼티 자체가 아니라, Getter/Setter/감시자 등을 재정의 하는 것을 의미함

- 부모 클래스에서 읽기전용이었어도 자식 클래스에서는 R/W 로 재정의 가능

  - B.U.T. R/W -> 읽기전용은 불가능

 

프로퍼티 감시자 재정의

- willSet / didSet 도 재정의 가능 -> 연산/저장 프로퍼티 가능

  - B.U.T. 상수 저장 / 읽기 연산 전용 프로퍼티는 재정의할 수 없다

    -> 상수 저장 프로퍼티나 읽기 전용 연산 프로퍼티는 값을 설정할 수 없으므로!

 

서브스크립트도 재정의가 가능하다

재정의 방지

- 부모 클래스를 상속받는 자식클래스에서 몇몇 특성을 재정의할 수 없도록 제한하기 위함

- final 키워드를 사용 (final var, final func, final class func ....)

 

클래스의 이니셜라이저 - 상속과 재정의

- 값 타입의 init 은 구분할 필요가 없지만 class에서는 지정/편의 init으로 구분된다

 

지정/편의 Init

지정 init

- 클래스의 주요 이니셜라이저

- 필요에 따라 부모클래스의 init을 호출할 수 있으며, class의 모든 프로퍼티를 초기화해야하는 임무를 맡음

- 클래스당 하나 이상 정의됨 -> 지정하지 않으면 기본 지정 init 사용

- if) 부모 클래스의 지정 init이 자식 클래스의 지정 init 역할이 가능하다면, 자식 클래스에서 지정 init 새성하지 않아도 됨

 

편의 init

- 초기화를 손쉽게 도와주는 역할

- 자신의 내부에서 지정 init을 호출함

- 지정 init의 매개변수가 많아, 외부에서 일일이 전달하기 어렵거나, 특정 목적에 사용하기 위해 사용

 

지정/편의 init 관계(클래스의 초기화 위임)

  1. 자식의 지정 init은 부모의 지정 init을 반드시 호출해야 함
  2. 편의 init은 자신을 정의한 클래스의 다른 init(편의/지정)을 반드시 호출
  3. 편의 init은 궁극적으로 지정 init을 반드시 호출

2단계 초기화

- 스위프트 컴파일러는 2단계 초기화를 오류없이 처리하기 위해 4가지 안전확인을 실행한다

  1. 자식의 지정 init이 부모의 init을 호출하기 전에 자신의 프로퍼티 모두 초기화 확인
  2. 자식의 지정 init은 상속받은 프로퍼티에 값을 할당하기 전에 반드시 부모의 init을 호출
  3. 편의 init은 자신의 프로퍼티를 포함하여, 어떤 프로퍼티라도 값을 할당하기 전에 다른 init을 호출
  4. 초기화 1단계를 마치기 전까지는 init은 인스턴스 메서드를 호출 불가
    1. 또, 인스턴스 프로퍼티의 값을 읽을 수도 없음
    2. self 프로퍼티를 자신의 인스턴스를 나타내는 값으로 활용 불가

- 클래스의 인스턴스는 초기화 1단계를 마치기전까지는 유효하지 않다

- 1단계를 거쳤을 때 비로소 유효한 인스턴스가 됨

 

  • 1단계
    • 클래스가 지정/편의 init을 호출
    • 클래스의 새로운 인스턴스를 위한 메모리가 할당 -> 아직 초기화되지 않은 상태
    • 지정 init은 클래스에 정의된 모든 저장 프로퍼티에 값이 있는지 확인
    • 지정 init은 부모의 init이 같은 동작을 행할 수 있도록 초기화를 양도
    • 부모는 상속 체인을 따라 최상위 클래스에 도달할 때까지 이 작업을 반복
    • 최상위 클래스에 도달했을 때, 모든 저장 프로퍼티에 값이 있다고 확인하면 인스턴스의 메모리가 초기화
  • 2단계
    • 최상위 클래스로부터 최하위 클래스까지 상속 체인을 따라 내려오면서 지정 init들이 인스턴스를 제각각 정의
      • 이 단계에서는 self를 통해 프로퍼티 값을 수정 가능
      • 인스턴스 메서드를 호출 가능
    • 마지막으로 각각의 편의 init을 통해 self. 를 통한 사용자 정의 작업을 진행 가능

 

 

서브스크립트

- 클래스, 구조체, 열거형에는 컬렉션, 리스트, 시퀀스 등에서 접근할 수 있는 서브스크립트를 정의할 수 있다

- 별도의 설정자, 접근자 등의 메서드를 구현하지 않아도 인덱스를 통해 값을 설정하거나 가져올 수 있다

ex) Dictionary에서 dictionary[key] -> 이것이 서브스크립트 다

 

문법

subscript(index: Int) -> Int{
	get{
    	//getter
    }
    set(newValue){
    	//setter
    }
}

 

구현

struct Student{
	//...
}

class School{
	var students: [Student] = []
    
    func addStudent()...
    ...
    
    //School Class 내부에 있는 값들 혹은 그 값들의 조합으로도 반환 가능
    subscript(index: Int) -> Student?{
    	if index < self.number{
        	return self.students[index]
        }
        return nil
    }
}

 

복수개의 서브스크립트

- 하나의 타입이 여러개의 서브스크립트를 가징 수 있다

- 매개변수 타입과 개수, 반환 타입이 다르면 가능하다

- 서브스크립트에도 다형성이 가능

 

타입 서브스크립트

- subscript 앞에 static/class 키워드를 붙여준다

 

 

모나드

- 스위프트는 함수형 프로그래밍 패러다임에서 파생된 기능이나 개념이 종종 등장한다.

- 단순히 고차함수를 사용, 함수를 일급 객체로 사용, 재귀함수를 사용한 로직을 구현하는 등의 특정 기능에 국한되는 것은 아니지만, 모나드를 익혀두면 더 깊이 있는 함수형 프로그래밍을 이해할 수 있다

 

모나드의 조건

  • 타입을 인자로 받는 타입 (특정 타입의 값을 포장)
  • 특정 타입의 값을 포장한 것을 반환하는 함수(메서드)가 존재
  • 포장된 값을 변환하여 같은 형태로 포장하는 함수(메서드)가 존재
  • 옵셔널 = 기본적인 모나드

FlatMap vs Map

let optionals: [Int?] = [1,2,nil,5]

let mapped: [Int?] = optionals.map{ $0 } // [Optional(1),Optional(2),nil,Optional(5)]
let compactMapped: [Int] = optionals.compactMap{ $0 } // compactMap = flatMap
//[1,2,5]

//compactMap 은 컨테이너 내의 컨테이너까지 벗기는? 것을 볼 수 있다.

맵, 필터, 리듀스

 

Map

  • 자신을 호출할 때, 매개변수로 전달된 함수를 실행하여 그 결과를 반환
  • Sequence, Collection 프로토콜을 따르거나 옵셔널은 모두 사용 가능
  • 사용 시, 컨테이너가 담고 있던 각각의 값을 매개변수를 통해 적용 후, 다시 컨테이너로 포장하여 반환
  • 이전에 알아본 for - in 구문과 크게 다르지 않다
  • B.U.T. 코드 재사용 측면, 컴파일러 최적화 측면에서 성능 차이 있다
  • 다중 쓰레드 환경에서도 예측지 못한 결과의 부작용을 방지 가능 -> (Thread-Safe ? )
let numbers = [1,2,3,4,5]
var doubledNum = []
var stringNum = []

//for - in
for number in numbers{
	doubledNum.append(number * 2)
    stringNum.append("\(number)")
}

//map
//클로저 간소화한 문법
doubledNum = numbers.map{ $0 * 2 }
stringNum = numbers.map{ "\($0)" }

 

Filter

  • 내부의 값을 걸러서 추출하는 고차함수
  • 맵과 마찬가지로 새로운 컨테이너에 값을 담아 반환
  • Map = 변형, Filter = 거르기
  • 반환 타입 = Bool
let numbers = [1,2,3,4,5]

//짝수 걸러내기
ler evenNumbers = numbers.filter{ $0 % 2 == 0 } // [2,4]

 

Reduce

  • 컨테이너 내부의 콘텐츠를 하나로 합하는 기능을 실행하는 고차함수
  • 두가지 형태로 구현됨
    • 1. 클로저가 각 요소를 전달받아 연산한 후 값을 다음 클로저 실행을 위해 반환하며 컨테이너를 순환
    • 2. 컨테이너를 순환하며 클로저가 실행되지만 클로저가 따로 결괏값을 반환하지 않은 형태
      • 대신 inout 매개변수를 사용하여 초깃값에 직접 연산을 실행함
let numbers = [1,2,3]

// 1번 형태 
//초깃값이 0이고 모든 값을 더한다
var sum: Int = numbers.reduce(0,{ (result: Int, next: Int) -> Int in
	print("\(result) + \(next)")
    // 0 + 1
    // 1 + 2
    // 3 + 3
    return result + next
})

// 2번 형태
//초깃값이 0이고 모든 값을 더한다
sum = numbers.reduce(into: 0,{ (result: inout Int, next: Int) in
	print("\(result) + \(next)")
    // 0 + 1
    // 1 + 2
    // 3 + 3
	result += next
})

+ Recent posts