0

我正在尝试更改块内 int 的值。(在这个代码示例中,SLRequest 本身只是我完整应用程序的上下文设置,预计不会在这里工作 - 只需编译并运行即可,以便我可以测试设置 int。Socialframework 需要包含在项目中为此编译示例。输出包括在下面。)

#import "MainViewController.h"
#import <Social/Social.h>

@interface MainViewController ()

@end

@implementation MainViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // This SLRequest stuff is just to provide the context for the block in question below
    NSString * theURLstr = @"http://someurl";
    NSURL * theNSURL = [[NSURL alloc] initWithString:theURLstr];
    SLRequest *theRequest = [SLRequest requestForServiceType: SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:theNSURL parameters:nil];
    [theRequest setAccount:nil]; // 

    // This is our test value.  We expect to be able to change it inside the following block
    __block int test = 44;
    NSLog(@"(BEFORE block) test is: %d", test);

    [theRequest performRequestWithHandler:
     ^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error2)
     {
         // We get here late.
         test = 27;
         NSLog(@"(INSIDE block) test is: %d", test);

     }];
    // We get here fine, but test isn't changed
    NSLog(@"(AFTER block) test is: %d", test); 
}

// - - 输出:

2013-02-10 19:13:53.283 debugExp1[92743:c07] (BEFORE block) test is: 44
2013-02-10 19:13:53.285 debugExp1[92743:c07] (AFTER block) test is: 44
2013-02-10 19:13:53.312 debugExp1[92743:1303] (INSIDE block) test is: 27

注意无序的输出行。它在执行中间之前开始和结束。我怎样才能解决这个问题?我只想更改该位置的 int 值并让它在块外使用。

=== 更新 ===

我在网上找到了这个问题的多个切线版本,但对我的目的来说是不可用的形式。所以也许我以一种无法回答的方式提出我的问题。让我尝试添加一些上下文。

我的应用程序正在组装来自 Twitter 指定的不同类型的多个 Twitter API 调用的数据。我不是为了展示而建表。我只想要 ivars 中的数据并最终管理对象。除了获取 Twitter 数据之外,该应用程序还有很多工作要做,因此我不想将太多的应用程序结构放在单个 performRequestWithHandler 块中,这样我就可以在某个时候访问它返回的数据。对我来说,从网上获取数据是一种实用操作,而不是唯一的应用程序设计决定因素。我的一些 API 调用特别依赖于从之前以特定顺序执行的其他类型的 API 调用收集的数据。

我需要知道这些先前的调用何时完成,以便我可以使用从它们的数据对象中设置的 ivar 值。

也就是说,我需要一种方法来进行 API 调用#1,将各种返回数据分配给 ivars,在 performRequestWithHandler 块之外使用 ivars 做其他事情,根据调用 #1 进行 API 调用#2 和 #3 等和后续的 API 调用返回,并最终洗牌到其他 viewController 以各种方式显示来自多个 API 调用的返回值的处理数据。

我完全明白 performRequestWithHandler 是在一个未指定的线程上执行的,该线程将在我不知道何时执行。我不担心由于同步网络访问延迟而导致 UI 长时间等待,因为我打算指定非常短的等待时间。我不会让我的用户坐等慢速网络连接!如果连接速度很慢,我会短暂显示一个活动指示器并放弃快速等待以显示警报。应用程序的那部分不是我在这里关注的挑战。

我的挑战是在部分或全部完成后将来自多个 performRequestWithHandler 调用的返回数据组装在一个地方。

在我看来,performRequestWithHandler 的调用级别可能太高,无法实现我需要的那种同步控制和数据访问。我不知道。

上面的代码示例只是我能想到的以一种简单的编译方式隔离和演示我的问题的最简单方法。我已经尝试了很多变种,但到目前为止没有任何乐趣。

希望这有助于澄清我正在使用的设计理念。

感谢您提供的任何具体帮助、线索或想法。在这一点上,一个微小的工作代码示例将是天赐之物,但如果/当我找到一个解决方案时,我会继续努力并发布解决方案。

4

3 回答 3

3

我认为你已经回答了你自己的问题,正如你所看到的,你在块甚至执行之前读取了 __block 变量(两次)。

这是您正在调用的异步方法。它将在viewDidLoad返回后的某个时间完成。所以日志输出完全没有乱序。您所看到的是这些电话如何发挥作用的现实。

您应该考虑您的设计并意识到此请求可能需要很长时间才能完成,特别是如果用户在慢速网络等情况下。因此您需要决定在请求进行时向用户呈现的内容,然后在请求最终完成时如何更新用户。

编辑

根据您的评论,让我尝试给您举一个小例子,希望能让您摆脱困境。我不一定要在这里为您编写一个完整的工作示例,该示例考虑了所有可能的并发性、内存管理和设计含义。但是,一种简单的开始方法是安排一个块,它只更新视图控制器上的某些属性(假设您的视图控制器足够长,可以让请求完成或失败!)

[theRequest performRequestWithHandler:
 ^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error2)
 {
     int test = 27;
     NSLog(@"(INSIDE block) test is: %d", test);

     // now schedule a block on the main queue which will update our UI with this
     // new data "27"
     dispatch_async(dispatch_get_main_queue(), ^{
         // here we are on the main queue, ready to read our test and do something with it.
         NSLog(@"(updating the UI) test is: %d", test);

         // now that we know we can read the data, we can save that data, update the UI, do
         // whatever is appropriate for your app
         // maybe this is as simple as:
         self.someProperty = test;

         // yes, there is a possible retain cycle here. Worry about that after
         // you have the basic mechanics of this understood.

     });

 }];

我希望这能为您指明正确的方向。除此之外,我建议有很多很好的课程(免费和付费)可以帮助您理解这些对移动开发(在 Android或iOS 上)非常重要的基本概念。处理异步方法和事件对于移动设备上的“hello world”之外的任何事情都是不言而喻的。

另一个编辑

我看到您对问题的新编辑。这确实有助于解释这里的上下文。如果您有很多不同的异步调用发生,那么您可能希望将其从您的视图控制器中取出并放入一些数据控制器中,该控制器完成所有这些工作并组装您的结果。在这种情况下,同样的基本原则也适用,您只需考虑更多的调用和更多的排序。

于 2013-02-11T03:30:04.380 回答
1

这是我根据 Firoze 关于 NSNotification 的建议提出的一种解决方案。在这个版本中,我使用的是字符串而不是 int,但对于我的测试目的,它是相同的区别。NSNotification 实际上比我希望的要好!它将对象从一个线程传输到另一个线程的能力将有很大帮助,尽管我在这里只使用 nil。输出如下。

- (void)viewDidLoad
{
[super viewDidLoad];

// Test SLRequest wireframe
NSString * theURLstr = @"http://someurl";
NSURL * theNSURL = [[NSURL alloc] initWithString:theURLstr];
SLRequest *theRequest = [SLRequest requestForServiceType: SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:theNSURL parameters:nil];
[theRequest setAccount:nil];

// Set default
self.myNewTestStr = @"default";
NSLog(@"In viewDidLoad, self.myNewTestStr DEFAULT is %@", self.myNewTestStr);

// Listen on "weGotOurValue"
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(gotOurValue) name:@"weGotOurValue" object:nil];
// [ActivityAlert presentWithText:@"Please wait"];

// Pretend to get some data from the net
[theRequest performRequestWithHandler:
 ^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error2)
 {
     // Not sure I need this dispatch, btw
     dispatch_async(dispatch_get_main_queue(), ^{

         // Change the ivar
         self.myNewTestStr = @"changed";

         // Post notification "weGotOurValue" 
         [[NSNotificationCenter defaultCenter] postNotificationName:@"weGotOurValue" object:nil];
         // [ActivityAlert dismiss];
     });
 }];
}

// Called by NSNotificationCenter when it receives
// a postNotificationName:@"weGotOurValue" message
- (void) gotOurValue {
    NSLog(@"In gotOurValue, self.myNewTestStr is %@", self.myNewTestStr);
}

输出:

2013-02-12 17:44:52.300 debugExp1[5549:c07] In viewDidLoad, self.myNewTestStr DEFAULT is default
2013-02-12 17:44:52.354 debugExp1[5549:c07] In gotOurValue, self.myNewTestStr is changed
于 2013-02-13T02:08:47.117 回答
-1

-(无效)重新加载配置文件{

// 1

SLRequest *request = [SLRequest
                      requestForServiceType:SLServiceTypeFacebook
                      requestMethod:SLRequestMethodGET
                      URL:[NSURL URLWithString:@"https://graph.facebook.com/me"]
                      parameters:@{ @"fields" : @"id,first_name,last_name,gender,birthday,email,picture"}];

// 2
request.account = [FacebookController sharedFacebookController].facebookAccount;
// 3
__block NSDictionary *jsonDictD;
[request performRequestWithHandler:^(NSData *responseData,
                                     NSHTTPURLResponse *urlResponse, NSError *error) {
    if (error)
    {
        // 4
        [AppHelper showAlertViewWithTag:123 title:APP_NAME message:@"There was an error reading your Facebook account." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
    }
    else
    {
        // 5
        NSString *decodedString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
        // NSLog(@"%@",decodedString);
        jsonDictD = [decodedString JSONValue];
        NSLog(@"%@",jsonDictD);
        if (jsonDictD)
        {
            [AppHelper saveToUserDefaults:[jsonDictD objectForKey:@"email"] withKey:@"email"];
            [AppHelper saveToUserDefaults:[jsonDictD objectForKey:@"id"] withKey:@"facebook_id"];


            [userImage setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[[[jsonDictD objectForKey:@"picture"] objectForKey:@"data"] objectForKey:@"url"]]]
                             placeholderImage:[UIImage imageNamed:@"DefaultPic.png"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
             {

                 userImage.image=image;
                 dummyImg=image;
                 [dummyImg retain];

             }
            failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error)
             {

             }];

            self.firstNameTextField.text=[jsonDictD objectForKey:@"first_name"];
            self.lastNameTextField.text=[jsonDictD objectForKey:@"last_name"];
            self.emailTextField.text=[jsonDictD objectForKey:@"email"];

            self.dobTextField.text=[jsonDictD objectForKey:@"birthday"];

            if ([[jsonDictD objectForKey:@"gender"] isEqualToString:@"male"])
            {
                gender=@"Male";
            }
            else
            {
                gender=@"Female";
            }
            //  [[NSNotificationCenter defaultCenter] postNotificationName:@"Get FB" object:nil];
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.tableView reloadData];
                [[DubitAppDelegate getAppDelegate] hideActivityViewer];


            });
        }
    }
}];

}

于 2013-07-23T20:01:24.887 回答