programing

프로토콜을 어레이 유형 및 기능 매개 변수로 신속하게 사용

easyjava 2023. 5. 4. 20:43
반응형

프로토콜을 어레이 유형 및 기능 매개 변수로 신속하게 사용

특정 프로토콜에 맞는 객체를 저장할 수 있는 클래스를 만들고 싶습니다.개체는 입력된 배열에 저장해야 합니다.Swift 설명서 프로토콜에 따르면 다음 유형으로 사용할 수 있습니다.

유형이기 때문에 다음과 같은 다른 유형이 허용되는 여러 위치에서 프로토콜을 사용할 수 있습니다.

  • 함수, 메서드 또는 이니셜라이저의 매개 변수 유형 또는 반환 유형으로
  • 상수, 변수 또는 속성의 유형으로 사용
  • 배열, 사전 또는 기타 컨테이너의 항목 유형

그러나 다음은 컴파일러 오류를 생성합니다.

프로토콜 'SomeProtocol'은 자체 또는 관련 형식 요구 사항이 있으므로 일반 제약 조건으로만 사용할 수 있습니다.

이 문제를 어떻게 해결해야 합니까?

protocol SomeProtocol: Equatable {
    func bla()
}

class SomeClass {
    
    var protocols = [SomeProtocol]()
    
    func addElement(element: SomeProtocol) {
        self.protocols.append(element)
    }
    
    func removeElement(element: SomeProtocol) {
        if let index = find(self.protocols, element) {
            self.protocols.removeAtIndex(index)
        }
    }
}

Swift의 프로토콜에서 아직 좋은 해결책이 없는 변종 문제를 발견했습니다.

배열 확장을 참조하여 배열이 Swift?로 정렬되었는지 확인하십시오. 배열 확장에는 특정 문제에 적합한 방법에 대한 제안이 포함되어 있습니다(질문은 매우 일반적이므로 이러한 답변을 사용하여 해결 방법을 찾을 수 있습니다).

사용된 클래스가 다음을 준수해야 하는 형식 제약 조건을 가진 일반 클래스를 만들려는 경우SomeProtocol다음과 같이:

class SomeClass<T: SomeProtocol> {
    typealias ElementType = T
    var protocols = [ElementType]()

    func addElement(element: ElementType) {
        self.protocols.append(element)
    }

    func removeElement(element: ElementType) {
        if let index = find(self.protocols, element) {
            self.protocols.removeAtIndex(index)
        }
    }
}

Swift에는 이를 구현하는 유형에 대해 다형성을 제공하지 않는 특별한 프로토콜 클래스가 있습니다.이러한 프로토콜은 다음을 사용합니다.Self또는associatedtype키워드를 정의합니다(및).Equatable그 중 하나입니다).

경우에 따라 유형 삭제 래퍼를 사용하여 컬렉션을 동형으로 만들 수 있습니다.다음은 예입니다.

// This protocol doesn't provide polymorphism over the types which implement it.
protocol X: Equatable {
    var x: Int { get }
}

// We can't use such protocols as types, only as generic-constraints.
func ==<T: X>(a: T, b: T) -> Bool {
    return a.x == b.x
}

// A type-erased wrapper can help overcome this limitation in some cases.
struct AnyX {
    private let _x: () -> Int
    var x: Int { return _x() }

    init<T: X>(_ some: T) {
        _x = { some.x }
    }
}

// Usage Example

struct XY: X {
    var x: Int
    var y: Int
}

struct XZ: X {
    var x: Int
    var z: Int
}

let xy = XY(x: 1, y: 2)
let xz = XZ(x: 3, z: 4)

//let xs = [xy, xz] // error
let xs = [AnyX(xy), AnyX(xz)]
xs.forEach { print($0.x) } // 1 3

제가 찾은 제한된 해결책은 프로토콜을 클래스 전용 프로토콜로 표시하는 것입니다.이렇게 하면 '===' 연산자를 사용하여 개체를 비교할 수 있습니다.이것이 구조물 등에 효과가 없을 것이라는 것은 이해하지만, 저의 경우에는 충분했습니다.

protocol SomeProtocol: class {
    func bla()
}

class SomeClass {

    var protocols = [SomeProtocol]()

    func addElement(element: SomeProtocol) {
        self.protocols.append(element)
    }

    func removeElement(element: SomeProtocol) {
        for i in 0...protocols.count {
            if protocols[i] === element {
                protocols.removeAtIndex(i)
                return
            }
        }
    }

}

솔루션은 매우 간단합니다.

