제네릭 타입을 사용하는 오버라이드 메소드 변경 사항(Swift 5.2)

2 답변 글타래를 보이고 있습니다
  • 글쓴이
    • i참
      참가자
      • 글작성 : 1
      • 답글작성 : 10

      공통으로 사용하는 UI 컴포넌트들은 아래와 같이 템플릿을 만들어 사용하곤 했는데요. 어제 갑자기 bind 메소드를 오버라이드 하는 부분에서 컴파일 에러가 발생했습니다(Xcode 11.4 beta). 분명 멀쩡히 빌드가 되었었는데 말이죠(Xcode 11.3.1).

      import UIKit
      class BaseTableViewCell: UITableViewCell {
          override func prepareForReuse() {
              self.gestureRecognizers?.removeAll()
          }
          override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
              super.init(style: style, reuseIdentifier: reuseIdentifier)
              selectionStyle = .none
              setupView()
          }
          required init?(coder: NSCoder) {
              fatalError("init(coder:) has not been implemented")
          }
          func bind<T>(content: T) {}
          func setupView() {}
      }
      
      class EventMapCell: BaseTableViewCell {
      //❗️Overridden method 'bind' has generic signature <T where T : EventMapCell.Content> Which is incompatible with base method's generic signature <T>; expected generic signature to be <T>
          override func bind<T: Content>(content: T) {
          ...
          }
      }
      extension EventMapCell {
          strcut Content {
              ...
          }
      }
      

      컴파일러의 버그인 줄 알았지만 아니었습니다. Swift 5.2에서 변경되는 부분이었습니다(확인을 도와준 라이노에게 감사감사).

      • Xcode 11.4 Beta 3 Release Notes
        A method override can no longer have a generic signature with requirements not imposed by the base method. For example, the below code produces an error. (23626260) (FB5382462)
      protocol P {}
      class Base {
          func foo<T>(arg: T) {}
      }
      class Derived: Base {
          // generates an error because of the added requirement
          override func foo<T: P>(arg: T) {}
      }
      

      처음엔 ‘이것도 되나? 되는구나!’ 하고 넘어갔는데 말입니다. 사실은 위험한 코드였습니다. 스위프트 포럼에서 간단한 예시를 가져왔습니다.

      1. class A는 프로토콜 P와 Q를 준수하는 타입 T를 인자로 사용하는 메소드 foo를 가집니다.
      2. class B는 A를 상속받고, 메소드 foo를 오버라이드하여 프로토콜 P, Q, R을 모두 준수하도록 하고 있습니다.
      3. 프로토콜 P와 Q를 준수하는 객체 C를, A타입의 객체 B의 foo 메소드 인자로 넘겨 실행하면 class A가 R을 채택하지 않았기 때문에 런타임에 BAD ACCESS에러가 발생합니다.
      public protocol P {}
      public protocol Q {}
      public protocol R {
        func r()
      }
      //1
      open class A {
        open func foo<T: P & Q>(arg: T) {}
      }
      //2
      open class B: A {
        open override func foo<T: P & Q & R>(arg: T) {
          arg.r()
        }
      }
      //3
      class C: P, Q {}
      let a: A = B()
      a.foo(arg: C())
      

      컴파일 타임에 타입 체크가 다 되는 줄 알았는데 버그였습니다. 리스코프 치환 원칙 위반이기도 하구요. 더 쓰려했는데 퇴근시간이 다가와서 이만 줄입니다. 즐거운 한 주 보내세요??

      참고

    • 야곰
      키 마스터
      • 글작성 : 37
      • 답글작성 : 580

      오우! 좋은정보 고급진 정보 고맙습니다!
      후딱 신나게 퇴근하시길 ㅎㅎ

      • i참
        참가자
        • 글작성 : 1
        • 답글작성 : 10

        읽어주셔서 감사합니다!

    • nobleidea
      참가자
      • 글작성 : 5
      • 답글작성 : 8

      런타임에 에러나니 컴파일 때 막아두었다라고 명쾌하게 알려주셔서 감사합니다! 고급정보 감사합니당!

      • i참
        참가자
        • 글작성 : 1
        • 답글작성 : 10

        댓글로 간단히 정리해주셔서 감사합니다!

2 답변 글타래를 보이고 있습니다
  • 답변은 로그인 후 가능합니다.

logo landscape small

사업자번호 : 743-81-02195
통신판매업 신고번호 : 제 2022-충북청주-1278 호
고객센터 : 카카오톡채널 @yagom