0

だから私は以下のコードを持っています。可変配列をスコアの観点から並べ替えようとしています。各パブのタイトルに辞書でスコアを割り当てました。しかし、keyssortedbyvalue を呼び出すと、うまくソートされます。ログ (以下) に見られるように、何らかの理由で、この変更はブロック外の変数 sortedPubArray に反映されていません。理由はありますか?

注: sortedPubArray 用に別の変数を作成したので、ブロックの外側の変数に反映されていないことを簡単に確認できます。

//Method to get us the pubs for a specific user
+(NSMutableArray *)PubsForUser:(PFUser *)passedUser
{
//First things first let's create our array to represent the array of pubs which should be returned when our query finally executes (down there somewhere)
__block NSMutableArray *pubArray = [[NSMutableArray alloc] init];
//And the sorted version, which we will ultimately return
__block NSMutableArray *sortedPubArray = [[NSMutableArray alloc] init];

//Get the user passed in so we can get there preferences
PFUser *currentUser = passedUser;
//Get all the keys into a local array so we can traverse through and find out what user likes
NSMutableArray *allTypes = [NSMutableArray arrayWithArray:[currentUser allKeys]];
//first we have to remove the username and email keys from the array
[allTypes removeObject:@"username"];
[allTypes removeObject:@"email"];

NSMutableArray *userTypes = [[NSMutableArray alloc] init];

//Now traverse through array and if the type is set to true add it to our local userTypes array
//For each category in the user
for (NSString *typeKey in allTypes) {
    //If the user has selected this category as one of their choices
    if ([currentUser[typeKey]  isEqual: @YES]) {
        //Then add the category name (ie the key) to our local property representing the users choosen style of pubs
        [userTypes addObject:typeKey];
    }
}

//Create our array of queries
NSMutableArray *queryArray = [[NSMutableArray alloc] init];

//Traverse through our array of user categories and create a query for each one.
for (NSString *style in userTypes) {

    //Set up Parse query
    PFQuery *pubQuery = [PFQuery queryWithClassName:@"Pub"];
    [pubQuery whereKey:style equalTo:@YES];

    //Add query to array of queries
    [queryArray addObject:pubQuery];

}



//Now create final query which will contain each of our subqueries
PFQuery *totalQuery = [PFQuery orQueryWithSubqueries:queryArray];
[totalQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    //Do error checking
    if (error) {
        //Log out error message to user
        UIAlertView *queryErrorAlert = [[UIAlertView alloc] initWithTitle:@"Whoops!" message:@"Houston there's a problem! Try again in 5 minutes or drop us an email if this keep's happening at gordon@outerapp.com" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [queryErrorAlert show];
    } //It worked!
    else {
        //So now we have an array of PFObjects ie Pubs!
        [pubArray addObjectsFromArray:objects];

        //Now sort the array by number of hits, ie number of categories the same. So that the pub/club most tailored to the users tastes is top of the array

        //First create array to contain all pub categories so we're not comparing user's restaurant categories with the pub (this could crash the app as you can see below, we'd be trying toaccessing properties of the pub which don't exist
        NSMutableArray *pubTypes = [NSMutableArray arrayWithArray:[[objects objectAtIndex:0] allKeys]];
        //And set up a dictionary to keep the score of each pub (score is how many of the same types it has as user
        NSMutableDictionary *pubScores = [[NSMutableDictionary alloc] init];


        //This requires us to iterate through the array assinging a value to the variable representing the "likeness" of the pub to the user. So the higher the score, the more hits
        for (PFObject *pub in pubArray) {

            int pubScore = 0;

            //So now we should calculate the total score, by iterating through and adding 1 each time it's true
            for (NSString *category in pubTypes) {

                //Test if the pub and the user's category choice is the same. ie this will iterate through student, theme, gastropub etc and everytime they are the same we add 1, and different -1
                if (pub[category] == currentUser[category]){

                    //They're the same so add to the score
                    pubScore++;

                } //If they're not the same
                else {

                    //Subtract one
                    pubScore--;

                }
            }

            //Now store the score of the pub in our dictionary
            [pubScores setValue:[NSNumber numberWithInt:pubScore] forKey:[pub objectForKey:@"PubName"]];
        }



        //And now finally simply sort the array by score (the first with the highest score), so that the pub with the best rating is at the top of the feed
        //To do this, we can use an inbuilt NSMutableDictionary method to output our keys in descending order of magnitude
        sortedPubArray = [NSMutableArray arrayWithArray:[pubScores keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2) {

            //If it's greater, put it above
            if ([obj1 integerValue] > [obj2 integerValue])
                return (NSComparisonResult)NSOrderedAscending;
            //If lower, then below
            if ([obj1 integerValue] < [obj2 integerValue])
                return (NSComparisonResult)NSOrderedDescending;

            return (NSComparisonResult)NSOrderedSame;

        }]];

        NSLog(@"FinishedBlockLog: %@", sortedPubArray);
    }
}];

