Swift 프로토콜에서 선택적 방법을 정의하는 방법은 무엇입니까?


359

스위프트에서 가능합니까? 그렇지 않은 경우 해결 방법이 있습니까?


1
나는 마침내 내 대답을 업데이트하도록 동기를 부여했으며, 당신은 그것을 받아 들일 수 있습니다.
akashivskyy

@akashivskyy 나는 가능한 모든 옵션과 장단점을 더 잘 보여주기 때문에 대답을 받아들입니다.
Selvin

답변:


504

1. 기본 구현 사용 (권장).

protocol MyProtocol {
    func doSomething()
}

extension MyProtocol {
    func doSomething() {
        /* return a default value or just leave empty */
    }
}

struct MyStruct: MyProtocol {
    /* no compile error */
}

장점

  • Objective-C 런타임이 필요하지 않습니다 (적어도 명시 적으로는 아님). 즉, 구조체, 열거 형 및 비 NSObject클래스를 준수 할 수 있습니다 . 또한 강력한 제네릭 시스템을 활용할 수 있습니다.

  • 이러한 프로토콜을 준수하는 유형을 만날 때는 항상 모든 요구 사항이 충족되는지 확인할 수 있습니다 . 항상 구체적인 구현이거나 기본 구현입니다. "인터페이스"또는 "계약"이 다른 언어로 작동하는 방식입니다.

단점

  • Void요구 사항이 아닌 경우 적절한 기본값을 가져야 하지만 항상 가능하지는 않습니다. 그러나이 문제점이 발생하면 해당 요구 사항에 실제로 기본 구현이 없어야하거나 API 디자인 중에 실수를 한 것입니다.

  • 적어도 특별한 반환 값으로 그 문제를 해결하지 않고 는 기본 구현과 전혀 구현을 구분할 수 없습니다 . 다음 예제를 고려하십시오.

    protocol SomeParserDelegate {
        func validate(value: Any) -> Bool
    }

    방금 반환하는 기본 구현을 제공하면 true언뜻보기에 괜찮습니다. 이제 다음 의사 코드를 고려하십시오.

    final class SomeParser {
        func parse(data: Data) -> [Any] {
            if /* delegate.validate(value:) is not implemented */ {
                /* parse very fast without validating */
            } else {
                /* parse and validate every value */
            }
        }
    }

    이러한 최적화를 구현할 수있는 방법은 없습니다. 대리인이 메소드를 구현하는지 여부를 알 수 없습니다.

    이 문제를 극복 할 수있는 여러 가지 방법이 있지만 (선택적 클로저, 서로 다른 작업을위한 다른 대리자 개체를 사용하여),이 예는 문제를 명확하게 나타냅니다.


사용 @objc optional.

@objc protocol MyProtocol {
    @objc optional func doSomething()
}

class MyClass: NSObject, MyProtocol {
    /* no compile error */
}

장점

  • 기본 구현이 필요하지 않습니다. 선택적인 메소드 나 변수를 선언하면 준비가 끝납니다.

단점

  • 모든 준수 유형이 Objective-C와 호환되도록 요구 하여 프로토콜의 기능심각하게 제한합니다 . 즉, 상속받은 클래스 만 NSObject이러한 프로토콜을 준수 할 수 있습니다. 구조체, 열거 형, 관련 유형이 없습니다.

  • 선택적으로 적합한 메소드를 구현 하거나 구현하는지 확인하여 선택적 메소드가 구현되는지 항상 확인해야 합니다. 선택적 메소드를 자주 호출하는 경우 많은 상용구가 발생할 수 있습니다.


17
확장 기능을 사용하여 신속하게 개선 된 선택적 방법을 살펴보십시오 (아래)
Daniel Kanaan

1
그리고 인스턴스에서 선택적 프로토콜 방법의 지원을 테스트하는 방법은 무엇입니까? respondsToSelector?
devios1

3
이러지 마! Swift는 프로토콜에서 선택적 방법을 지원하지 않습니다.
fpg1503

2
이 방법은 매개 변수에서 옵션을 지원하지 않습니다. 즉, 그렇게 할 수 없습니다optional func doSomething(param: Int?)
SoftDesigner

