0

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

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)
4

1 回答 1

0

不处理 asynch 调用的细节,只是它是异步的,在 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;
于 2015-07-17T05:08:40.420 回答