protocol SomeProtocol {
    func bla()
}

class SomeClass {
    init() {}

    var protocols = [SomeProtocol]()

    func addElement<T: SomeProtocol where T: Equatable>(element: T) {
        protocols.append(element)
    }

    func removeElement<T: SomeProtocol where T: Equatable>(element: T) {
        protocols = protocols.filter {
            if let e = $0 as? T where e == element {
                return false
            }
            return true
        }
    }
}

저는 당신의 주요 목표가 일부 프로토콜에 부합하는 객체 컬렉션을 보유하고 이 컬렉션에 추가한 후 삭제하는 것이라고 생각합니다.이것은 당신의 고객인 "SomeClass"에 명시된 기능입니다.동등한 상속에는 자체가 필요하며 이 기능에는 필요하지 않습니다.사용자 지정 비교기를 사용할 수 있는 "인덱스" 기능을 사용하여 Obj-C의 어레이에서 이 작업을 수행할 수 있었지만 Swift에서는 지원되지 않습니다.따라서 가장 간단한 해결책은 아래 코드에 나와 있는 것처럼 배열 대신 사전을 사용하는 것입니다.당신이 원하는 프로토콜 어레이를 돌려줄 getElements()를 제공했습니다.그래서 SomeClass를 사용하는 사람이라면 누구나 사전이 구현에 사용되었는지조차 모를 것입니다.

어떤 경우든, 당신은 당신의 객체를 분리하기 위해 어떤 구별되는 속성이 필요할 것이기 때문에, 저는 그것이 "이름"이라고 가정했습니다.새 SomeProtocol 인스턴스를 생성할 때 element.name = "foo"를 수행해야 합니다.이름이 설정되지 않은 경우에도 인스턴스를 만들 수 있지만 컬렉션에 추가되지 않고 addElement()가 "false"를 반환합니다.

protocol SomeProtocol {
    var name:String? {get set} // Since elements need to distinguished, 
    //we will assume it is by name in this example.
    func bla()
}

class SomeClass {

    //var protocols = [SomeProtocol]() //find is not supported in 2.0, indexOf if
     // There is an Obj-C function index, that find element using custom comparator such as the one below, not available in Swift
    /*
    static func compareProtocols(one:SomeProtocol, toTheOther:SomeProtocol)->Bool {
        if (one.name == nil) {return false}
        if(toTheOther.name == nil) {return false}
        if(one.name ==  toTheOther.name!) {return true}
        return false
    }
   */

    //The best choice here is to use dictionary
    var protocols = [String:SomeProtocol]()


    func addElement(element: SomeProtocol) -> Bool {
        //self.protocols.append(element)
        if let index = element.name {
            protocols[index] = element
            return true
        }
        return false
    }

    func removeElement(element: SomeProtocol) {
        //if let index = find(self.protocols, element) { // find not suported in Swift 2.0


        if let index = element.name {
            protocols.removeValueForKey(index)
        }
    }

    func getElements() -> [SomeProtocol] {
        return Array(protocols.values)
    }
}

Swift 5.7 / Xcode 14 이후 이 문제는 다음을 사용하여 우아하게 해결할 수 있습니다.any.

protocol SomeProtocol: Equatable {
    func bla()
}

class SomeClass {
    var protocols = [any SomeProtocol]()
    
    func addElement(element: any SomeProtocol) {
        protocols.append(element)
    }
    
    func removeElement(element: any SomeProtocol) {
        if let index = find(protocols, element) {
            protocols.remove(at: index)
        }
    }
}

순수하지 않은 Swift 솔루션을 블로그 게시물에서 찾았습니다. http://blog.inferis.org/blog/2015/05/27/swift-an-array-of-protocols/

요령은 에 따르는 것입니다.NSObjectProtocol 소개하면서isEqual() 따서사는대를 Equatable 및 방법인 " 의프토기사"==당신은 요소를 찾고 제거하기 위해 당신만의 함수를 쓸 수 있습니다.

은 귀사의 다음당구다니입현을 한 것입니다.find(array, element) -> Int?함수:

protocol SomeProtocol: NSObjectProtocol {

}

func find(protocols: [SomeProtocol], element: SomeProtocol) -> Int? {
    for (index, object) in protocols.enumerated() {
        if (object.isEqual(element)) {
            return index
        }
    }

    return nil
}

이 , 는 " " " " " " 입니다.SomeProtocol는 속해야합다에서 .NSObject.

언급URL : https://stackoverflow.com/questions/24888560/usage-of-protocols-as-array-types-and-function-parameters-in-swift

반응형