[swift] ‘@objc’가 아닌 방법은 ‘@objc’프로토콜의 선택적 요구 사항을 충족하지 않습니다.

개요 :

  • Objective-C 옵션 기능 중 하나의 기본 구현을 제공하는 프로토콜 P1이 있습니다.
  • 선택적 기능의 기본 구현을 제공하면 경고가 있습니다.

컴파일러 경고 :

Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'

버전:

  • 스위프트 : 3
  • Xcode : 8 (공개 릴리스)

시도한 횟수 :

  • 추가를 시도 @objc했지만 도움이되지 않음

질문:

  • 이 문제를 어떻게 해결합니까?
  • 해결 방법이 있습니까?

암호:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {

}

extension P1 where Self : UIViewController {

    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return UIViewController()
    }
}


class A : UIViewController, P1 {

}



답변

나는 당신의 질문에 대답 할 수 있다고 생각하지만 당신이 좋아할 대답은 아닙니다.

요약 : @objc 기능은 현재 프로토콜 확장에 없을 수 있습니다. 이상적인 솔루션은 아니지만 대신 기본 클래스를 만들 수 있습니다.

프로토콜 확장 및 Objective-C

첫째,이 질문 / 답변 ( Can Swift Method Defined on Extensions on Extensions on Protocols Accessed in Objective-c )은 프로토콜 확장이 내부적으로 전달되는 방식 때문에 프로토콜 확장에서 선언 된 메서드가 objc_msgSend()함수에 표시되지 않음을 시사하는 것 같습니다. 따라서 Objective-C 코드에는 표시되지 않습니다. 확장 프로그램에서 정의하려는 방법은 Objective-C에서 볼 수 있어야하기 때문에 ( UIKit사용할 수 있으므로)를 포함하지 않은 것에 대해 소리를 지르지 @objc만 일단 포함하면 @objc허용되지 않기 때문에 소리를 지 릅니다 . 프로토콜 확장. 이는 프로토콜 확장이 현재 Objective-C에 표시되지 않기 때문일 수 있습니다.

@objc“@objc는 클래스의 멤버, @objc 프로토콜 및 클래스의 구체적인 확장에만 사용할 수 있습니다.”라는 상태 를 추가하면 오류 메시지가 표시 됩니다. 이것은 수업이 아닙니다. @objc 프로토콜에 대한 확장은 프로토콜 정의 자체 (예 : 요구 사항)에있는 것과 동일하지 않으며 “concrete”라는 단어는 프로토콜 확장이 구체적인 클래스 확장으로 간주되지 않음을 암시합니다.

해결 방법

불행히도 이것은 기본 구현이 Objective-C 프레임 워크에 표시되어야 할 때 프로토콜 확장을 사용하는 것을 거의 완전히 방지합니다. 처음 @objc에는 Swift Compiler가 형식을 준수하는 클래스가 클래스라는 것을 보장 할 수 없기 때문에 프로토콜 확장에서 허용되지 않는다고 생각했습니다 UIViewController. 그래서 내가 넣어 class에 요구 사항을 P1. 이것은 작동하지 않았습니다.

아마도 유일한 해결 방법은 여기에서 프로토콜 대신 기본 클래스를 사용하는 것입니다.하지만 클래스가 단일 기본 클래스 만 가질 수 있지만 여러 프로토콜을 준수 할 수 있기 때문에 이것은 분명히 완전히 이상적이지는 않습니다.

이 경로를 선택하는 경우이 질문 ( Swift 3 ObjC Optional Protocol Method Not Called in Subclass )을 고려하십시오. Swift 3의 또 다른 현재 문제는 하위 클래스가 수퍼 클래스의 선택적 프로토콜 요구 사항 구현을 자동으로 상속하지 않는다는 것입니다. 그 질문에 대한 대답 @objc은 그것을 우회하기 위해 특별한 적응을 사용 합니다.

문제보고

Swift 오픈 소스 프로젝트에서 일하는 사람들 사이에서 이미 논의되고 있다고 생각하지만 , 결국 Swift Core Team으로 갈 가능성이있는 Apple의 Bug Reporter 또는 Swift의 버그 리포터 를 사용하여 알고 있는지 확인할 수 있습니다 . 그러나 이들 중 하나는 버그가 너무 광범위하거나 이미 알려져 있음을 알 수 있습니다. Swift 팀은 당신이 찾고있는 것을 새로운 언어 기능으로 고려할 수도 있습니다.이 경우 먼저 메일 링리스트를 확인해야 합니다 .

