如何正确地将子类传递给Swift'inout'方法进行更新?

时间:2016-06-16 19:34:00

标签: ios swift parse-platform

我想为我的项目创建一个通用的'refreshInBackground'方法,允许更新我的各种PFObject子类。我不能只使用PFObject.refreshInBackground因为我想'包含'几个'键'(指向其他对象的指针)

问题在于,当我将子类作为'inout'参数传递时,我被告知

  

无法将不可变值作为inout参数传递:从'ParseUser'到'PFObject'的隐式转换需要临时

1)为什么'currentUser'不可变?那是因为它试图进行隐式转换吗?

我的子类很简单

class ParseUser : PFUser {
    @NSManaged var teams : [ParseTeam]           // Teams that the user is a member of

    ..more stuff..
}

更新它的电话

var currentUser : ParseUser?
if currentUser != nil {
    // utilize the local user cache... but refresh the user
    // to get the teams
    self.refreshInBackground(parseObject: &currentUser!, withKeys: ["teams"], withCompletion: nil)
}

最后,通用更新功能:

// fetch and refresh an object in the background, including various pointers to included keys
// this is necessary because the native Parse fetchInBackground does not allow 'includeKeys'
func refreshInBackground(inout parseObject object : PFObject, withKeys includeKeys : [String]?, withCompletion completion : ((error : NSError) -> Void)?) {
    // make sure our object has been stored on the server before refershing
    // if it has an objectId, it has been stored
    if let objectId = object.objectId {
        let query = PFQuery(className:object.parseClassName)
        query.whereKey("objectId", equalTo: objectId)
        if let keys = includeKeys {
            query.includeKeys(keys)
        }
        query.getFirstObjectInBackgroundWithBlock({ (updatedObject, error) in
            if error == nil, let update = updatedObject {
                object = update
            }
            completion?(error: error)
        })
    }
    else {
        // oops the object hasn't been saved yet, so don't refresh it
        completion?(error: NSError(domain: "xxxxx", code: 911, userInfo: ["message":"Object Not saved"]))
    }
}

我尝试通过设置临时变量,将其转换并传入来解决它,但当然不会更新currentUser指针...只是临时变量

// Clearly doesn't work as the assignment is made to a placeholder
var user = currentUser! as PFObject
self.refreshInBackground(parseObject: &user, withKeys: ["teams"], withCompletion: nil)

最后,这可以通过返回更新的对象然后在完成处理程序中设置它来解决...但我想了解如何在Swift中执行此操作,这样我每次都不必这样做。理想情况下,“刷新”调用是自包含的。

感谢

2 个答案:

答案 0 :(得分:6)

您无法将派生类型作为基类型的inout参数传递的原因是,这将允许调用者破坏类型安全性。考虑这个例子(不起作用):

class Base {}
class Derived1: Base {}
class Derived2: Base {}

func updateFoo(inout foo: Base) {
    foo = Derived2()
}

var derived1: Derived1 = Derived1()
updateFoo(&derived1)
// derived1 is of type Derived2???

就错误消息而言,Swift的错误消息还不是很好。你经常会收到误导性的信息,而你可能会遇到其中一种情况。

另一件不起作用的事情是inout参数不应包含在转义闭包中。您对变量的inout引用仅在接受变量的函数结束之前有效,我的理解是getFirstObjectInBackground将很容易超过refreshInBackground(并且违反参考的生命周期) 。 SE-0035解释了为什么它现在不起作用以及它如何与Swift 3一起使用。

总之,inout参数创建变量的“卷影副本”,并在函数调用结束时将可能修改的副本分配回原始副本,因此要对变量进行任何修改必须在函数调用结束之前发生(这与后台任务不兼容,后者意味着将来无限期结束)。 Swift 3中的编译时错误是在转义闭包中使用inout参数。它目前正在编译但不起作用。

答案 1 :(得分:2)

你也许可以使用泛型来重写它,它可以让你做你想做的事情(我实际上没有测试过这个,但它应该很接近):

func refreshInBackground<T: PFObject>(inout parseObject object : T, withKeys includeKeys : [String]?, withCompletion completion : ((error : NSError) -> Void)?) {
    // make sure our object has been stored on the server before refershing
    // if it has an objectId, it has been stored
    if let objectId = object.objectId {
        let query = PFQuery(className:object.parseClassName)
        query.whereKey("objectId", equalTo: objectId)
        if let keys = includeKeys {
            query.includeKeys(keys)
        }
        query.getFirstObjectInBackgroundWithBlock({ (updatedObject, error) in
            if error == nil, let update = updatedObject as? T {
                object = update
            }
        })
    }
    else {
        // oops the object hasn't been saved yet, so don't refresh it
        completion?(error: NSError(domain: "xxxxx", code: 911, userInfo: ["message":"Object Not saved"]))
    }
}