Làm thế nào để tạo các giao thức chung trong Swift?


85

Tôi muốn tạo một giao thức với một phương thức nhận đầu vào chung và trả về giá trị chung.

Đây là những gì tôi đã thử cho đến nay, nhưng nó tạo ra lỗi cú pháp.

Sử dụng số nhận dạng không khai báo T.

Tôi đang làm gì sai?

protocol ApiMapperProtocol {
    func MapFromSource(T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    func MapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel() as UserModel
        var accountsData:NSArray = data["Accounts"] as NSArray     
        return user
    } 
}

Vui lòng kiểm tra câu trả lời của tôi: stackoverflow.com/a/54900296/3564632
denis_lor 27/02/19

Câu trả lời:


141

Nó hơi khác đối với các giao thức. Xem "Các loại liên kết" trong tài liệu của Apple .

Đây là cách bạn sử dụng nó trong ví dụ của bạn

protocol ApiMapperProtocol {
    associatedtype T
    associatedtype U
    func MapFromSource(_:T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    typealias T = NSDictionary
    typealias U = UserModel

    func MapFromSource(_ data:NSDictionary) -> UserModel {
        var user = UserModel()
        var accountsData:NSArray = data["Accounts"] as NSArray
        // For Swift 1.2, you need this line instead
        // var accountsData:NSArray = data["Accounts"] as! NSArray
        return user
    }
}

5
Lưu ý rằng mục đích duy nhất của ApiMapperProtocol là được sử dụng cho ràng buộc chung. Nó không giống như bạn có thể viết let x: ApiMapperProtocol = UserMapper ()
Ben

18
Tại sao Apple lại khăng khăng làm mọi thứ trở nên trực quan?
deusprogrammer

@Ben làm cách nào để đạt được let x: ApiMapperProtocol = UserMapper () trong trường hợp này?
denis_lor 25/02/19

@denis_lor nếu xlà địa phương, thì bạn không cần phải nói rõ ràng loại của nó, vì vậy let x = UserMapper().
Ben Leggiero

2
@BenLeggiero Tôi vừa phát hiện ra bạn có thể làm những việc như let x: ApiMapperProtocol = UserMapper () nếu sử dụng một trong lớp chung chung giữa: stackoverflow.com/a/54900296/3564632
denis_lor 27/02/19

21

Để giải thích một chút về câu trả lời của Lou Franco , Nếu bạn muốn tạo một phương thức sử dụng một phương pháp cụ thể ApiMapperProtocol, do đó bạn làm như vậy:

protocol ApiMapperProtocol {
    associatedtype T
    associatedtype U
    func mapFromSource(T) -> U
}

class UserMapper: NSObject, ApiMapperProtocol {
    // these typealiases aren't required, but I'm including them for clarity
    // Normally, you just allow swift to infer them
    typealias T = NSDictionary 
    typealias U = UserModel

    func mapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel()
        var accountsData: NSArray = data["Accounts"] as NSArray
        // For Swift 1.2, you need this line instead
        // var accountsData: NSArray = data["Accounts"] as! NSArray
        return user
    }
}

class UsesApiMapperProtocol {
    func usesApiMapperProtocol<
        SourceType,
        MappedType,
        ApiMapperProtocolType: ApiMapperProtocol where
          ApiMapperProtocolType.T == SourceType,
          ApiMapperProtocolType.U == MappedType>(
          apiMapperProtocol: ApiMapperProtocolType, 
          source: SourceType) -> MappedType {
        return apiMapperProtocol.mapFromSource(source)
    }
}

UsesApiMapperProtocolhiện được đảm bảo chỉ chấp nhận SourceTypecác ứng dụng tương thích với ApiMapperProtocol:

let dictionary: NSDictionary = ...
let uses = UsesApiMapperProtocol()
let userModel: UserModel = uses.usesApiMapperProtocol(UserMapper()
    source: dictionary)

Đây là một bài viết rất hay, được ủng hộ. Một vài câu hỏi ngu ngốc: tại sao họ quyết định sử dụng as!thay vì chỉ sử dụng astrong Swift 1.2? Thứ hai: bạn có thể cho tôi biết tại sao chúng ta cần xác định type aliaslại (tức là typealias T = NSDictionary typealias U = UserModel) trong lớp tuân theo giao thức không? Cảm ơn trước.
Unheilig

Tôi không biết tại sao họ chuyển từ assang as!. Kiểm tra các devforums.
Heath Borders

typealias T=NSDictionarytypealias U=UserModelkhông bắt buộc. Tôi đã cập nhật ví dụ để phản ánh điều đó.
Heath Borders

2
như! để chỉ ra rằng nó có thể không thành công. Làm cho nó rõ ràng hơn cho nhà phát triển.
user965972,

Nó nằm ở cuối câu trả lời.
Heath Borders

4

Để có được generic và cũng như việc nó khai báo như thế này, let userMapper: ApiMapperProtocol = UserMapper()bạn phải có một Generic Class tuân theo giao thức trả về một phần tử chung.

protocol ApiMapperProtocol {
    associatedtype I
    associatedType O
    func MapFromSource(data: I) -> O
}

class ApiMapper<I, O>: ApiMapperProtocol {
    func MapFromSource(data: I) -> O {
        fatalError() // Should be always overridden by the class
    }
}

class UserMapper: NSObject, ApiMapper<NSDictionary, UserModel> {
    override func MapFromSource(data: NSDictionary) -> UserModel {
        var user = UserModel() as UserModel
        var accountsData:NSArray = data["Accounts"] as NSArray     
        return user
    } 
}

Bây giờ bạn cũng có thể tham khảo userMappernhư một ApiMappertổ chức có triển khai cụ thể hướng tới UserMapper:

let userMapper: ApiMapper = UserMapper()
let userModel: UserModel = userMapper.MapFromSource(data: ...)

Ý nghĩa của việc có một giao thức trong trường hợp này là gì? Nó không được sử dụng trong khai báo userMapper.
alekop

-1

CÁCH CHẤP NHẬN VÀ SỬ DỤNG GIẤY TỜ CHÍNH HÃNG

giao thức Chung {

associatedtype T
associatedtype U

func operation(_ t:T)->U

}

// sử dụng Giao thức Chung

struct Test: Chung {

typealias T = UserModel
typealias U = Any

func operation(_ t: UserModel)->Any {
    let dict = ["name":"saurabh"]
    return dict
    
} 

}


-3

Bạn có thể sử dụng các phương pháp mẫu với tính năng xóa kiểu ...

protocol HeavyDelegate : class {
  func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R
}  

class Heavy<P, R> {
    typealias Param = P
    typealias Return = R
    weak var delegate : HeavyDelegate?  
    func inject(p : P) -> R? {  
        if delegate != nil {
            return delegate?.heavy(self, shouldReturn: p)
        }  
        return nil  
    }
    func callMe(r : Return) {
    }
}
class Delegate : HeavyDelegate {
    typealias H = Heavy<(Int, String), String>

    func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R {
        let h = heavy as! H
        h.callMe("Hello")
        print("Invoked")
        return "Hello" as! R
    }  
}

let heavy = Heavy<(Int, String), String>()
let delegate = Delegate()
heavy.delegate = delegate
heavy.inject((5, "alive"))

2
Bài đăng này không có lời giải thích. Bạn cũng đã đăng nó như trên stackoverflow.com/questions/28614990/…
user1427799
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.