'pure'Swift (@objc없이)에서 약한 프로토콜 참조를 만드는 방법


561

weaka protocol로 선언 하지 않으면 Swift에서 참조가 작동하지 않는 것 같습니다 @objc. 순수한 Swift 앱에서는 원하지 않습니다.

이 코드는 컴파일 오류를 발생시킵니다 ( weak클래스 이외 유형에는 적용 할 수 없음 MyClassDelegate).

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

프로토콜 앞에 접두사를 붙이면 @objc작동합니다.

질문 : '순수한'스위프트 방법은 weak delegate무엇입니까?


답변:


1038

프로토콜 유형을로 선언해야합니다 AnyObject.

protocol ProtocolNameDelegate: AnyObject {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

사용 AnyObject하면 클래스만이 프로토콜을 준수 할 수 있지만 구조체 또는 열거 형은 할 수 없다고 말합니다.


25
이 솔루션의 내 문제는 대리인을 호출하면 충돌이 발생한다는 것입니다-EXC_BAD_ACCESS (다른 곳에서 지적한 것처럼). 이것은 버그 인 것 같습니다. 내가 찾은 유일한 해결책은 @objc를 사용하고 프로토콜에서 모든 Swift 데이터 유형을 제거하는 것입니다.
Jim T

12
Swift에서 약한 델리게이트를 수행하는 올바른 방법은 무엇입니까? 애플 문서는 보여 주거나 자신의 예제 코드에 약한과 대리자를 선언되지 않습니다 developer.apple.com/library/ios/documentation/swift/conceptual/...
C0D3

2
이것은 항상 안전하지는 않습니다. 델리 게이터에 대한 참조를 보유하고 있고 강력한 참조주기를 중단해야하는 경우 델리게이트를 약하게 만들어야합니다. 델리게이트가 델리 게이터에 대한 참조를 보유하지 않으면 델리게이트가 범위를 벗어날 수 있으며 (약하기 때문에) 충돌 및 기타 문제가 발생할 수 있습니다.
Trev14

5
BTW : "새로운 스타일"(Swift 5)이해야한다고 생각 protocol ProtocolNameDelegate: AnyObject하지만 중요하지 않습니다.
hnh

1
그것은해야 AnyObject하기 때문에 class어느 시점에서 더 이상 사용되지 않습니다.
José

283

보충 답변

나는 대의원이 약한 지 아닌지에 대해 항상 혼란 스러웠다. 최근에 델리게이트와 약한 참조를 사용하는시기에 대해 더 많이 배웠으므로 향후 시청자를 위해 여기에 보충 요점을 추가하겠습니다.

  • weak키워드 를 사용하는 목적은 강력한 참조주기 를 피하기 위한 것입니다 (주기 유지). 강력한 참조주기는 두 클래스 인스턴스가 서로에 대한 강력한 참조를 가질 때 발생합니다. 그들의 참조 카운트는 절대 0이되지 않으므로 할당 해제되지 않습니다.

  • weak델리게이트가 클래스 인 경우 에만 사용해야 합니다. Swift 구조체와 열거 형은 참조 유형이 아닌 값 유형 (새로운 인스턴스가 생성 될 때 값이 복사 됨)이므로 강력한 참조 주기를 만들지 않습니다 .

  • weak참조는 항상 선택적이며 (그렇지 않으면 사용 unowned) 항상 할당 var되지 않을 let때 옵션을 설정할 수 있도록 항상 사용 하지는 않습니다 nil.

  • 부모 클래스는 자연스럽게 자식 클래스를 강력하게 참조해야하므로 weak키워드를 사용하지 않아야 합니다. 그러나 자녀가 부모에 대한 참조를 원할 경우 weak키워드 를 사용하여 참조를 약하게 만들어야합니다 .

  • weak부모를 참조하는 자식뿐만 아니라 자신이 소유하지 않은 클래스에 대한 참조를 원할 때 사용해야합니다. 두 개의 비 계층 클래스가 서로를 참조해야하는 경우 하나를 약하게 선택하십시오. 선택하는 것은 상황에 따라 다릅니다. 이에 대한 자세한 내용은 이 질문에 대한 답변을 참조하십시오 .

  • 일반적으로weak 대의원은 대부분의 대리자가 자신이 소유하지 않은 클래스를 참조하므로 대의원으로 표시해야합니다 . 자녀가 대리자를 사용하여 부모와 의사 소통하는 경우에는 반드시 그렇습니다. 위임에 약한 참조를 사용하는 것이 문서에서 권장하는 것입니다. (그러나 볼 너무.)

  • 프로토콜은 참조 유형 (클래스)과 값 유형 (구조체, 열거 형) 모두에 사용할 수 있습니다 . 따라서 대의원을 약하게 만들어야 할 경우에는 개체 전용 프로토콜로 만들어야합니다. 이를 수행하는 방법 AnyObject은 프로토콜의 상속 목록 에 추가 하는 것입니다. 과거에는 class키워드를 사용하여이 작업을 수행 했지만 AnyObject지금 선호 합니다.

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }

추가 연구

다음 기사를 읽으면 이것을 훨씬 더 잘 이해할 수있었습니다. 또한 unowned키워드 및 폐쇄와 관련하여 발생하는 강력한 참조주기와 같은 관련 문제에 대해서도 설명합니다.

관련


5
이것은 모두 훌륭하고 흥미 롭지 만 실제로 내 원래의 질문과 관련이 없습니다. 우리는 이미 모든 것을 알고 있으며 약한 프로토콜 참조를 선언하는 방법 을 궁금해 했습니다 (@flainez가 완벽하게 대답했습니다).
hnh

