How to change a non-local (global) variable from within a block?

I am new to Objective-C and must dynamically change the value of @property (strong, nonatomic) NSMutableArray *allCategories from within the AFHTTPRequestOperationManager in the success block.
[self.allCategories addObject:tempObject];
does not change the value of allCategories when repeated in a loop.

The variable was initialized as self.allCategories = [[NSMutableArray alloc]init];
in viewDidLoad.

I also tried to create a temporary variable like __block NSMutableArray *tempCategories = [[NSMutableArray alloc]init]; before initiating the AFHTTPRequestOperationManager object. tempCategories does not even retain its meaning.

I canโ€™t understand whatโ€™s going on.

Edit
sorry for the inconvenience
viewDidLoad has the following code
self.allCategories = [[NSMutableArray alloc]init];
[self loadData];

Here is the code

 -(NSMutableArray *)loadData { __block NSMutableArray *tempCategories = [[NSMutableArray alloc]init]; manager = [AFHTTPRequestOperationManager manager]; [manager GET:kAPICategoryList parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { // downcast id to NSMutableDictionary NSMutableDictionary *json = (NSMutableDictionary *)responseObject; // check if dictionary is non nil has at least 1 element if (json != nil && [json count] >= 1) { // NSLog(@"json:\t%@", json); // check json is non nil & has success message if ([json objectForKey:kAPIKeyCategoryRoot] != nil) { NSArray *arrCategoriesRoot = [json objectForKey:kAPIKeyCategoryRoot]; // check categories has some data if (arrCategoriesRoot.count >= 1) { for (int i = 0; i < arrCategoriesRoot.count; i++) { SomeModel *pCategory; NSDictionary *dctCategorySingle = [arrCategoriesRoot objectAtIndex:i]; // check category has sub category if ([dctCategorySingle objectForKey:kAPIKeyCategorySubCategory] != nil) { // create category with sub category pCategory = [[SomeModel alloc]initWithSubCategorisedCategoryID:[dctCategorySingle objectForKey:kAPIKeyCategoryID] name:[dctCategorySingle objectForKey:kAPIKeyCategoryName] image:kIMGCategoryDefault subCategory:[dctCategorySingle objectForKey:kAPIKeyCategorySubCategory]]; } else{ // create just a category pCategory = [[SomeModel alloc]initWithCategoryID:[dctCategorySingle objectForKey:kAPIKeyCategoryID] name:[dctCategorySingle objectForKey:kAPIKeyCategoryName] image:kIMGCategoryDefault]; } // else just [tempCategories addObject:pCategory]; [_allCategories addObject:pCategory]; } // for NSLog(@"categories count %lu", [self.allCategories count]); } // if count >= 1 } else if ([json objectForKey:kAPIRespMsgCategoryFetchErrKey] != nil) { [Utility showAlertWithTitle:kAPIRespMsgCategoryFetchErrKey message:[json objectForKey:kAPIRespMsgCategoryFetchErrVal] button:kMsgButtonOkayTtl]; } } else { // error in login => enable login NSLog(@"%@", kMsgNetworkEmptyJSON); } } // network error failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"error %@", [error localizedDescription]); }]; NSLog(@"tempCategories count %lu", [tempCategories count]); return tempCategories; } 

Here is the output form of NSLog:

2015-03-19 18: 27: 17.845 MyProject [4011: 121268] viewDidLoad
2015-03-19 18: 27: 18.133 MyProject [4011: 121268] tempCategories count 0
2015-03-19 18: 27: 18.136 MyProject [4011: 121268] numberOfRowsInSection count 0
2015-03-19 18: 27: 18.137 MyProject [4011: 121268] numberOfRowsInSection count 0
2015-03-19 18: 27: 19.019 MyProject [4011: 121268] categories count 20

When loadData finishes, all categories have no data in it (zero).

+5
source share
4 answers

As far as I know, this should work this way. Are you sure your success block is called before you check the contents of allCategories ?

The success block works asynchronously, which means that it will only be executed when RequestOperation completed (which can take a long time if you download something big)

If you try to get the value of allCategories before the success block is executed, you will not get what you expect. I would recommend using breakpoints or NSLog in your success block to make sure that it was executed when you think it does.

eg

  ... successBlock:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"Success"); [self.allCategories addObject:tempObject] }]; //End of request [operation start]; //Begin executing the AFHTTPOperation NSLog("%@",self.allCategories.description); //probably nil or empty //since the success block hasn't been called yet 

EDIT:

Be that as it may, you are returning the value before it was set using the async operation, in order to return the value from the async operation, I would suggest taking a look at this answer and this . You should also read a little about how an asynchronous task works.

Basically, what you want to do with asynchronous operations / tasks, make sure the value will be available if you want to use it. The main problem is that you do not know when the value will be set, but you can make sure that you want to do when it is set.

To do this, you can create a simple method with a custom completion block.

 - (void)myCustomMethodWithCompletionBlock: (void (^)(NSArray *))completion { //Do your request //... successBlock:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"Success"); completionBlock(allCategories); }]; //End of request } 

Meanwhile, in the main method, you call

  [self myCustomMethodWithCompletionBlock:^(NSArray *allCategories) { self.allCategories = allCategories; //Do other stuff you need to with that variable since now you are //sure the value will be set unless the operation failed }]; 
+4
source

Try the following:

 dispatch_async(dispatch_get_main_queue(), ^{ [self.allCategories addObject:tempObject]; }); 
0
source

I had the same problem a few days ago. My problem was that my array looks null, the distribution of arrays in the viewdidload method may be the request of your request before viewDidLoad. Check it out with debugging if you see that the array is nill and then distribute the array elsewhere. PS: I'm not an expert, but maybe this is the same problem with me.

0
source

Define an NSMutableArray with the following line.

 @property (nonatomic, strong) NSMutableArray * arrData; 

initialize in viewDidLoad

 - (void)viewDidLoad { [super viewDidLoad]; self.arrData = [NSMutableArray array]; } 

call the following method with any UIButton action for see output OR working behavior

 - (void) TestMethod { dispatch_queue_t queue = dispatch_queue_create("myQueue", 0); dispatch_async(queue, ^{ AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL urlWithEncoding:@"https://www.google.co.in/?gws_rd=ssl"]]; [httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]]; [httpClient setDefaultHeader:@"Accept" value:@"application/json"]; [httpClient setParameterEncoding:AFJSONParameterEncoding]; NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:@"" parameters:nil]; [request setTimeoutInterval:180]; [AFJSONRequestOperation addAcceptableContentTypes:[NSSet setWithObject:@"text/html"]]; dispatch_semaphore_t sema = dispatch_semaphore_create(0); AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { [self.arrData addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"test",@"t3da",@"adsf",@"afds", nil]]; dispatch_semaphore_signal(sema); } failure:^ (NSURLRequest *request, NSURLResponse *response, NSError *error, id json){ [self.arrData addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"test",@"t3da",@"adsf",@"afds", nil]]; dispatch_semaphore_signal(sema); }]; [operation start]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); DLog(@"arrData = %@",self.arrData); }); } 
0
source

Source: https://habr.com/ru/post/1215674/


All Articles