4
확실히이 대답은 요즘 몇 년 후에 본질적으로 잘못 되었습니다. 오늘날 Swift에서는 기본 구현을위한 확장을 추가하기 만하면 Swift의 기본 기능입니다. (아래의 모든 현대 답변이 보여주는 것처럼) 요즘 objc 플래그를 추가하는 것은 잘못 될 것입니다.
Fattie

394

Swift 2 이상에서는 프로토콜의 기본 구현을 추가 할 수 있습니다. 이것은 프로토콜에서 새로운 선택적인 방법을 만듭니다.

protocol MyProtocol {
    func doSomethingNonOptionalMethod()
    func doSomethingOptionalMethod()
}

extension MyProtocol {
    func doSomethingOptionalMethod(){ 
        // leaving this empty 
    }
}

선택적 프로토콜 메소드를 작성하는 것은 좋은 방법은 아니지만 프로토콜 콜백에서 구조체를 사용할 수 있습니다.

나는 여기에 작은 요약을 썼다 : https://www.avanderlee.com/swift-2-0/optional-protocol-methods/


2
이것은 아마도 Swift에서 가장 깨끗한 방법입니다. Swift 2.0 이전에는 작동하지 않습니다.
Entalpi

13
@MattQuiros 실제로 프로토콜 정의에서 함수를 선언해야한다는 것을 알았습니다. 그렇지 않으면 프로토콜에 맞는 클래스에서 no-op 확장 함수가 재정의되지 않습니다.
Ian Pearce

3
@IanPearce가 올 바르며 의도적으로 설계된 것 같습니다. WWDC의 "Protocol Oriented Programming"토크 (408)에서 주요 프로토콜의 메소드에 대해 적합한 유형에 제공되는 "사용자 지정 지점"에 대해 설명합니다. 필요한 사용자 지정 지점은 확장에서 정의를받지 않습니다. 선택적 요점은 그렇지 않습니다. 일반적으로 사용자 정의하지 않아야하는 프로토콜의 메소드는 확장에서 완전히 선언 / 정의되어있어 준수 유형을 사용자 정의 할 수 없도록 동적 유형으로 구체적으로 캐스트하지 않는 한 준수 유형을 사용자 정의 할 수 없습니다 .
matthias

4
@FranklinYu이 작업을 수행 할 수 있지만 실제로는 필요하지 않은 'throws'로 API 디자인을 오염시킵니다. 나는 "마이크로 프로토콜"이라는 아이디어를 더 좋아한다. 예를 들어 각 방법은 하나의 프로토콜로 확인한 다음 확인할 수 있습니다. 객체가 프로토콜 인 경우
Darko

3
@Antoine 프로토콜의 함수는 정의에 따라 공개이기 때문에 확장에서 함수를 공개해야한다고 생각합니다. 프로토콜이 모듈 외부에서 사용될 경우 솔루션이 작동하지 않습니다.
Vadim Eisenberg

39

선택적 수정 자@objc 속성 을 사용하여 선택적 요구 사항 프로토콜을 정의 하는 방법에 대한 답변 이 있으므로 프로토콜 확장을 사용하여 선택적 프로토콜을 정의하는 방법에 대한 샘플을 제공합니다.

아래 코드는 Swift 3. *입니다.

/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocol Cancelable {

    /// default implementation is empty.
    func cancel()
}

extension Cancelable {

    func cancel() {}
}

class Plane: Cancelable {
  //Since cancel() have default implementation, that is optional to class Plane
}

let plane = Plane()
plane.cancel()
// Print out *United Airlines can't cancelable*

프로토콜 확장 메소드는 Objective-C 코드로 호출 할 수 없으며 Swift 팀은이를 수정하지 않습니다. https://bugs.swift.org/browse/SR-492


1
잘 했어! Objective-C 런타임에 관여하지 않고 문제를 해결하므로 정답이어야합니다.
Lukas

정말 좋은 것!
tania_S

신속한 문서에서- "확장에 의해 제공되는 기본 구현의 프로토콜 요구 사항은 선택적 프로토콜 요구 사항과 다릅니다. 준수 유형이 자체 구현을 제공 할 필요는 없지만 기본 구현이있는 요구 사항은 선택적 체인없이 호출 할 수 있습니다."
Mec Os

34

신속한 특정 유형을 사용하는 경우 프로토콜을 "@objc"로 표시하는 것과 관련된 다른 답변은 작동하지 않습니다.

struct Info {
    var height: Int
    var weight: Int
} 

@objc protocol Health {
    func isInfoHealthy(info: Info) -> Bool
} 
//Error "Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"

신속하게 작동하는 선택적 프로토콜을 선언하려면 함수를 func 대신 변수로 선언하십시오.

protocol Health {
    var isInfoHealthy: (Info) -> (Bool)? { get set }
}

그런 다음 다음과 같이 프로토콜을 구현하십시오.

class Human: Health {
    var isInfoHealthy: (Info) -> (Bool)? = { info in
        if info.weight < 200 && info.height > 72 {
            return true
        }
        return false
    }
    //Or leave out the implementation and declare it as:  
    //var isInfoHealthy: (Info) -> (Bool)?
}

그런 다음 "?"를 사용할 수 있습니다. 기능 구현 여부 확인

func returnEntity() -> Health {
    return Human()
}

var anEntity: Health = returnEntity()

var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))? 
//"isHealthy" is true

3
이 솔루션을 사용하면 프로토콜 기능 내에서 자체 액세스 할 수 없습니다. 경우에 따라 문제가 발생할 수 있습니다!
George Green

1
@GeorgeGreen 자신에 액세스 할 수 있습니다. 함수 변수를 게으른 것으로 표시하고 클로저 안에 캡처 목록을 사용하십시오 .
Zag

클래스, 프로토콜, 메소드 및 특성 만 @objc를 사용할 수 있습니다. @ objc 프로토콜 메소드 정의에서 Enum 매개 변수를 사용하는 경우 운명입니다.
khunshan

1
@ khunshan,이 방법은 @ objc로 표시 할 것이 필요하지 않습니다. 무슨 말입니까?
Zag

swift와 objc 사이에 열거 형을 사용할 수 없다는 주제에 대한 정보는 @objc 키워드로 브리지 할 수 있습니다.
khunshan

34

다음은 위임 패턴이있는 구체적인 예입니다.

프로토콜 설정 :

@objc protocol MyProtocol:class
{
    func requiredMethod()
    optional func optionalMethod()
}

class MyClass: NSObject
{
    weak var delegate:MyProtocol?

    func callDelegate()
    {
        delegate?.requiredMethod()
        delegate?.optionalMethod?()
    }
}

델리게이트를 클래스로 설정하고 프로토콜을 구현하십시오. 선택적 메소드를 구현할 필요가 없음을 참조하십시오.

class AnotherClass: NSObject, MyProtocol
{
    init()
    {
        super.init()

        let myInstance = MyClass()
        myInstance.delegate = self
    }

    func requiredMethod()
    {
    }
}

한 가지 중요한 점은 선택적 방법이 선택적이며 "?"가 필요하다는 것입니다. 전화 할 때. 두 번째 물음표를 언급하십시오.

delegate?.optionalMethod?()

2
이것이 정답이어야합니다. 간단하고 깨끗하며 설명이 필요합니다.
Echelon

동의합니다.이 답변은 짧고 달콤하며 정직합니다. 감사!
LuAndre

31

에서 스위프트 3.0

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}

시간을 절약 할 수 있습니다.


6
@objc모든 회원에게 왜 필요한 이유 가 있습니까?
DevAndArtist

@ Gatam Sareriya :이 접근법이나 빈 확장 방법을 만드는 것이 가장 좋다고 생각하는 것은 무엇입니까?
eonist

나는 required플래그로 메소드를 시도 했지만 오류가 발생했습니다 : required'init'선언에만 사용할 수 있습니다.
Ben

27
  • optional각 방법 전에 키워드 를 추가해야합니다 .
  • 그러나 이것이 작동 하려면 프로토콜에 @objc 속성이 표시되어 있어야합니다.
  • 이것은이 프로토콜이 클래스에는 적용 가능하지만 구조에는 적용되지 않음을 의미합니다.

사소한 수정 (편집하기에는 너무 작습니다!)이지만 '@optional'이 아닌 '선택적'이어야합니다.
Ali Beadle

5
또한 프로토콜을 구현하는 클래스를 프로토콜뿐만 아니라로 표시해야 @objc합니다.
Johnathon Sullinger

13

프로토콜 상속을 통한 순수한 Swift 접근 방식 :

//Required methods
protocol MyProtocol {
    func foo()
}

//Optional methods
protocol MyExtendedProtocol: MyProtocol {
    func bar()
}

class MyClass {
    var delegate: MyProtocol
    func myMethod() {
        (delegate as? MyExtendedProtocol).bar()
    }
}

11

Antoine의 답변의 메커니즘을 설명하려면 :

protocol SomeProtocol {
    func aMethod()
}

extension SomeProtocol {
    func aMethod() {
        print("extensionImplementation")
    }
}

class protocolImplementingObject: SomeProtocol {

}

class protocolImplementingMethodOverridingObject: SomeProtocol {
    func aMethod() {
        print("classImplementation")
    }
}

let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()

noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"

8

선택적 프로토콜 메소드를 구현할 수있는 방법 을 묻기 전에 구현해야하는지 묻어 야한다고 생각합니다.

우리가 빠른 프로토콜을 고전적인 객체 지향 프로그래밍에서 인터페이스 로 생각한다면 , 선택적 메소드는 그다지 의미가 없으며 아마도 기본 구현을 만들거나 프로토콜을 프로토콜 세트로 분리하는 것이 더 나은 해결책 일 것입니다 (아마도 상속 관계가있을 수 있습니다) 프로토콜에서 가능한 방법 조합을 나타냅니다.

자세한 내용은 https://useyourloaf.com/blog/swift-optional-protocol-methods/를 참조 하십시오 .이 문제에 대한 훌륭한 개요를 제공합니다.


이. SOLID 소프트웨어 개발 원칙"I" 원칙을 준수합니다 .
Eric

7

원래 질문에서 약간 벗어난 주제이지만 Antoine의 아이디어를 바탕으로 누군가에게 도움이 될 것이라고 생각했습니다.

프로토콜 확장이있는 구조체에 대해 계산 된 속성을 선택적으로 만들 수도 있습니다.

프로토콜의 속성을 선택적으로 만들 수 있습니다

protocol SomeProtocol {
    var required: String { get }
    var optional: String? { get }
}

프로토콜 확장에서 더미 계산 속성 구현

extension SomeProtocol {
    var optional: String? { return nil }
}

그리고 이제 선택적 속성이 구현되었거나 구현되지 않은 구조체를 사용할 수 있습니다

struct ConformsWithoutOptional {
    let required: String
}

struct ConformsWithOptional {
    let required: String
    let optional: String?
}

또한 내 블로그의 Swift 프로토콜에서 선택적 속성 을 수행하는 방법을 작성 했으며 Swift 2 릴리스를 통해 변경 사항이있을 경우 계속 업데이트됩니다.


5

선택적이고 필요한 델리게이트 메소드를 작성하는 방법

@objc protocol InterViewDelegate:class {

    @objc optional func optfunc()  //    This is optional
    func requiredfunc()//     This is required 

}

3

다음은 신속한 클래스에만 해당되는 간단한 예제이며 구조 또는 열거 형에는 해당되지 않습니다. 선택적인 프로토콜 방법에는 두 가지 수준의 옵션 연결이 있습니다. 또한 프로토콜을 채택한 클래스는 선언에 @objc 속성이 필요합니다.

@objc protocol CollectionOfDataDelegate{
   optional func indexDidChange(index: Int)
}


@objc class RootView: CollectionOfDataDelegate{

    var data = CollectionOfData()

   init(){
      data.delegate = self
      data.indexIsNow()
   }

  func indexDidChange(index: Int) {
      println("The index is currently: \(index)")
  }

}

class CollectionOfData{
    var index : Int?
    weak var delegate : CollectionOfDataDelegate?

   func indexIsNow(){
      index = 23
      delegate?.indexDidChange?(index!)
    }

 }

" 플레이시 두 가지 수준의 옵션 "부분에 대해 좀 더 설명해 주 delegate?.indexDidChange?(index!)시겠습니까?
Unheilig

