Swift 4에서 #selector ()를 사용하여 @objc 추론 폐기를 처리하는 방법은 무엇입니까?


131

프로젝트의 소스 코드를 Swift 3에서 Swift 4로 변환하려고합니다. Xcode가 알려주는 경고 중 하나는 선택기에 관한 것입니다.

예를 들어 다음과 같이 일반 선택기를 사용하여 버튼에 대상을 추가합니다.

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

이것이 나타내는 경고입니다.

'#selector'의 인수는 Swift 4에서 사용되지 않는 '@objc'속성 유추에 의존하는 'ViewController'의 인스턴스 메소드 'myAction ()'을 나타냅니다.

이 인스턴스 메소드를 Objective-C에 노출 시키려면 '@objc'를 추가하십시오.

이제 Fix오류 메시지를 누르면 내 기능이 수행됩니다.

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

@objc마크 를 포함하기 위해 모든 기능의 이름을 바꾸고 싶지 않으며 필요하지 않다고 가정합니다.

지원 중단을 처리하기 위해 선택기를 다시 작성하려면 어떻게해야하나요?


관련 질문 :


3
아니, 그들을 마킹 등 @objc 이다 의 Obj-C에 노출, 따라서 선택기로 사용하기 위해 지금 필요.
Hamish 2016

3
더 이상 사용되지 않는 부분은 공용 액세스 기능을 @objc? 그것은 약간 성가신 일이지만, 나는 일반적 으로이 기능을 비공개로 설정하여 @objc어쨌든 그것을 표시해야합니다 .
코너

2
변경 사항에 대한 자세한 내용은 SE-0160 을 참조하십시오 . 또 다른 대안은 @objcMembers모든 Obj-C 호환 멤버를 Obj-C에 노출시키기 위해 주어진 클래스 를 표시하는 것이지만 실제로 전체 클래스를 노출 해야하는 경우가 아니라면 조언하지 않습니다.
Hamish 2016

3
@LinusGeffarth 제안에서 언급했듯이, 바이너리의 크기를 불필요하게 늘리고 동적 연결은 더 오래 걸릴 것입니다. Obj-C에서 특정 항목을 사용하기 위해 특별히 의미를 추가하는 것이 너무 번거롭지 않다고 생각합니다.
Hamish 2016

1
작동하지 않았습니다.
LinusGeffarth

답변:


156

해결 방법은 정확합니다. Objective-C에 노출되는 방법을 만들기 위해 선택할 수있는 선택기에 대해 아무것도 없습니다.

이 경고의 첫 번째 이유는 SE-0160 의 결과입니다 . Swift 4 이전에는 상속 클래스 internal의 Objective-C 호환 상위 멤버 가 Objective-C에 노출 된 NSObject것으로 추론 @objc되었으므로 선택기를 사용하여 호출 할 수있었습니다 (Obj-C 런타임이 메소드를 검색하기 위해 필요하므로) 주어진 선택기에 대한 구현).

그러나 Swift 4에서는 더 이상 그렇지 않습니다. @objc예를 들어, @objc메소드의 오버라이드 , @objc프로토콜 요구 사항의 구현 및와 @objc같은 속성을 갖는 선언과 같이 매우 구체적인 선언 만이 유추 됩니다 @IBOutlet.

위의 링크 된 제안에 자세히 설명 된 바와 같이 이것의 배후의 동기 는 우선 NSObject동일한 선택기가 있기 때문에 상속 클래스의 메서드 오버로드가 서로 충돌 하는 것을 방지하는 것입니다 . 둘째, Obj-C에 노출 될 필요가없는 멤버에 대해 썽크를 생성하지 않아도되므로 이진 크기를 줄이고 세 번째로 동적 연결 속도를 향상시킵니다.

멤버를 Obj-C에 노출하려면 @objc예를 들어 다음 과 같이 표시해야합니다 .

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

( "최소 추론"옵션을 선택한 상태로 실행할 때 선택기가있는 경우 마이그레이션 기는이 작업을 자동으로 수행해야합니다)

멤버 그룹을 Obj-C에 노출 시키려면 다음을 사용하십시오 @objc extension.

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

이렇게하면 정의 된 모든 멤버가 Obj-C에 노출되고 Obj-C에 노출 될 수없는 멤버 (에 명시 적으로 표시되지 않은 경우 @nonobjc)에 오류가 발생합니다.

모든 Obj-C 호환 멤버를 Obj-C에 노출 해야하는 클래스가있는 경우 클래스를 @objcMembers다음 과 같이 표시 할 수 있습니다 .

@objcMembers
class ViewController: UIViewController {
   // ...
}

이제 추론 할 수있는 모든 구성원이됩니다 @objc. 그러나 위에서 언급 한 단점을 불필요하게 노출시키는 단점을 감안할 때 Obj-C에 노출 된 모든 구성원 이 실제로 필요한 경우가 아니면이 작업을 수행하지 않는 것이 좋습니다 .