30
네가 옳아. 실제로 이전과 같은 질문이 있었지만이 배경 정보가 많이 누락되었습니다. 위의 내용을 읽고 귀하의 질문과 관련된 모든 문제를 이해하는 데 도움이되는 보충 메모를 작성했습니다. 이제 나는 당신의 대답을 적용하고 내가 왜하는지 알고 있습니다. 앞으로 시청자에게도 도움이 되길 바랍니다.
Suragch

5
그러나 유형에 의존하지 않는 약한 프로토콜을 가질 수 있습니까? 자체적으로 프로토콜은 어떤 객체가 자신을 준수하는지 상관하지 않습니다. 따라서 클래스 또는 구조체 모두 클래스를 준수 할 수 있습니다. 둘 다 준수 할 수 있다는 이점이 있지만 클래스 유형이 약한 클래스 만 가질 수 있습니까?
FlowUI. SimpleUITesting.com

> 대부분의 델리게이트는 자신이 소유하지 않은 클래스를 참조하기 때문에 이것을 대부분의 델리 게이터로 다시 작성합니다. 그렇지 않으면 소유되지 않은 개체가 소유자가됩니다
Victor Jalencas (

36

AnyObject Swift에서 약한 참조를 사용하는 공식적인 방법입니다.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

Apple에서 :

강력한 참조주기를 방지하려면 델리게이트를 약한 참조로 선언해야합니다. 약한 참조에 대한 자세한 내용은 클래스 인스턴스 간의 강력한 참조주기를 참조하십시오. 프로토콜을 클래스 전용으로 표시하면 나중에 대리자가 약한 참조를 사용해야한다고 선언 할 수 있습니다. 클래스 전용 프로토콜에 설명 된대로 AnyObject 에서 상속하여 프로토콜을 클래스 전용으로 표시합니다 .

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID276


7
흥미 롭군 되어 class스위프트 4.1에서 사용되지?
hnh

@hnh 클래스를 만들어서 "의사 프로토콜"을 만들 수 있지만 프로토콜 : AnyObject는 클래스를 만드는 것보다 부작용이 적은 OP가 요구하는 것을 정확하게 수행합니다. (여전히 값 유형으로 이러한 프로토콜을 사용할 수는 없지만 클래스를 선언하면 그 중 어느 것도 해결되지 않습니다)
Arru

8

업데이트 : 매뉴얼이 업데이트 된 것으로 보이며 내가 언급 한 예제가 제거 된 것 같습니다. 위의 @flainez의 답변 편집을 참조하십시오.

원본 : Obj-C와 상호 운용되지 않더라도 @objc를 사용하는 것이 올바른 방법입니다. 프로토콜이 열거 형이나 구조체가 아닌 클래스에 적용되고 있는지 확인합니다. 매뉴얼의 "프로토콜 적합성 확인"을 참조하십시오.


언급했듯이 이것은 IMO가 질문에 대한 답변이 아닙니다. 평범한 Swift 프로그램은 NS'ism에 묶이지 않고 자신의 입장에 설 수 있어야합니다 (이것은 더 이상 델리게이트를 사용하지 않고 다른 디자인 구조를 사용함을 의미 할 수 있습니다). 내 순수한 Swift MyClass는 실제로 목적지가 구조 체인지 객체인지 상관하지 않으며 옵션이 필요하지 않습니다. 어쩌면 그들은 나중에 고칠지도 모릅니다. 결국 새로운 언어입니다. 참조 의미가 필요한 경우 '클래스 프로토콜 XYZ'와 같은 것입니까?
hnh

4
\ @objc에 추가적인 부작용이 있다는 점도 주목할 가치가 있다고 생각합니다. @eXhausted의 NSObjectProtocol 제안이 조금 더 좋습니다. \ @objc-클래스 델리게이트가 'handleResult (r : MySwiftResultClass)'와 같은 객체 인수를 취하면 MySwiftResultClass는 NSObject에서 상속해야합니다! 그리고 아마도 더 이상 네임 스페이스가되지 않은 것 같습니다. \ @objc는 언어 기능이 아닌 브리징 기능입니다.
hnh

나는 그들이 이것을 해결했다고 생각합니다. 다음과 같이 작성하십시오. protocol MyClassDelegate : class {}
user3675131

이것에 대한 문서는 어디에 있습니까? 이것에 대한 정보를 찾을 수 없기 때문에 맹인이거나 무언가 잘못하고 있습니다 ... O_O
BastiBen

나는 확실하지가 OP의 질문에 여부에 응답하면 합니다만, 이것은 당신이 Objc-C와 상호 운용하는 경우 특히 유용)
댄 Rosenstark는

-1

프로토콜은 AnyObject의 하위 클래스 여야합니다.

아래에 주어진 예

    protocol NameOfProtocol: class {
   // member of protocol
    }
   class ClassName: UIViewController {
      weak var delegate: NameOfProtocol? 
    }

-9

Apple은 "class"대신 "NSObjectProtocol"을 사용합니다.

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

이것은 또한 저에게 효과적이며 내 자신의 델리게이트 패턴을 구현하려고 할 때 나타나는 오류를 제거했습니다.


5
질문과 관련이없는이 질문은 델리게이트 객체를 지원하는 순수한 Swift 클래스 (특히 NSObject 없음 )를 작성하는 것에 관한 것입니다. Objective-C 프로토콜 구현에 관한 것이 아닙니다. 후자는 @objc 일명 NSObjectProtocol이 필요합니다.
hnh

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