최신 정보

2016 년 12 월이 문제 Swift 커뮤니티 에보고 되었습니다. 이 문제는 여전히 중간 우선 순위로 진행 중으로 표시되지만 다음과 같은 댓글이 추가되었습니다.

이것은 의도 된 것입니다. 프로토콜 준수 후에 확장이 추가 될 수 있기 때문에 모든 채택 자에게 메소드 구현을 추가 할 방법이 없습니다. 하지만 확장이 프로토콜과 동일한 모듈에 있으면 허용 할 수 있다고 생각합니다.

그러나 프로토콜이 확장과 동일한 모듈에 있기 때문에 향후 Swift 버전에서이 작업을 수행 할 수 있습니다.

업데이트 2

2017 년 2 월,이 문제 다음 메시지와 함께 Swift Core Team 멤버 중 한 명이 “Wo n’t Do” 로 공식적으로 종결 되었습니다.

이는 의도적입니다. 프로토콜 확장은 Objective-C 런타임의 제한으로 인해 @objc 진입 점을 도입 할 수 없습니다. @objc 진입 점을 NSObject에 추가하려면 NSObject를 확장합니다.

확장 NSObject하거나 심지어 UIViewController원하는 것을 정확하게 달성하지 못하지만 불행히도 가능해 보이지는 않습니다.

(매우) 장기적인 미래에 우리는 @objc메소드에 대한 의존도를 완전히 없앨 수 있을지 모르지만 Cocoa 프레임 워크가 현재 Swift로 작성되지 않았기 때문에 조만간 오지 않을 것입니다 (안정적인 ABI를 갖기 전까지는 불가능합니다). .

업데이트 3

2019 년 가을부터 점점 더 많은 Apple 프레임 워크가 Swift로 작성되고 있기 때문에 이것은 문제가되지 않습니다. 예를 들어, SwiftUI대신 을 사용 하면 메서드를 참조 할 때 필요하지 UIKit않으므로 문제를 완전히 피할 @objc수 있습니다 SwiftUI.

Swift로 작성된 Apple 프레임 워크는 다음과 같습니다.

  • SwiftUI
  • RealityKit
  • 결합시키다
  • CryptoKit

Swift가 공식적으로 Swift 5.0 및 5.1에서 각각 ABI 및 모듈 안정이므로이 패턴이 시간이 지남에 따라 계속 될 것으로 예상 할 수 있습니다.


답변

내가 사용하는 신속한 프레임 워크에서 ‘모듈 안정성'( ‘배포 용 라이브러리 빌드’설정)을 활성화 한 후이 문제를 만났습니다.

내가 가진 것은 다음과 같습니다.

class AwesomeClass: LessAwesomeClass {
...
}

extension AwesomeClass: GreatDelegate {
  func niceDelegateFunc() {
  }
}

확장 기능에 다음 오류가 있습니다.

  • ‘LessAwesomeClass’하위 클래스 확장의 ‘@objc’인스턴스 메서드에는 iOS 13.0.0이 필요합니다.

  • ‘@objc’가 아닌 메서드 ‘niceDelegateFunc’는 ‘@objc’프로토콜 ‘GreatDelegate’의 요구 사항을 충족하지 않습니다.

확장이 아닌 클래스로 함수를 이동하면 문제가 해결되었습니다.


답변

또 다른 해결 방법이 있습니다. 이 문제도 발생했으며 아직 UIKit에서 SwiftUI로 전환 할 수 없습니다. 기본 구현을 공통 기본 클래스로 이동하는 것은 나에게도 옵션이 아닙니다. 내 기본 구현은 상당히 광범위했기 때문에 모든 코드를 복제하고 싶지 않았습니다. 내가 사용한 해결 방법은 프로토콜에서 래퍼 함수를 ​​사용한 다음 각 클래스에서 해당 함수를 호출하는 것입니다. 예쁘지는 않지만 상황에 따라 대안보다 나을 수 있습니다. 코드는 다음과 같습니다.

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}

extension P1 where Self : UIViewController {
    func wrapPresentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return UIViewController()
    }
}

class A : UIViewController, P1 {
    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return wrapPresentationController(controller, viewControllerForAdaptivePresentationStyle: style)
    }
}


답변