performSelector在所有实例上输入方法,但只有一个线程完成

时间:2012-08-17 16:53:42

标签: iphone objective-c ios multithreading objective-c-blocks

这可能是一个天真的问题,但无论如何我都会问它,因为我找不到任何能够解决这个问题的文件。

我在设备上和使用Xcode45-DP4的模拟器中运行iOS5.1。

我有一个循环遍历一个类的多个实例的数组。在该循环中,我在实例上使用performSelector来启动一个执行一些相对较慢的网络操作的线程 - 拉下我宁愿在后台执行的数据。

    [arrayOfFriends enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        Friend *f = (Friend*)obj;
        iOSSLog(@"%d", idx);
        [f performSelectorInBackground:@selector(showDescription) withObject:nil];



-(void)fetchTwitterStatus
{
iOSSLog(@"Trying to fetch twitterstatus %@ %@", self.hash, self.twitterUserName);
[mLocalTwitterUser fetchTwitterAPIUserStatusWithScreenName:twitterUserName
                                     withCompletionHandler:^(NSArray *arrayOfStatus, NSError *error) {
                                         if(error) {
                                             iOSSLog(@"%@", error);
                                         } else {
                                             iOSSLog(@"Got twitterstatus %@ %d", self.twitterUserName, [arrayOfStatus count]);
                                             @synchronized(items) {
                                                 [items addObjectsFromArray:arrayOfStatus];
                                             }
                                         }
                                     }];
}

在我的测试用例中有四个实例。每个实例都会获得它的选择器,你知道。前三个肯定开始,但只有最后一个实际完成,如日志线所示“Got twitterstatus ...”这很奇怪。

我还可以验证选择器调用“fetchTwitterStatus”

的方法

我在这里缺少多线程的基本小块是什么?

编辑:这里是fetchTwitterAPIUserStatusWithScreenName ...这里有很多,但实际上是用JSON响应调用Twitter API端点user_timeline。

- (void)fetchTwitterUserStatusWithScreenName:(NSString *)screenname
                          excludeReplies:(BOOL)excludeReplies
                   withCompletionHandler:(OtterTwitterSearchHandler)completionHandler

{
self.twitterAPIStatusHandler = completionHandler;
//self.fetchTwitterUserStatusHandler = completionHandler;
NSString *urlString = [NSString stringWithFormat:@"https://api.twitter.com/1/statuses/user_timeline.json?screen_name=%@&include_rts=true&include_entities=true&exclude_replies=%@&count=50", screenname, excludeReplies?@"true":@"false"];
NSURL *url = [NSURL URLWithString:urlString];

#warning this isn't the way to do it - just checking the cache for refresh of the scroller
[[ASIDownloadCache sharedCache]removeCachedDataForURL:url];

iOSSRequest *request = [[iOSSRequest alloc] initWithURL:url
                                             parameters:nil
                                          requestMethod:iOSSRequestMethodGET];

NSMutableDictionary *oauthParams = [NSMutableDictionary dictionary];
[oauthParams setObject:[[Twitter sharedService] apiKey] forKey:kASIOAuthConsumerKey];
[oauthParams setObject:[[Twitter sharedService] apiSecret] forKey:kASIOAuthConsumerSecret];
[oauthParams setObject:[self oAuthAccessToken] forKey:kASIOAuthTokenKey];
[oauthParams setObject:kASIOAuthSignatureMethodHMAC_SHA1 forKey:kASIOAuthSignatureMethodKey];
[oauthParams setObject:@"1.0" forKey:kASIOAuthVersionKey];
[oauthParams setObject:[self oAuthAccessTokenSecret] forKey:kASIOAuthTokenSecretKey];

request.oauth_params = oauthParams;


[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
    if (error) {
        if (self.twitterAPIStatusHandler) {
            self.twitterAPIStatusHandler(nil, error);
            self.twitterAPIStatusHandler = nil;
        }
    } else {
        NSMutableArray *recentStatusForTwitterUser = [[NSMutableArray alloc]init];
        NSArray *array = [Twitter JSONFromData:responseData];
        [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            TwitterStatus *twitterStatus = nil;
            twitterStatus = [[TwitterStatus alloc]initWithDictionary:obj];
            [recentStatusForTwitterUser addObject:twitterStatus];
        }];
        if (self.twitterAPIStatusHandler) {
            self.twitterAPIStatusHandler(recentStatusForTwitterUser, nil);
            self.twitterAPIStatusHandler = nil;
        }
    }
}];

}

2 个答案:

答案 0 :(得分:1)

我建议尽可能使用已经提供的异步抽象。这是一个非常罕见/独特的情况,你需要直接处理线程。

我发现将每个基于网络的后台任务视为队列上的同步NSOperation非常有效。

获取NSOperationQueue的新实例,配置它,向其添加任务以及管理队列。这种方法的好处是每个任务都可以作为一个简单的同步任务来实现,并且队列负责并发。您可以选择设置依赖项(此任务必须在该任务之前完成)。

答案 1 :(得分:0)

  

我是多线程的基本小块是什么   在这里失踪?

采用非多线程代码并通过在后台执行任意方法来分离随机数量的线程注定要失败。

并发是一种必须从一开始就仔细考虑的设计模式(或者是一种巨大的重构努力)。

首先,您不希望为每个网络连接生成一个线程。其次,鉴于这些只是HTTP请求,您可能希望使用类内置的系统进行异步HTTP通信。最后,您的并发模型必须准确指定如何保​​持所有数据处于隔离状态,直到您使用任何用于将数据同步回中央存储的机制为止。

很难说在没有看到更多信息的情况下,代码会脱轨。