简体   繁体   中英

Modifying global variables in Objective-C blocks

So I have an instance function that takes in an NSInteger as a parameter; and in the function, I have a block. I need to modify the NSInteger that gets passed into the function. But it isn't a __block type. How should I go about doing that? The original function is too complicated so I'll just put a simplified version here...

//@interface
@property(nonatomic) NSInteger input; 
...

//@implementation
[self doThis:self.input];

-(void)doThis:(NSInteger)integer{
    [API doSomethingWithThisInteger:integer success:^(NSMutableDictionary *data){
        ...
    } failure:^(NSString *error){
        integer--;
    }
}

I know that I'm supposed to pass in a __block type variable but if I initialized a new one in the function (ie __block NSInteger temp = integer ) and put temp-- instead of integer-- in the failure block, then self.input would remain the same since the initialization statement copies the value of input instead of referencing to it. What should I do here? Is there a way to make the new variable a reference to the variable I pass into the function? Thanks!


EDIT: solution to problem - Used a global variable instead of a property -

@implementation
NSInteger input;
....
[self doThis:&input]; //sends in the address of the input
....
- (void)doThis:(NSInteger *)integer{ //takes the pointer of the input instead of its actual value so it gets referenced rather than getting copied
    [API doSomethingWithThisInteger:integer success:^(NSMutableDictionary *data){
    ...
} failure:^(NSString *error){
    *integer = *integer - 1; //dereference the pointer to get the value.
}

You have to give a block some reference to variable to modify. By calling doThis: you pass an integer by value (not by reference), so failure block gets effectively just a copy of integer value - so original variable has no chance to get modified.

The same is valid for __block NSInteger temp = integer - temp gets a copy of an integer . Block can modify temp , however it's just a copy of integer - so no chance to change the original value.

To get the value changed, use:

-(void)doThis
{
    [API doSomethingWithSuccess:^(NSMutableDictionary *data)
    {
        ...
    } 
    failure:^(NSString *error)
    {
        self.input--;
    }
}

This way you get a reference to input via self . However, it's considered bad in ARC environment because self gets impliciltly captured by the block and this may lead to retain cycle. So, the best way is create weak reference to self and let it get captured by the block:

-(void)doThis
{
    __weak typeof(self) weakSelf = self;
    [API doSomethingWithSuccess:^(NSMutableDictionary *data)
    {
        ...
    } 
    failure:^(NSString *error)
    {
        weakSelf.input--;
    }
}

PS Your question effectively discloses, that you have no idea, how it works - pointers, passing parameters by value/by reference, ObjC blocks etc. You should get more theoretical knowledge about your programming language to avoid such questions in future.

Objective-C, like C, passes everything by value. It welds objects on top of C by putting them on the heap and referring to them by pointer. So what you're passing around isn't the object itself, it's the address of the object. The address itself is passed by value. But if someone knows the address, they can go to the heap and modify the object.

Your input to doThis: is a parameter, integer . So when calling that method what will have happened is:

  1. whatever you wrote where the parameter should be will be evaluated and, if necessary, implicitly cast to `NSInteger';
  2. a copy of that NSInteger will be supplied to the method;
  3. having received its own copy, integer is now equivalent to a local variable for the method.

So eg you could do:

[object doThis:8];

The 8 is copied into what is effectively a local variable within doThis: , so you can modify integer all you like regardless of the fact that you passed in a constant.

If you want doThis: to be able to modify the integer then you need to supply a pointer to it. And once you have that you should have no problem using that pointer inside a block. The pointer itself will be captured but, as when passing a pointer into a method, if you modify what the pointer points to then that will effect everybody else that looks there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM