ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift4: Protocol Oriented Programming - 2.타입 선택
    POP 2021. 2. 14. 03:19

    2장 타입 선택

    타입

    Swift에서 타입은 이름 있는 타입복합 타입으로 나뉜다.

    이름 있는 타입은 클래스, 구조체, 열거형, 프로토콜 등이 있으며, 원시 타입이라 불리는 스위프트 표준 데이터 타입들은 모두 이름 있는 타입이다.

    복합 타입은 타입을 정의할 때 이름을 부여하지 않은 타입을 말하며, 함수 타입과 튜플 등이 있다. typealias 를 이용하여 복합 타입에 별칭을 붙여 호출할 수도 있다.

    또한, 참조 타입값 타입으로도 타입을 나눌 수 있다.

    참조 타입 인스턴스를 전달하는 것은 원본 인스턴스의 참조를 전달하는 것으로, 두 참조가 같은 인스턴스를 공유한다는 뜻이다. : 클래스

    값 타입 인스턴스를 전달할 때에는 인스턴스의 새로운 복사본을 전달하며, 각 인스턴스가 유일한 사본의 의미를 가진다. : 구조체, 열거형, 튜플

    프로토콜은 인스턴스를 생성할 수 없으므로 참조 타입이나 값 타입이 될 수 없다.

    클래스

    클래스는 객체의 프로퍼티, 메소드, 생성자을 단일 타입으로 캡슐화하는 일종의 구성체. 클래스의 인스턴스를 일반적으로 객체라고 한다.

    구조체

    스위프트에서 가장 중요한 타입‼️ 인스턴스의 프로퍼티, 메소드, 생성자를 단일 타입으로 캡슐화하는 하나의 구성체. 구조체에서는 개발자가 생성자를 만들어주지 않아도 모든 프로퍼티를 초기화하기 위한 기본 생성자를 만들어준다. 기본 생성자는 모든 non-optional 프로퍼티의 초기값을 요구.

    구조체는 값 타입이기 때문에 기본적인 메소드에서 인스턴스의 프로퍼티 값을 변경하지 못한다. 따라서, 구조체 내부의 프로퍼티 값의 변경을 위해서는 mutating 키워드를 메소드 앞에 꼭 써주어야 한다.

    접근 제어

    open

    해당 아이템이 정의된 모듈의 어느 곳에서나 서브클래싱, 오버라이딩 및 접근이 가능. 해당 모듈을 임포트한 다른 모듈에서도 같은 동작이 가능하다.

    public

    해당 아이템이 정의된 모듈을 임포트한 다른 모듈에서는 서브클래싱과 오버라이딩이 불가하다는 점 빼고는 open이랑 같음.

    internal

    default 접근 제어. 정의된 모듈 내부에서 프로퍼티, 메소드, 클래스를 사용 가능하지만, 프레임워크 외부의 코드에서는 접근 불가.

    fileprivate

    아이템이 정의되어있는 소스 파일 내부(파일 단위)에서만 접근 가능.

    private

    가장 폐쇄적인 접근 단위로, 해당 아이템을 정의한 정의부 소스 코드 내에서만 접근이 가능하다.

     

    어떠한 엔티티가 본인보다 더 제한적인 접근 제한을 가진 엔티티에 의존할 경우 본인을 해당 엔티티보다 덜 제한적이게 만들 수는 없다.

     

    열거형

    스위프트의 열거형이 다른 언어의 열거형과 다른 이유 중 하나는 raw values로 값을 할당할 수 있기 때문. 또한, 클래스나 구조체처럼 연산 프로퍼티나 생성자 또는 메소드를 가질 수 있다.

    각 case에 연관 값을 저장할 수 있다.

    enum Devices2 {
        case IPod(model: Int, year: Int, memory: Int)
        case IPhone(model: String, memory: Int)
        case IPad(model: String, memory: Int)
    }
    
    var myPhone = Devices2.IPhone(model: "6", memory: 64)
    var myTablet = Devices2.IPad(model: "Pro", memory: 128)
    
    switch myPhone {
    case .IPod(let model, let _, let memory):
        print("iPod: \(model) \(memory)")
    case .IPhone(let model, let memory):
        print("iPhone: \(model) \(memory)")
    case .IPad(let model, let memory):
        print("iPad \(model) \(memory)")
    }

     

     

    연관 값 검색 용이하게 하기

    extension BookFormat {
        var pageCount: Int {
            switch self {
            case .PaperBack(pageCount: let pageCount, _):
                return pageCount
            case .HardCover(pageCount: let pageCount, _):
                return pageCount
            case .PDF(pageCount: let pageCount, _):
                return pageCount
            case .EPub(pageCount: let pageCount, _):
                return pageCount
            case .Kindle(pageCount: let pageCount, _):
                return pageCount
            }
        }
        
        var price: Double {
            switch self {
            case .PaperBack(_, price: let price):
                return price
            case .HardCover(_, price: let price):
                return price
            case .PDF(_, price: let price):
                return price
            case .EPub(_, price: let price):
                return price
            case .Kindle(_, price: let price):
                return price
            }
        }
    }
    
    print("\(paperBack.pageCount) - \(paperBack.price)")

     

    튜플

    유한하며, 쉼표로 구분하는 순서 있는 요소의 목록. 값 타입.

     

    프로토콜

    프로토콜도 타입이다.

     

    값 타입과 참조 타입

    인스턴스가 전달되는 방식의 차이에 따라 나뉜다. 값 타입은 원본의 복사본이 전달되기 때문에 원본 인스턴스의 변경이 전달된 복사본에 영향을 미치지 않는다. 참조 타입은 원본 인스턴스의 참조를 전달하여, 하나의 참조가 변경되면 같은 것을 가리키는 다른 참조에게도 그 영향이 미친다.

    값 타입은 예상치 못한 변화로부터 인스턴스를 보호해준다. 값 타입에서 원본 값을 변경하고 싶을 때에는 inout 키워드를 이용한다.

     

    참조 타입 - 재귀적 데이터 타입

    재귀적 데이터 타입: 같은 타입의 다른 값을 프로퍼티로 같는 타입

    동적 자료 구조: 런타임에서 요구 사항에 따라 크기가 늘어나거나 줄어든다. ex. 리스트, 트리

    struct LinkedListValueType {
        var value: String
        var next: LinkedListValueType? // error 
    }

    위 코드와 같이 재귀적 값 타입은 허용되지 않는다.

     

    참조 타입 - 상속

    클래스의 계층 구조를 남발(?)할 경우에는 복잡성이 증가하여, 변경 사항이 어떻게 영향을 미칠지 파악하기 어렵다. 스위프트에서는 이러한 복잡성을 프로토콜 지향 접근법으로 해결한다.

     

    다이내믹 디스패치

    클래스의 계층 구조가 있을 경우, 구현체의 함수 등을 사용할 때 어떤 클래스에서 구현된 함수인지 선택하는 과정런타임에 수행되며, 이를 다이내믹 디스패치라고 한다.

    따라서 런타임 오버헤드의 일정 부분은 참조 타입의 상속과 관련이 있다는 것! 이를 줄이기 위해 클래스나 메소드 등에 final 키워드로 서브클래싱 불가, 오버라이딩 불가의 의미를 나타낼 수 있다.

    그러나 이런 오버헤드를 피하기 위해서는 그냥 값 타입을 갖는 프로토콜 지향 설계를 하는 편이 더 낫다.

     

    스위프트 내장 타입

    스위프트의 내장 데이터 타입은 대부분 구조체로 구현되어 있으며, 확장할 수 있다. (서브클래싱은 X)

    배열, 딕셔너리, 셋 등의 자료 구조도 구조체로 구현돼 있다.

     

    Copy-on-write(COW)

    값 타입은 다른 변수에 저장될 때 원본의 복사본을 전달한다. 만약, 5만개의 요소를 가진 배열을 다른 변수에 할당하면, 5만개의 요소 전부가 복사되어야 한다 → 결론은 아님.

    스위프트에서는 Copy-on-write를 사용하여 자료 구조가 변경되지 않는 한 새로운 복사본을 만들지 않는다. 그러나 모든 값 타입에 사용되지는 않고, 스위프트 표준 라이브러리에서 특정 타입에만 구현되어 있다.

     

     

    코드

    https://github.com/A-by-alimelon/Swift-ProtocolOrientedProgramming/blob/main/ProtocolOrientedProgramming/ProtocolOrientedProgramming/Type.swift

     

    A-by-alimelon/Swift-ProtocolOrientedProgramming

    Contribute to A-by-alimelon/Swift-ProtocolOrientedProgramming development by creating an account on GitHub.

    github.com

     

    참고 문헌

    https://book.naver.com/bookdb/book_detail.nhn?bid=14107671

     

    스위프트 4

    스위프트가 해마다 발전하면서 프로토콜지향 프로그래밍 역시 계속해서 변화하고 있다. 스위프트 2에서 처음으로 언급된 프로토콜지향 프로그래밍은 스위프트가 3, 4로 업데이트 되면서 클래스

    book.naver.com

     

    댓글

Designed by Tistory.