2
코드를 최신 구문으로 변환 할 때 Xcode가 자동으로 그렇게하지 않는 이유는 무엇입니까?
LinusGeffarth

1
나는 @IBAction또한 자동으로 궁금해 @objc? 지금까지는 그렇지 않았지만 논리적 일 것입니다. 편집 : nvm, 제안서 @IBAction에는 방법이 충분하다고 명시 되어 있습니다 @objc.
Sulthan

8
나는 이것 중 어느 것도 이해하지 못한다. Objective-C-code가 없습니다. 버튼에 대상을 추가하는 순수한 스위프트 방식이 있습니까? 아니면 선택기를 사용합니까? 이 속성으로 코드를 수렴하고 싶지 않습니다. UIButton이 일부 NSObject / Obj-c-thing에서 파생 되었기 때문입니까?
Sti

11
@Sti 직접 대답하려면 " 버튼에 대상을 추가하는 순수한 스위프트 방식이 없습니까? 아니면 셀렉터를 사용합니까? "– 아니오, 셀렉터를 사용하여 메소드에 디스패치하는 "순수한 스위프트"방식은 없습니다. 그들은 특정 선택기를 호출하기 위해 메소드 구현을 조회하기 위해 Obj-C 런타임에 의존합니다. Swift 런타임에는 해당 기능이 없습니다.
Hamish

12
남자 셀렉터는 엉망입니다. 그들이 속이는 다른 모든 빠른 업데이트처럼 보입니다. 왜 메소드를위한 순수한 자동 완성 셀렉터를 가질 수 없는가? 코드가 너무보기 흉하게 보입니다.
John Riselvato '10

16

애플 공식 문서 . @objc를 사용하여 Selector 메서드를 호출해야합니다.

Objective-C에서 선택기는 Objective-C 메소드의 이름을 나타내는 유형입니다. Swift에서 Objective-C 선택기는 Selector구조 로 표시되며 #selector 표현식을 사용하여 구성 할 수 있습니다 . Objective-C에서 호출 할 수있는 메소드의 선택기를 작성하려면 다음과 같이 메소드 이름을 전달하십시오 #selector(MyViewController.tappedButton(sender:)). 특성의 목표-C 게터 세터 또는 방법에 대한 선택을 구성하기 위해, 접두어 속성 이름 통과 getter:또는 setter:같은 라벨, #selector(getter: MyViewController.myButton).


내가 이해하는 것은 Objective-C에서 호출할지 여부에 관계없이 어떤 경우에는 @objc를 func의 시작 부분에 추가해야한다는 것입니다. 나는 단지 몇 년 동안 Obj-C를 사용해온 스위프트를 배우고있다
SundialSoft

10

현재 Swift 4.2에서는 @IBAction을 메소드에 할당하는 것만 으로이 어리석은 @objc 주석을 피할 수 있다고 생각합니다.

```

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))


@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}

1
이것은 또한 인터페이스 빌더에게이 기능이 연결될 수 있음을 알려줄 것입니다. @objc와 같은 일을하고 있습니다.
Josh Paradroid

2

다른 답변에서 이미 언급했듯이 @objc선택기 의 주석 을 피할 수있는 방법이 없습니다 .

그러나 OP에 언급 된 경고는 다음 단계를 수행하여 침묵시킬 수 있습니다.

  1. 빌드 설정으로 이동
  2. 키워드 @objc 검색
  3. Swift 3 @objc 인터페이스 의 값 을Off

아래는 위에서 언급 한 단계를 보여주는 스크린 샷입니다.

"Swift 3 @objc 인터페이스"경고음

도움이 되었기를 바랍니다


이것이 더 큰 그림에서 프로젝트에 어떤 영향을 미칩니 까?
Zonily Jame

1
* .ipa 또는 * .xarchive 크기 및 빌드 시간은 어떻습니까?
Zonily Jame

@ZonilyJame 나는 빌드 시간을 알지 못했지만 IPA 크기에는 영향을 미치지 않았다
S1LENT WARRIOR

Apple 지침에 따라 swift 4.0 이상 버전을 사용하는 경우 사용할 수있는 권장 값이 아닙니다. 이 help.apple.com/xcode/mac/current/#/deve838b19a1
Bhavin_m의

1
Xcode 11에서 값을 기본값으로 설정하여이 답변을 업데이트 할 수 있습니다. 경고를 완전히 제거하려면 프로젝트와 대상 모두에서 값을 설정하는 것이 중요합니다.
Tommie C.

2

뷰 컨트롤러에 객관적인 c 멤버가 필요한 경우 뷰 컨트롤러 상단에 @objcMembers 를 추가하십시오 . 그리고 코드에 IBAction을 추가하여이를 피할 수 있습니다.

@IBAction func buttonAction() {

}

스토리 보드에서이 콘센트를 연결하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.