大器晩成

[프로토콜] 프로토콜의 확장 (Protocol Extensions) 본문

iOS/Swift 문법

[프로토콜] 프로토콜의 확장 (Protocol Extensions)

zerobugpark 2025. 2. 18. 19:12

프로토콜의 확장 

  • 프로토콜을 채택한 타입에 제공하기 위해서 메서드, 초기화, 서브 스크립트, 연산 프로퍼티들은 확장에서 구현할 수 있습니다.
protocol Bird {
    func fly()     //Withness Table
    func sit()
}

class Eagle: Bird {
    
}

class Sparrow: Bird {
    func fly() {
        print("참새가 날고 있습니다.")
    }

    func hunt() {
 	   print("참새가 사냥 중입니다.")
    }
}
extension Bird {
    func fly() {
        print("새가 날고 있습니다.")
    }
    
    func sit() {
        print("새가 앉아 있습니다.")
    }
     
    func hunt() { //Direct Dispatch
        print("새가 사냥 중입니다.")
    }
}

let eagle = Eagle()
eagle.fly() //새가 날고 있습니다.
eagle.hunt() // 새가 사냥 중입니다.

let sparrow = Sparrow()
sparrow.fly() //참새가 날고 있습니다.
sparrow.hunt() 참새가 사냥 중입니다.
  • 구조체나 클래스에서 직접적으로 구현하지 않더라도, 확장에서 구현한 내용이 구조체나 클래스에서 사용할 수 있습니다.
  • "프로토콜 확장"을 제공해서 메서드의 디폴트 구현(기본 메서드)을 제공합니다. (코드의 중복을 피할 수 있습니다.)

[요구사항의 메서드 우선순위 적용] - Witness Table

  • 1. (채택) 구현시 해당 메서드 2. 기본 메서드 (확장에서 제공하는 메서드)

[요구사항 메서드의 없을 경우] - Direct Dispath

  • 타입이 프로토콜 타입이면, 프로토콜에 정의된 함수를 사용
    (프로토콜 / Direct)
  • 타입이 프로토콜 타입이 아니라면, 자기 클래스 또는 구조체 내의 정된 함수 사용
    (구조체 / Direct, 클래스 Virtual table)

 

프로토콜의 확장을 통한 다영성 제공

  • 클래스
protocol Bird {
    func fly()
    func sit()
}


class Sparrow: Bird {
    func fly() {
        print("참새가 날고있습니다.")
    }
    
    func hunt() {
        print("참새가 사냥 중입니다.")
    }
}
extension Bird {
    func fly() {
        print("새가 날고있습니다.")
    }
    
    func sit() {
        print("새가 앉아있습니다.")
    }
    
    func hunt() {
        print("새가 사냥중입니다.")
    }
}

[Class Virtual 테이블] 
- func fly // 참새가 날고 있습니다.
- func sit // 새가 앉아 있습니다. 
- func hunt // 참새가 사냥 중입니다.
Sparrow
let sparrow: Sparrow = Sparrow()
sparrow.fly() // V테이블 
sparrow.sit() // V테이블
sparrow.hunt() // V테이블


[Protocol Witness 테이블] 
- func fly // 참새가 날고 있습니다.
- func sit // 새가 앉아 있습니다. 

let sparrow2: Bird = Sparrow()
sparrow2.fly()  // 프로토콜 W 테이블
sparrow2.sit()  // 프로토콜 W 테이블
sparrow2.hunt() // Direct Dispatch (주소를 직접 전달)

 

  • 구조체
protocol Bird {
    func fly()
    func sit()
}


struct Eagle: Bird {
    func fly() {
        print("독수리가 날고있습니다.")
    }
    
    func hunt() {
        print("독수리가 사냥 중입니다.")
    }
}
extension Bird {
    func fly() {
        print("새가 날고있습니다.")
    }
    
    func sit() {
        print("새가 앉아있습니다.")
    }
    
    func hunt() {
        print("새가 사냥중입니다.")
    }
}

// 구조체는 메서드 테이블이 없음

let eagle: Eagle = Eagle()
eagle.fly() // (Direct) 직접 메모리 주소 삽입
eagle.sit() // (Direct) 직접 메모리 주소 삽입
eagle.hunt() // (Direct) 직접 메모리 주소 삽입
//독수리가 날고있습니다.
//새가 앉아있습니다.
//독수리가 사냥 중입니다.

let eagle2: Bird = Eagle()
eagle2.fly() // 프로토콜 - W테이블
eagle2.sit() // 프로토콜 - W테이블
eagle2.hunt() // (Direct) 직접 메모리 주소 삽입
//독수리가 날고있습니다.
//새가 앉아있습니다.
//새가 사냥중입니다.
  • 프로토콜을 채택한 구조체의 경우, 크기에 따라 Stack 또는 Heap에 저장됩니다.
  • String의 경우 구조체여서 Stack에 저장되지만, 엄청 문자열이 길 경우 Heap에 저장될 수도 있습니다.

 

프로토콜의 확장의 적용 제한 

  • 프로토콜 확장에서 where절을 통해 프로토콜의 확장의 적용을 제한할 수 있습니다.
  • "특정 프로토콜"을 채택한 타입에만 프로토콜 확장이 적용되도록 제한
    where Self: 특정 프로토콜 (Self: 타입 자체를 의미)
  • 특정 프로토콜을 채택하지 않으면, 프로토콜 확장이 적용되지 않습니다.
protocol Bird {
    func fly()
    func sit()
}

protocol Animals {
    func eat()
    func sleep()
}


extension Animals where Self: Bird {
    func eat() {
        print("밥을 먹습니다.")
    }
    func sleep() {
        print("잠을 잡니다")
    }
}

//Bird를 채택하지 않으면, eat과 sleep 메서드는 직접 구현해야 합니다.
class Eagle: Animals, Bird {
    func sit() {
        print("독수리가 앉았습니다.")
        
    }
    
    func fly() {
        print("독수리가 날고있습니다.")
    }
}
  

class Eagle2: Animals {
    func eat() {
        
    }
    
    func sleep() {
        
    }
    
    func sit() {
        print("독수리가 앉았습니다.")
        
    }
    
    func fly() {
        print("독수리가 날고있습니다.")
    }
}

 

728x90