[ 참고이 질문은 원래 Swift 2.2에서 공식화되었습니다. 두 가지 중요한 언어 변경을 포함하여 Swift 4에서 수정되었습니다. 첫 번째 메소드 매개 변수 외부는 더 이상 자동으로 억제되지 않으며 선택기는 Objective-C에 명시 적으로 노출되어야합니다.]
내 수업에 다음 두 가지 메서드가 있다고 가정 해 보겠습니다.
@objc func test() {}
@objc func test(_ sender:AnyObject?) {}
이제 Swift 2.2의 새로운 #selector
구문을 사용하여 첫 번째 방법 인 func test()
. 어떻게하나요? 내가 이것을 시도 할 때 :
let selector = #selector(test) // error
… “의 모호한 사용”오류가 발생 test()
합니다. 하지만 이렇게 말하면 :
let selector = #selector(test(_:)) // ok, but...
… 오류가 도망 간다, 그러나 나는 지금 말하는 겁니다 잘못된 방법 의 하나 와 매개 변수. 매개 변수가 없는 것을 참조하고 싶습니다 . 어떻게하나요?
[참고 : 예제는 인위적이지 않습니다. NSObject에는 Objective-C copy
및 copy:
인스턴스 메서드 인 Swift copy()
및 copy(sender:AnyObject?)
; 실생활에서 쉽게 문제가 발생할 수 있습니다.]
답변
[ 참고이 답변은 원래 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) // or: 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
인스턴스 메소드가 아닌 클래스 메소드 라고 말한 것처럼 보이지만 그럼에도 불구하고 선택기로 올바르게 해석 될 것입니다. 그게 전부입니다.)
답변
누락 된 명확성을 추가하고 싶습니다. 클래스 외부에서 인스턴스 메서드에 액세스합니다.
class Foo {
@objc func test() {}
@objc func test(_ sender: AnyObject?) {}
}
의 전체 서명 클래스 ‘의 관점에서 test()
방법은 (Foo) -> () -> Void
당신이를 얻기 위해 지정해야합니다 Selector
.
#selector(Foo.test as (Foo) -> () -> Void)
#selector(Foo.test(_:))
또는 Selector
원래 답변에 표시된대로 인스턴스의를 참조 할 수 있습니다 .
let foo = Foo()
#selector(foo.test as () -> Void)
#selector(foo.test(_:))
답변
제 경우 (Xcode 11.3.1) 오류는 디버깅하는 동안 lldb를 사용할 때만 발생했습니다. 실행할 때 제대로 작동합니다.