-1

TimeLine twitter コードをロードする NSObject を作成しています。

+ (NSArray *)executeTweetFetch

{

    __block NSArray *fetchedTweets = [NSArray array];

    NSURL *getTweetUrl = [NSURL URLWithString:@"http://api.twitter.com/1.1/statuses/home_timeline.json"];



    SLRequest *tweetRequest  = [SLRequest requestForServiceType:SLServiceTypeTwitter

                                                  requestMethod:SLRequestMethodGET

                                                            URL:getTweetUrl

                                                     parameters:nil];
    tweetRequest.account = ACAccount HERE!!!;

    [tweetRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {

        if ([urlResponse statusCode] == 200) {
            // Parse the responseData, which we asked to be in JSON format for this request, into an NSDictionary using NSJSONSerialization.

            NSError *jsonParsingError = nil;
            fetchedTweets = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
            //At this point, fetchedTweet seems working fine, it gets the array send back.
        }
    }];

    return fetchedTweets;

}

...大丈夫です...しかし、ACAccount (別のビュー) を追加する必要があります。+ (NSArray *) であるため、(ACAccount) または外部のオブジェクトを認識しません。

4

1 に答える 1

3

同期性

あなたが尋ねている問題に入る前に、@MartinR がより詳細に言及したことについて議論する価値があります: メソッド-[ACAccount performRequestWithHandler:]は非同期です。

コードが実行されているスレッドからメソッドを呼び出すと、一部のアクティビティが 2 番目のバックグラウンドキューで開始されます。performRequestWithHandler:その間、最初のスレッド (実行中のスレッドと)で実行が続行されますexecuteTweetRequest。が呼び出されてから不確定な時間executeTweetRequestが経過すると、唯一の引数として渡されたブロック-[ACAccount performRequestWithHandler:]が呼び出されます。

したがって、 からツイートの配列を同期的に返すことはできませんexecuteTweetRequest

最もスタイリッシュなアプローチは、メソッドをブロックベースに変更することです。

+ (void)fetchTweetsWithCompletion:(void (^)(NSArray *, NSError *))block
{
    NSURL *getTweetUrl = [NSURL URLWithString:@"http://api.twitter.com/1.1/statuses/home_timeline.json"];
    SLRequest *tweetRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                                 requestMethod:SLRequestMethodGET
                                                           URL:getTweetUrl
                                                    parameters:nil];
    tweetRequest.account = //...this will be addressed in a moment
    [tweetRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
        if ([urlResponse statusCode] == 200) {
            NSError *jsonParsingError = nil;
            NSArray *fetchedTweets = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
            block(fetchedTweets, jsonParsingError);
        }
    }];
}

fetchTweetsWithCompletion:2 番目のサブクラスから呼び出す場合UITableViewController、おそらく次のようなブロックを渡します。

- (void)viewDidLoad
{
    //...
    [TwitterWrapper fetchTweetsWithCompletion:^(NSArray *tweets, NSError *error) {
        if (tweets) {
            self.tweets = tweets;
            [self.tableView reloadData];
        } else if (error) {
            //present a UIAlertView, perhaps...
        }
    }];
    //...
}

このコードの結果、バックグラウンド キューでのツイートのフェッチが完了すると、テーブル ビュー コントローラーのプロパティtweetsがフェッチされた結果に設定され、そのテーブル ビューがデータをリロードします。

スコープ (質問への対処)

あなたが説明している問題

+ (NSArray *) であるため、(ACAccount) または外部のオブジェクトを認識しません。

クラスメソッド内からクラスのインスタンスのインスタンス変数にアクセスできないことです(TwitterWrapper便宜上、呼び出します)+[TwitterWrapper executeTweetFetch]。ここでの問題はスコープの 1 つです。(結果として、ARCのおかげで、問題はメモリ管理の問題でもあります。)

私たちの目標は、最初のテーブル ビュー コントローラーからどこかのインスタンスを隠し、ACAccount2 番目のテーブル ビュー コントローラーからそのインスタンスにアクセスすることです。

これにはいくつかの方法があります。

グローバル変数を使用する

最悪の アプローチ、恐ろしいグローバル変数を使用することです:

では、for twitterを次のTwitterWrapper.hように宣言します。externACAccount

//TwitterWrapper.h

extern ACAccount *twitterAccount;

@interface TwitterWrapper : executeTweetFetch
+ (void)fetchTweetsWithCompletion:(void (^)(NSArray *, NSError *))block;
@end

では、グローバル スコープを持つようTwitterWrapper.mに定義twitterAccountします (たとえば、@implementationブロックの前に:

ACAccount *twitterAccount;

@implementation TwitterWrapper
//...
@end

そして、クラスメソッドの定義ではexecuteTweetFetch

+ (void)fetchTweetsWithCompletion:(void (^)(NSArray *, NSError *))block
{
    //...
    tweetRequest.account = twitterAccount;
    //...
}

: このアプローチは非常にスタイリッシュではなく、まったく危険なことは言うまでもありません。 グローバル変数は可能な限り避けるべきです。 この場合、1 つを回避することは確かに可能です。


次の 2 つの方法は、どちらもインスタンス メソッドへの変更と へのプロパティfetchTweetsWithCompletion:の追加を伴います。クラスは次のようになりますACAccountTwitterWrapper@interface

@interface TwitterWrapper : NSObject
@property (nonatomic) ACAccount *account;
- (void)fetchTweetsWithCompletion:(void (^)(NSArray *, NSError *))block;
@end

は次の@implementationようになります

@implementation TwitterWrapper

- (void)fetchTweetsWithCompletion:(void (^)(NSArray *, NSError *))block
{
    //...
    twitterRequest.account = self.account;
    //...
}

@end

共有インスタンスを使用する

この場合の 2 番目に悪い方法は、 の共有インスタンスを使用することですTwitterWrapper。これには、クラス メソッド ( のような巧妙なものと呼ばれる+sharedWrapper) をクラスに追加することが含まれます。

@interface TwitterWrapper : NSObject
+ (TwitterWrapper *)sharedWrapper;
@property (nonatomic) ACAccount *account;
- (void)fetchTweetsWithCompletion:(void (^)(NSArray *, NSError *))block;
@end

Grand Central Dispatchを使用してローカル変数dispatch_onceを初期化します。static

@implementation TwitterWrapper

//...

+ (TwitterWrapper *)sharedWrapper
{
    static TwitterWrapper *sharedWrapper = nil;

    static dispatch_once_t pred;
    dispatch_once(&pred, ^{ 
        sharedWrapper = [[self alloc] init]; 
    });

    return sharedWrapper;
}

//...

@end

この時点で、最初のテーブル ビュー コントローラーTwitterWrapperの のインスタンスを格納し、2 番目のテーブル ビュー コントローラーから(の実装内で) を利用できる のインスタンスができました。ACAccountself.account-fetchTweetsWithCompletion:

最初のサブクラス内のどこかにUITableViewControllerあるでしょう

//...
ACAccount *theAccount = //this is initialized somehow within this class (the first UITableViewController subclass), probably by the selection of a row
[[TwitterWrapper sharedWrapper] setAccount:theAccount];
//...

次に、2 番目のUITableViewControllerサブクラス内 (おそらく 内-viewDidLoad) で、

    //...
    [[TwitterWrapper sharedWrapper] fetchTweetsWithCompletion:^(NSArray *tweets, NSError *error) {
        if (tweets) {
            self.tweets = tweets;
            [self.tableView reloadData];
        } else if (error) {
            //present a UIAlertView, perhaps...
        }
    }];
    //...

ただし、このソリューションは、グローバル変数を使用する場合と非常によく似ています。(違いは、グローバル変数の作成がスレッドセーフになることです。)

TwitterWrapper のインスタンスを渡す

TwitterWrapperより良い解決策は、最初のテーブル ビュー コントローラーから 2 番目のテーブル ビュー コントローラーにのインスタンスを渡すことです。

: 簡潔にするために、ユーザーが最初のテーブル ビュー コントローラーでアカウント行を選択した直後に、2 番目のテーブル ビュー コントローラーがアクティブになると仮定します。

これを行うには、プロパティを追加する必要があります

@property (nonatomic) TwitterWrapper *twitterWrapper;

あなたの2番目のUITableViewControllerサブクラスに。

最初のテーブルビューコントローラー内のどこかにあるでしょう

//...
ACAccount *theAccount = //this is initialized somehow within this class (the first UITableViewController subclass), probably by the selection of a row
TwitterWrapper *theTwitterWrapper = [[TwitterWrapper alloc] init];
twitterWrapper.account = theAccount;
SecondTableViewController *tweetsTableViewController = //initialize the table view controller
tweetsTableViewController.twitterWrapper = theTwitterWrapper;
//...    

次に、2 番目のUITableViewControllerサブクラス内 (おそらく 内-viewDidLoad) で、

    //...
    [self.twitterWrapper fetchTweetsWithCompletion:^(NSArray *tweets, NSError *error) {
        if (tweets) {
            self.tweets = tweets;
            [self.tableView reloadData];
        } else if (error) {
            //present a UIAlertView, perhaps...
        }
    }];
    //...

ACAccount のインスタンスを渡す

TwitterWrapper2 番目UITableViewControllerのサブクラス インスタンスにのインスタンスを格納するのではなく、 のインスタンスを格納し、もう一度ツイートを取得するために のインターフェイスをACAccount変更するのが最善の解決策です。TwitterWrapper

この場合、fetch メソッドを再びクラス メソッドにする必要があります。@MartinR が提案したように、フェッチ メソッドにアカウント パラメータを追加します。

+ (void)fetchTweetsWithAccount:(ACAccount *)theAccount completion:(void (^)(NSArray *, NSError *))block
{
    NSURL *getTweetUrl = [NSURL URLWithString:@"http://api.twitter.com/1.1/statuses/home_timeline.json"];
    SLRequest *tweetRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                                 requestMethod:SLRequestMethodGET
                                                           URL:getTweetUrl
                                                    parameters:nil];
    tweetRequest.account = theAccount;
    [tweetRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
        if ([urlResponse statusCode] == 200) {
            NSError *jsonParsingError = nil;
            NSArray *fetchedTweets = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
            block(fetchedTweets, jsonParsingError);
        }
    }];
}

次に、type のプロパティをACAccount2 番目のUITableViewControllerサブクラスに追加します。

@property (nonatomic) ACAccount *account;

次に、最初のテーブルビューコントローラー内のどこかにあるでしょう

//...
ACAccount *theAccount = ////this is initialized somehow within this class (the first UITableViewController subclass), probably by the selection of a row
SecondTableViewController *tweetsTableViewController = //initialize the table view controller
tweetsTableViewController.account = theAccount;
//...    

次に、2 番目のUITableViewControllerサブクラス内 (おそらく 内-viewDidLoad) で、

    //...
    [TwitterWrapper fetchTweetsWithAccount:self.account completion:^(NSArray *tweets, NSError *error) {
        if (tweets) {
            self.tweets = tweets;
            [self.tableView reloadData];
        } else if (error) {
            //present a UIAlertView, perhaps...
        }
    }];
    //...
于 2013-01-20T19:29:54.280 に答える