【问题标题】:Infer Generic Type in Class Method with Swift使用 Swift 推断类方法中的泛型类型
【发布时间】:2016-09-19 21:50:07
【问题描述】:

泛型方法是否可以根据正在执行的类来推断其类型?我使用 CoreData NSManagedObject 模型来存储和检索本地数据,并设法以一种易于阅读和可用的方式使所有内容通用化,除了在一个地方。如果用户希望查询本地数据库以获取对象列表,他将编写以下行:

let posts: [Post] = Post.all()

这将正确返回数据库中的“所有” Post 对象,但语法要求在从 Post 类本身 (Post.all()) 调用方法之上定义类型 ([Post]),这感觉不必要的多余。有什么方法可以简单地通过从Post 类调用all() 方法来定义泛型类型?我想我可以创建用于获取数据的全局函数,如下所示:

let posts: [Post] = all()

这感觉不像语法如下那样可读:

let posts = Post.all()

尝试改进这一点的目的是让任何选择此项目的开发人员都可以轻松快速地学习结构和风格。此外,这有望在未来提高一般代码的可读性,无论是有人正在处理它还是只是出于其他原因阅读它。

为了更深入了解,这里是有关当前结构的更多信息:

//Model.swift - The model base class. All models extend this class.

class Model: NSManagedObject {
    /**
    Some other stuff here
    **/

    //MARK: Fetch
    internal class func fetch<T: Model>(predicate: NSPredicate? = nil) -> [T]? {
        do {
            if let request = NSFetchRequest.FromEntityName(self.entityName) { //Get entity with the name defined in the current class
                request.predicate = predicate
                if let result = try self.context?.executeFetchRequest(request) as? [T] {
                    return result
                }
            }
        }
        catch let error as NSError {
            Log.Error("\(error)")
        }
        return nil
    }

    //MARK: Fetch general
    class func all<T: Model>() -> [T]? {
        if let result: [T] = self.fetch() {
            return result
        }
        Log.warning("No \(self.entityName) found")
        return nil
    }
}

//Post.swift - An example model class. Extends Model.swift

class Post: Model {
    //some fields
}

//Example view controller
class ViewController: UIViewController {
    override func viewDidLoad() {
        let posts: [Post] = Post.all()
        //do stuff
    }
}

如果有人对此有任何想法,请告诉我。感谢所有帮助!

【问题讨论】:

    标签: ios swift generics


    【解决方案1】:

    在一般情况下,类方法返回“类的类型”甚至是子类的典型方式是使用协议扩展和Self 类型。这是一个示例,它将您的方法归结为最低限度,以使类型检查按您想要的方式工作:

    // define a protocol
    protocol ModelType {}
    // create a static method on the protocol that returns [Self]
    extension ModelType where Self: NSManagedObject {
        static func all() -> [Self]? {
            return [Self]() // do your fetch here
        }
    }
    // conform to the protocol in your class hierarchy
    class Model: NSManagedObject, ModelType {}
    class Post: Model {}
    
    let posts = Post.all()
    // implicit type of `posts` is `[Post]?`
    

    注意all() 应该由协议扩展提供,但不是协议的要求。如果在protocol ModelType 中声明all(),则不能使其使用动态调度,如果要使用动态类型,则必须这样做。


    另外,请注意,在 Swift 3(和 macOS 10.12 / iOS 10 / tvOS 10 / watchOS 3)中,Core Data 本身定义了一些 Swift API 快捷方式来替换您为自己定义的一些快捷方式。注意这个来自What's New in Core Data的例子:

    func findAnimals() {
        context.performAndWait({
            let request = Animal.fetchRequest // implicitly NSFetchRequest<Animal>
            do {
                let searchResults = try request.execute()
                // use searchResults ...
            } catch {
                print("Error with request: \(error)")
            }
        })
    }
    

    最后,关于你选择的风格的一些评论......

    仅供参考,我将所有静态/类方法中的第一个字母大写作为惯例

    尝试改进这一点的目的是让任何选择此项目的开发人员都可以轻松快速地学习结构和风格。此外,这有望在未来提高一般代码的可读性

    我不确定打破语言标准约定(如 Swift 3 API Guidelines 中推荐的小写方法名称)是否与您的目标非常一致,即让其他新开发代码库的开发人员更容易阅读和参与。

    【讨论】:

    • 很好的信息。让我试试看。另外,RE:代码风格-我会考虑的。无论如何,约定本身只是来自与其他人一起在 Swift 中工作,它是标准。不过,我没有在这个项目上与他们合作,所以我想如果语言指南指向不同的方式,就没有理由建立它。
    • 当我开始实现它时,我看到的一件事是let request = Animal.fetchRequest 应该是let request = Animal.fetchRequest();如果其他人看到此答案,使用该修复程序编辑您的回复可能会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-08
    • 1970-01-01
    • 2012-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多