如何 return 从函数内的解析查询生成的函数值?
How to return a value from a function that is generated from a parse query within the function?
问题:当我在我的 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)
不处理异步调用的细节,只是它是异步的事实,在 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;
问题:当我在我的 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)
不处理异步调用的细节,只是它是异步的事实,在 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;