จะสร้างโปรโตคอลทั่วไปใน Swift ได้อย่างไร


85

ฉันต้องการสร้างโปรโตคอลด้วยวิธีการที่รับข้อมูลทั่วไปและส่งคืนค่าทั่วไป

นี่คือสิ่งที่ฉันได้ลองทำแล้ว แต่มันทำให้เกิดข้อผิดพลาดทางไวยากรณ์

การใช้ตัวระบุที่ไม่ได้ประกาศ T.

ผมทำอะไรผิดหรือเปล่า?

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
    } 
}

โปรดตรวจสอบคำตอบของฉัน: stackoverflow.com/a/54900296/3564632
denis_lor

คำตอบ:


143

มันแตกต่างกันเล็กน้อยสำหรับโปรโตคอล ดู "ที่เกี่ยวข้องประเภท" ในเอกสารประกอบของแอปเปิ้ล

นี่คือวิธีที่คุณใช้ในตัวอย่างของคุณ

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
โปรดทราบว่าจุดประสงค์เดียวของ ApiMapperProtocol คือการใช้สำหรับข้อ จำกัด ทั่วไป ไม่ใช่ว่าคุณสามารถเขียน let x: ApiMapperProtocol = UserMapper ()
Ben

19
เหตุใด Apple จึงยืนยันที่จะทำทุกอย่างให้เป็นไปตามธรรมชาติ
deusprogrammer

@ เบ็นจะบรรลุให้ x: ApiMapperProtocol = UserMapper () ได้อย่างไรในกรณีนี้
denis_lor

@denis_lor ถ้าเป็นท้องถิ่นแล้วคุณไม่จำเป็นต้องพูดอย่างชัดเจนชนิดดังนั้นx let x = UserMapper()
Ben Leggiero

2
@BenLeggiero ฉันเพิ่งค้นพบว่าคุณสามารถทำสิ่งต่างๆเช่น let x: ApiMapperProtocol = UserMapper () หากใช้ในคลาสทั่วไประดับกลาง: stackoverflow.com/a/54900296/3564632
denis_lor

21

เพื่ออธิบายคำตอบของ Lou Francoสักเล็กน้อยหากคุณต้องการสร้างวิธีการที่ใช้เฉพาะApiMapperProtocolคุณต้องทำดังนี้:

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)
    }
}

UsesApiMapperProtocolตอนนี้รับประกันว่าจะยอมรับเฉพาะSourceTypeที่เข้ากันได้กับที่ระบุApiMapperProtocol:

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

นี่เป็นการเขียนที่ดีมากและมีการโหวตเพิ่มขึ้น คำถามโง่ ๆ สองสามข้อ: ทำไมพวกเขาถึงตัดสินใจใช้as!แทนที่จะใช้asใน Swift 1.2 ประการที่สอง: คุณบอกฉันได้ไหมว่าทำไมเราต้องกำหนดtype aliasอีกครั้ง (เช่นtypealias T = NSDictionary typealias U = UserModel) ในคลาสที่สอดคล้องกับโปรโตคอล ขอบคุณล่วงหน้า.
Unheilig

ผมไม่ทราบว่าทำไมพวกเขาเปลี่ยนจากการas as!ตรวจสอบ devforums
Heath Borders

typealias T=NSDictionaryและtypealias U=UserModelไม่จำเป็น ฉันอัปเดตตัวอย่างเพื่อสะท้อนให้เห็นว่า
Heath Borders

2
เช่น! เพื่อระบุว่าอาจล้มเหลว ทำให้ผู้พัฒนาชัดเจนขึ้น
user965972

ท้ายคำตอบ
Heath Borders

4

เพื่อให้บรรลุการมี generics และการประกาศเช่นนี้let userMapper: ApiMapperProtocol = UserMapper()คุณต้องมี Generic Class ที่สอดคล้องกับโปรโตคอลซึ่งจะส่งคืนองค์ประกอบทั่วไป

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
    } 
}

ตอนนี้คุณสามารถอ้างถึงuserMapperว่าApiMapperมีการใช้งานเฉพาะสำหรับUserMapper:

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

1
ประเด็นของการมีโปรโตคอลในกรณีนี้คืออะไร? ไม่ได้ใช้ในการประกาศของuserMapper.
alekop

-1

วิธีการคำนวณและใช้กระบวนการทั่วไป

โปรโตคอลทั่วไป {

associatedtype T
associatedtype U

func operation(_ t:T)->U

}

// ใช้ Generic Protocol

การทดสอบโครงสร้าง: ทั่วไป {

typealias T = UserModel
typealias U = Any

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

}


-3

คุณสามารถใช้เมธอด template กับ type-erasure ...

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
โพสต์นี้ไม่มีคำอธิบาย คุณได้โพสต์ไว้เช่นกันในstackoverflow.com/questions/28614990/…
user1427799
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.