신속하게 배열 유형 및 기능 매개 변수로 프로토콜 사용
특정 프로토콜에 맞는 객체를 저장할 수있는 클래스를 만들고 싶습니다. 개체는 형식화 된 배열에 저장해야합니다. 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
The limited solution that I found is to mark the protocol as a class-only protocol. This will allow you to compare objects using '===' operator. I understand this won't work for structs, etc., but it was good enough in my case.
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
}
}
}
}
The solution is pretty simple:
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
}
}
}
I take it that your main aim is to hold a collection of objects conforming to some protocol, add to this collection and delete from it. This is the functionality as stated in your client, "SomeClass". Equatable inheritance requires self and that is not needed for this functionality. We could have made this work in arrays in Obj-C using "index" function that can take a custom comparator but this is not supported in Swift. So the simplest solution is to use a dictionary instead of an array as shown in the code below. I have provided getElements() which will give you back the protocol array you wanted. So anyone using SomeClass would not even know that a dictionary was used for implementation.
Since in any case, you would need some distinguishing property to separate your objets, I have assumed it is "name". Please make sure that your do element.name = "foo" when you create a new SomeProtocol instance. If the name is not set, you can still create the instance, but it won't be added to the collection and addElement() will return "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)
}
}
I found a not pure-pure Swift solution on that blog post: http://blog.inferis.org/blog/2015/05/27/swift-an-array-of-protocols/
The trick is to conform to NSObjectProtocol
as it introduces isEqual()
. Therefore instead of using the Equatable
protocol and its default usage of ==
you could write your own function to find the element and remove it.
Here is the implementation of your find(array, element) -> Int?
function:
protocol SomeProtocol: NSObjectProtocol {
}
func find(protocols: [SomeProtocol], element: SomeProtocol) -> Int? {
for (index, object) in protocols.enumerated() {
if (object.isEqual(element)) {
return index
}
}
return nil
}
Note: In this case your objects conforming to SomeProtocol
must inherits from NSObject
.
'development' 카테고리의 다른 글
자식 서브 모듈 추적 최신 (0) | 2020.07.04 |
---|---|
TypeError : module .__ init __ ()는 최대 2 개의 인수를받습니다 (3 개 제공) (0) | 2020.07.04 |
Maven을 사용할 때 더 엄격한 Java 8 Javadoc을 해결하는 방법 (0) | 2020.07.03 |
“char s [static 10]”과 같은 함수의 배열 매개 변수에서 static 키워드의 목적은 무엇입니까? (0) | 2020.07.03 |
CSS @ font-face-“src : local ( '☺')”은 무슨 뜻입니까? (0) | 2020.07.03 |