-1

我正在创建一个 NSObject 来加载 TimeLine twitter 代码:

+ (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:]是异步的。

从正在执行代码的线程调用该方法会导致某些活动在第二个后台队列上启动。同时,在第一个线程(正在执行的线程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:从您的第二个子类调用时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]。这里的问题是范围之一。(结果,多亏了ARC,问题也是内存管理之一。)

我们的目标是从您的第一个表视图控制器中存储一个实例,ACAccount并从您的第二个表视图控制器访问该实例。

有几种方法可以做到这一点:

使用全局变量

糟糕的 方法是使用可怕的全局变量

在中,您将TwitterWrapper.h声明for twitter: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;
    //...
}

注意:这种方法非常不时尚,更不用说完全危险了。 应尽可能避免使用全局变量。 在这种情况下,避免一个当然是可能的。


接下来的两种方法都涉及更改fetchTweetsWithCompletion:为实例方法并将ACAccount属性添加到TwitterWrapper. 类'@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

使用共享实例

在这种情况下,第二个最糟糕的方法是使用TwitterWrapper. 这涉及向类中添加一个类方法(称为类似 的方法+sharedWrapper):

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

并使用Grand Central Dispatchdispatch_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,您可以将第一个表视图控制器中的实例存储到该实例中,ACAccount并从您的第二个表视图控制器中使用(通过self.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];
//...

然后,在您的第二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您的第一个表视图控制器的实例传递给您的第二个。

注意:为简洁起见,我假设第二个表视图控制器在用户选择第一个表视图控制器中的帐户行后立即变为活动状态。

为此,您需要添加一个属性

@property (nonatomic) TwitterWrapper *twitterWrapper;

到你的第二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;
//...    

然后,在您的第二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 实例

最好的解决方案不是TwitterWrapper在您的第二个子类实例上存储一个实例,而是存储一个实例并更改接口以再次获取推文。UITableViewControllerACAccountTwitterWrapper

在这种情况下,我们希望 fetch 方法再次成为类方法。正如@MartinR 建议的那样,在您的 fetch 方法中添加一个帐户参数:

+ (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 的属性添加ACAccount到您的第二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;
//...    

然后,在您的第二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 回答