2
만약 우리가 protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } 다음 과 같은 선택적이지 않은 메소드를 갖도록 프로토콜을 작성했다면 , 물음표없이 호출 할 것입니다 : delegate?.indexDidChange(index!) 프로토콜에서 메소드에 대한 선택적 요구 사항을 설정할 때, 그에 맞는 Type은 그 메소드를 구현하지 않을 수 있습니다 이므로 ?구현을 확인하는 데 사용되며 , 없는 경우 프로그램이 중단되지 않습니다. @Unheilig
축복의 로프

weak var delegate : CollectionOfDataDelegate?(약한 참조를 보장?)
Alfie Hanssen

@BlessingLopes delegate?사용법에 대한 설명을 답변에 추가 할 수 있습니까 ? 그 정보는 앞으로 다른 사람들을 위해 실제로 속해야합니다. 나는 이것을 공표하고 싶지만 그 정보는 실제로 답에 있어야합니다.
Johnathon Sullinger

3

순식간에 신속하게하고 싶다면 가장 좋은 방법은 Swift 유형의 구조체 와 같은 Swift 유형을 반환하는 경우 기본 구현 particullary를 제공하는 것입니다

예 :

struct magicDatas {
    var damagePoints : Int?
    var manaPoints : Int?
}

protocol magicCastDelegate {
    func castFire() -> magicDatas
    func castIce() -> magicDatas
}

extension magicCastDelegate {
    func castFire() -> magicDatas {
        return magicDatas()
    }

    func castIce() -> magicDatas {
        return magicDatas()
    }
}

모든 기능 을 정의하지 않고 프로토콜을 구현할 수 있습니다.


2

신속한 프로토콜에서 선택적 방법을 생성 할 수있는 두 가지 방법이 있습니다.

1-첫 번째 옵션은 @objc 속성을 사용하여 프로토콜을 표시하는 것입니다. 이것은 클래스에서만 채택 할 수 있음을 의미하지만 개별 메소드를 다음과 같이 선택적인 것으로 표시합니다.

@objc protocol MyProtocol {
    @objc optional func optionalMethod()
}

2-더 빠른 방법 :이 옵션이 더 좋습니다. 이와 같이 아무것도하지 않는 선택적 메소드의 기본 구현을 작성하십시오.

protocol MyProtocol {
    func optionalMethod()
    func notOptionalMethod()
}

extension MyProtocol {

    func optionalMethod() {
        //this is a empty implementation to allow this method to be optional
    }
}

Swift에는 확장이라는 기능이있어 선택적으로 원하는 메소드에 기본 구현을 제공 할 수 있습니다.


0

하나의 옵션은 옵션 함수 변수로 저장하는 것입니다.

struct MyAwesomeStruct {
    var myWonderfulFunction : Optional<(Int) -> Int> = nil
}

let squareCalculator =
    MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)

-1

프로토콜에서 함수를 정의하고 해당 프로토콜의 확장을 만든 다음 선택적으로 사용할 함수에 대해 빈 구현을 만듭니다.


-2

Optional Protocol신속하게 정의하려면 해당 프로토콜 내에서 선언 및 / 선언 @objc전에 키워드 를 사용해야 합니다. 다음은 프로토콜의 선택적 속성 샘플입니다.Protocolattributemethod

@objc protocol Protocol {

  @objc optional var name:String?

}

class MyClass: Protocol {

   // No error

}

2
이것이 질문에 대한 답변이 될 수 있지만이 답변이 문제를 해결하는 데 어떻게 도움이 될 수 있는지에 대한 설명을 추가하는 것이 좋습니다. 자세한 내용 을 알아 보려면 좋은 답변작성하려면 어떻게합니까? 를 참조하십시오.
Roshana Pitigala

-23

@optional메서드 나 속성 앞에을 넣습니다 .


컴파일러 오류 : '선택적'속성은 @objc 프로토콜의 멤버에만 적용 할 수 있습니다.
Selvin

3
@optional올바른 키워드조차 아닙니다. 입니다 optional. 클래스 프로토콜을 @objc속성으로 선언해야 합니다.
Johnathon Sullinger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.