问题:当我在我的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)
答案 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;