passing __block parameters to class method (for get request)

812 Views Asked by At

I want to create the following class method:

+(void) getValue4Key:(NSString*)p_key andSet:(id)p_variable
{    
    NSString *baseURLString = <<myURL>>;
    @try{
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
        [manager GET:baseURLString
          parameters:nil
             success:^(AFHTTPRequestOperation *operation, id responseObject) {
                 NSDictionary* element = responseObject[0];
                 element = [element objectForKey:@"fields"];

                 p_variable = [element objectForKey:@"value"];
             }
             failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                 NSLog(@"getSystemVariableKey error: %@",error);
             }];
    }
    @catch (NSException *exception) {
        NSLog(@"exception %@", exception);
    }
}

Two questions:

  1. I get the following error: Variable is not assignable (missing __block type specifier) how can I set a block to the method parameter?
  2. How to call this function should I pass the variable with &self.setMe?
2

There are 2 best solutions below

0
On

I don't think the approach of passing an ivar by reference to be set asynchronously at some later time is a good approach. What if the object (referred to in question #2 as the self in self.setMe) is destroyed before the request completes? You're going to have random crashes.

Instead, you should approach this with a completion block that callers can use to set the ivar:

+ (void)getValue4Key:(NSString*)p_key 
      withCompletion:(void (^)(id value))completion
{    
    NSString *baseURLString = <<myURL>>;
    @try{
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
        [manager GET:baseURLString
          parameters:nil
             success:^(AFHTTPRequestOperation *operation, id responseObject) {
                 NSDictionary* element = responseObject[0];
                 element = [element objectForKey:@"fields"];

                 id value = [element objectForKey:@"value"];
                 if (completion) {
                     completion(value);
                 }
             }
             failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                 NSLog(@"getSystemVariableKey error: %@",error);
                 if (completion) {
                     completion(nil);
                 }
             }];
    }
    @catch (NSException *exception) {
        NSLog(@"exception %@", exception);
    }
}

Then you would call this function like this:

YourObject *__weak weakSelf = self;
[YourObject getValue4Key:@"your_key" completion:^(id value){
    weakSelf.setMe = value;
}];

Now, if self gets destroyed, weakSelf will become nil so the callback will be, basically, a no-op.

This has the added advantage of not needing to pass ivar pointers by reference, which you will note, doesn't happen very often at all in the iOS frameworks (NSError being the only exception I can think of off-hand).

0
On

p_variable is a parameter, which is a local variable of the function. Your block is run asynchronously at the end of some operation. By the time the block runs, it is long after the getValue4Key:andSet: has already returned. Therefore, nobody can make use of the p_variable variable even if you can set it.