如何从函数中的解析查询生成的函数返回值?

时间:2015-07-16 23:20:15

标签: ios swift parse-platform completionhandler

问题:当我在我的ViewController tableView cellForRowAtIndexPath方法中调用下面的函数时,我得到nil,但是我需要将生成的对象设置为一个名为rankObject的PFObject变量,我在ViewController中全局声明。我已经尝试了很多东西,包括完成处理程序,但我仍然陷入困境。我将如何完成这项任务?

func getOrMakeRankRelations(sound: PFObject) {
    var rankRelation = sound.relationForKey("userRanks")
    let currentUser = PFUser.currentUser()!
    var relationQuery = rankRelation.query()
    relationQuery?.whereKey("user", equalTo: currentUser)
    relationQuery?.findObjectsInBackgroundWithBlock { (theObject: [AnyObject]?, error: NSError?) -> Void in
        if (theObject?.isEmpty == true) {
            println("Rank object is empty")

            //Make Ranking PFObject
            var ranking = PFObject(className: "Ranking")
            ranking["user"] = PFUser.currentUser()!
            ranking["Rank"] = 0

            //Upload ranking to Parse
            ranking.saveInBackgroundWithBlock { (saved: Bool, error: NSError?) -> Void in
                if saved {
                    println("Saved ranking")

                    //Add relation between sound and ranking
                    rankRelation.addObject(ranking)
                    sound.saveInBackgroundWithBlock { (saved: Bool, error: NSError?) -> Void in
                        if saved {
                            println("Saved relation")
                            var theObject = (theObject![0] as? PFObject)!
                            self.rankObject = theObject //////////NOTE:  the variable saves while in this closure /////////////
                        } else {
                            println("Relation not saved :(")
                        }
                    }
                } else {
                    println(error)
                }
            }
        } else {
            var theObject = (theObject![0] as? PFObject)!
            self.rankObject = theObject  //////////NOTE:  the variable also saves while in this closure /////////////
        }
    }
}

以下是我在tableView中调用函数的方法:

getOrMakeRankRelation(sound)

1 个答案:

答案 0 :(得分:0)

不处理异步调用的细节,只是它是异步的这一事实,在Objective-C(和Swift)中我们声明一个函数至少需要两个参数:无论参数化远程请求是什么,以及请求完成时运行的块。

阅读你的代码,我还建议将get和create部分分解为自己的函数,只是为了保持理智,所以......

- (void)getOrCreateRankForSound:(PFObject *)sound completion:(void (^)(PFObject *, NSError *))completion {

    [self getRankForSound:sound completion:^(PFObject *rank, NSError *error) {
        if (!rank && !error) {  // there's no rank, and no error either, so we must create one

            [self createRankForSound:sound completion:^(PFObject *rank, NSError *error) {
                completion(rank, error);
            }];

        } else {
            completion(rank, error);
        }
    }];
}

- (void)createRankForSound:(PFObject *)sound completion:(void (^)(PFObject *, NSError *))completion {

    // build a PFObject rank for the sound, then save it and tell our
    // caller what happened by invoking the completion block
    // ...
    [newlyCreatedRank saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
        return (success)? completion(newlyCreatedRank, nil) : completion(nil, error);
    }];
}

现在你的cellForRowAtIndexPath可以安全地调用这个方法,因为它会缓存结果。

// pseudo code in cellForRowAtIndexPath
PFObject *sound = self.myDatasourceArray[indexPath.row];

// we want to change some cell subview to present the rank object

// don't make network requests in this method without caching the result
// assume we have a dictionary of rank objects indexed by the sound object id to which they correspond
PFObject *rank = self.ranksForSound[sound.objectId];
if (rank) {
    // no need to make a network request, use the rank, presumably to update the cell
    cell.rankLabel.text = // some function of rank
} else {
    // we need to make a network request, put a placeholder value in the cell and call our function
    cell.rankLabel.text = // some placeholder value
    [self getOrCreateRankForSound:sound completion:^(PFObject *rank, NSError *error) {
        // this is important, the cell might have scrolled away by the time this block runs
        // don't alter the cell directly, just reload it.  cellForRowAtIndexPath will
        // get called again, but now the cache will be primed
        if (!error) {
            [tableView reloadRowsAtIndexPaths:@[indexPath]];
        }
    }];
}
// configure other parts of the cell
return cell;