[ 참고이 답변은 원래 Swift 2.2에서 공식화되었습니다. 두 가지 중요한 언어 변경을 포함하는 Swift 4 용으로 수정되었습니다. 첫 번째 메소드 매개 변수 외부는 더 이상 자동으로 억제되지 않으며 선택기는 Objective-C에 명시 적으로 노출되어야합니다.]
함수 참조를 올바른 메서드 서명 으로 캐스팅 하여이 문제를 해결할 수 있습니다 .
let selector = #selector(test as () -> Void)
(하지만 제 생각에는 이렇게 할 필요가 없습니다. 저는이 상황을 버그로 간주하여 함수를 참조하는 Swift의 구문이 부적절하다는 것을 보여줍니다. 버그 보고서를 제출했지만 소용이 없습니다.)
새 #selector
구문 을 요약하면 다음 과 같습니다.
이 구문의 목적은 선택자를 리터럴 문자열로 제공 할 때 발생할 수있는 너무 흔한 런타임 충돌 (일반적으로 "인식되지 않은 선택자")을 방지하는 것입니다. 함수 참조를#selector()
취하고 컴파일러는 함수가 실제로 존재하는지 확인하고 Objective-C 선택기에 대한 참조를 해결합니다. 따라서 쉽게 실수 할 수 없습니다.
( 편집 : 예, 할 수 있습니다. 완전한 lunkhead가 될 수 있으며에서 지정한 작업 메시지를 구현하지 않는 인스턴스로 대상을 설정할 수 있습니다 #selector
. 컴파일러는 당신을 멈추지 않을 것이고 당신은 좋은 옛날. 한숨 ...)
함수 참조는 다음 세 가지 형식 중 하나로 나타날 수 있습니다.
베어 이름 함수의. 함수가 모호하지 않으면 충분합니다. 따라서 예를 들면 다음과 같습니다.
@objc func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test)
}
test
메서드 는 하나뿐 이므로 #selector
매개 변수를 사용하고 #selector
매개 변수를 언급하지 않더라도 참조합니다. 배후에서 해결 된 Objective-C 선택기는 여전히 올바르게 "test:"
(콜론을 사용하여 매개 변수를 나타냄) 그대로입니다.
나머지 시그니처 와 함께 함수의 이름 . 예를 들면 :
func test() {}
func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test(_:))
}
두 가지 test
방법이 있으므로 차별화해야합니다. 표기법 은 매개 변수 test(_:)
가있는 두 번째 표기법으로 해석됩니다 .
나머지 시그니처가 있거나없는 함수 이름과 매개 변수 유형 을 표시 하는 캐스트 . 그러므로:
@objc func test(_ integer:Int) {}
@nonobjc func test(_ string:String) {}
func makeSelector() {
let selector1 = #selector(test as (Int) -> Void)
let selector2 = #selector(test(_:) as (Int) -> Void)
}
여기서 우리는 한 오버로드 test(_:)
. 오브젝티브 C는 오버로드를 허용하지 않기 때문에 과부하 때문에 단지 그들 중 하나가 노출되어, 오브젝티브 C에 노출 될 수 없으며, 우리는 단지 하나에 대한 선택 형성 할 수 있다 셀렉터는 오브젝티브 C의 기능이기 때문에, 노출을 . 그러나 우리는 스위프트에 관한 한 여전히 명확하게 해야합니다 .
(내 의견으로는 오용되는 것은 위의 답변의 기초로 사용되는 언어 적 기능입니다.)
또한 함수가 어떤 클래스에 있는지 알려줌으로써 Swift가 함수 참조를 해결하도록 도와야 할 수도 있습니다.
클래스가이 클래스와 같거나이 클래스의 상위 클래스 체인보다 높은 경우 일반적으로 추가 해결이 필요하지 않습니다 (위의 예에 표시된대로). 선택적 self
으로 점 표기법으로 말할 수 있습니다 (예 : #selector(self.test)
, 일부 상황에서는 그렇게해야 할 수도 있습니다.
그렇지 않으면 이 실제 예제 ( MPMusicPlayerController 임) 에서와 같이 점 표기법과 함께 메서드가 구현 된 인스턴스에 대한 참조를 사용합니다 self.mp
.
let pause = UIBarButtonItem(barButtonSystemItem: .pause,
target: self.mp, action: #selector(self.mp.pause))
... 또는 점 표기법과 함께 클래스 이름을 사용할 수 있습니다 .
class ClassA : NSObject {
@objc func test() {}
}
class ClassB {
func makeSelector() {
let selector = #selector(ClassA.test)
}
}
( test
인스턴스 메소드가 아닌 클래스 메소드 라고 말한 것처럼 보이지만 그럼에도 불구하고 선택기로 올바르게 해석 될 것입니다. 그게 전부입니다.)