NSLog(@"FinishedMethodLog: %@", sortedPubArray);
return pubArray;
}

ログ:

2013-11-09 16:35:27.722 外部 [5037:70b] FinishedMethodLog: ( )

2013-11-09 16:35:27.723 Outer[5037:70b] メソッドの戻り値に割り当てられた変数: ( )

2013-11-09 16:35:27.925 Outer[5037:70b] FinishedBlockLog: (TestPub、「Waxy O'Connors」、「The Ark」、「The Counting House」、Radio)

2013-11-09 16:35:32.590 Outer[5037:70b] メソッドの戻り値が割り当てられた変数をログアウトするために作成したボタン: ( )

したがって、ブロックが終了したときに sortedPubArray をログアウトしていることがわかりますが、ブロックの外側にある sortedPubArray にはまだ反映されていません。どんな助けでも大歓迎です

4

1 に答える 1

3

__blockまず、 forを使用する理由はありませんpubArray。ブロック内のどちらの変数も変更していません (変数が参照するオブジェクトでメソッドを呼び出しても、変数の値は変更されません)。の場合sortedPubArray、空の可変配列をその変数に割り当てる理由はありません。コードの後でその変数に再割り当てするだけだからです。

ところで:PubsForUser:する必要がありますpubsForUser:。メソッドは常に小文字で始まります。

ただし、問題の本当の原因は同時実行モデルにあります。バックグラウンドで何かを実行していますが、結果がフォアグラウンドですぐに利用できることを期待しています。ログ ステートメントを検討してください。

2013-11-09 16:35:27.722 Outer[5037:70b] FinishedMethodLog: ( )

2013-11-09 16:35:27.723 Outer[5037:70b] Variable assigned to the return of the method: ( )

2013-11-09 16:35:27.925 Outer[5037:70b] FinishedBlockLog: ( TestPub, "Waxy O'Connors", "The Ark", "The Counting House", Radio )

タイムスタンプに注意してください。あなたpubsForUserMethod:は に終了していますが、バックグラウンド実行は, ~200ms 後16:35:27.722まで終了しません。16:35:27.925

これを同期的に実行するか、バックグラウンド ブロックの最後にコールバックを配置して、メイン スレッドに準備が整ったことを伝える必要があります (ほとんどの場合)。

dispatch_async(dispatch_get_main_queue(), ^{ [myUIThingy youManYourStuffIsReady: sortedPubArray]; });

実行のために単に何かをバックグラウンドに放り込むことは、あまり良い同時実行モデルではないことに注意してください。データ、同期ポイント、およびスレッド間の一貫性について非常に慎重に検討する必要があります。


ここで、返されたを実際に使用している場合pubsArray(なぜsortedPubsArray存在するのかという疑問が生じます)、UI の状態が更新されない理由をまだ疑問に思っている場合...

... UI の状態はそれ自体では更新されません。そのバックグラウンド タスクには、配列の内容を表示しているテーブル ビューをリロードする必要があることを UI に知らせるコールバックが必要です。

pubsArrayまた、元の値をコピーしたり投げたりしているものがないことを確認する必要があります。pubsForUser:returnを作成(void)し、すべてが完了したときに実行される引数としてブロックを受け取る可能性がある方がはるかに優れています。

于 2013-11-09T18:02:44.587 に答える