Modifying global variables in Objective-C blocks - ios

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 (i.e. __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--;
}
}
P.S. 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:
whatever you wrote where the parameter should be will be evaluated and, if necessary, implicitly cast to `NSInteger';
a copy of that NSInteger will be supplied to the method;
having received its own copy, integer is now equivalent to a local variable for the method.
So e.g. 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.

Related

Objective c block in a block

Lot of blocks here!
I am trying to use blocks to perform an operation on each record present in a dictionary.
I created a weak reference of the strongRecordBlock and used that to call itself in the strongRecordBlock. It all worked fine until I introduced my actual operation (DataManager addRecord) that I need to perform which in turn is a block.
So there is an exception now, the weakRecordBlock is null after first iteration of the recursive loop. Can anybody please guide!
__weak __block void (^weakRecordBlock)(int i);
void (^strongRecordBlock)(int) = ^(int i) {
NSString *key = weakSelf.recordDictionary.allKeys[i];
CSVRecord *record = [weakSelf.recordDictionary objectForKey:key];
NSLog(#"%d %#", i, record.recordFullname);
[[DataManager sharedInstance] addRecord:record onSuccess:^(NSString *objectId) {
if (i < weakSelf.recordDictionary.allKeys.count-1) {
weakRecordBlock(i+1);//Crashes here
}
else {
completedBlock();
}
} onError:^(NSError *error) {
onError(error);
}];
};
weakRecordBlock = strongRecordBlock;
strongRecordBlock(0);
The problems is that you are trying to use weak pointer inside of block. And it lead to realising weak point after first run loop.
So you need to create strong reference to the weakRecordBlock inside of strongRecordBlock. So just add this line of code on top of your strongRecordBlock
void (^strongPointerToWeakRecordBlock)(int i) = weakRecordBlock;
and replace weakRecordBlock(i+1) with strongPointerToWeakRecordBlock(i+1)
RomanSalabay's answer is right. The problem is -[DataManager addRecord:record onSuccess:] executes its argument block asynchronously. Therefore, that block (the argument to -[DataManager addRecord:record onSuccess:]) needs to keep a strong reference to the objects and blocks used within, to keep them alive until the block runs; otherwise they can be deallocated by the time that block runs. But it captures a weak reference (weakRecordBlock). It needs to copy a strong reference instead. So you should assign the weak variable to a strong variable in the body of the outer block to let the inner block capture.

Objective-C NSArray can't be changed when passed to method in different class

I have an NSArray and I need to change the order of the items within it. I have written a method that will determine the new order:
+(NSArray*)sortProxyForms:(NSArray*)arrayOfForms
{
NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {
return [#(a.ordinal) compare:#(b.ordinal)]; // #(a.ordinal) aka Boxing turns int into NSNumber
}];
arrayOfForms = [sortedForms copy]; // DOES NOT WORK
return sortedOfForms; // WORKS IF ASSIGNED IN THE CALLER
}
So, I can pass the NSArray to be sorted into the method. I call the method like this:
[HPSModelUtilities sortProxyForms:_formProxies];
If I actually try setting arrayOfForms (a reference to _formProxies) within the method then once I have returned from the method then the array is unchanged.
However, if I return the sorted array from the method and assign it to the NSArray in the calling method then the assignment works:
_formProxies = [HPSModelUtilities sortProxyForms:_formProxies]; // _formProxies NSArray is changed
_formProxies is declared in the calling class, and "HPSModelUtilities" is a different class.
How come the NSArray can be changed in the caller, but not changed in the called method, even though it is passed by reference?
When you pass a value into a method it is copied. This is called "pass by value". The arrayOfForms you are passing in is a pointer to an NSArray. This means that the pointer is copied when passed in. Redirecting this pointer to another instance of an NSArray does not change where the original pointer is pointing.
I would rename your method to (NSArray*)sortedArrayFromProxyForms:(NSArray*)proxyForms
If you really want to change where your NSArray reference is pointing in the method. Do it like this.
+ (void)sortProxyForms:(NSArray**)proxyForms {
*proxyForms = sortedForms;
}
You are passing a copy of the array reference (subtly different than passing by reference), but then you are changing where that reference points with this line:
arrayOfForms = [sortedForms copy];
arrayOfForms no longer points to the array instance you passed, but to a different array. You could pass a pointer of pointer, and change where the caller's pointer is pointing, but for what you are doing, I think the reassignment is fine.
If you'd really like here's what your function would look like with pointer of pointer:
+(void)sortProxyForms:(NSArray**)arrayOfForms {
NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {
return [#(a.ordinal) compare:#(b.ordinal)]; // #(a.ordinal) aka Boxing turns int into NSNumber
}];
*arrayOfForms = [sortedForms copy];
}
but I'll add the caveat that this isn't a pattern you see often in objective-c, so I'd avoid it when there are other alternatives available.
Also note when calling this function you need to add the & to get the extra level of indirection:
[HPSModelUtilities sortProxyForms:&_formProxies];

How to determine when the value pointed to by a pointer is nil

I have a situation where troops can attack buildings. Each troop keeps a pointer to its target.
#property (nonatomic, weak) Building *target;
In an update loop, the troops periodically cause damage to their target.
if (_target)
{
if (/*enough time has passed since last attack, attack again*/)
{
[_target attack];
if (_target.health <= 0)
{
[_target removeFromParentAndCleanup:YES]; //Cocos2d
_target = nil;
}
}
}
else /* Find new target */
The problem is:
troop1 deals the blow that fells building1 and moves on to building2
troop2 was attacking building1 but waits until its next attack to determine that building1 is now nil.
I realise the problem is that troop2's pointer has not been set to nil and instead I should be checking that the value of the pointer is nil.
I tried using if (*_target) but was met with the message
Statement requires expression of scalar type
If there a way to achieve this kind of comparison in Objective-C? What other options are there for determining when a value has changed? KVO? Some extensive delegate pattern?
It is the pointer itself that is set to nil when the object it points to is deallocated. if (objectPointer == nil) is always the way to check if an object is nil in Objective-C/Cocoa. If the pointer is not nil, it means the object in question has not in fact been deallocated. If you dereference a pointer to an object, you get a struct, hence the compiler error about needing a scalar value in the if expression.
So, in your case, if if(self.target != nil) is not giving you the result you expect, you should look for remaining strong references to the target (from other objects).
More broadly, as hinted at by trojanfoe's answer, you're relying on ARC's zeroing weak reference behavior for real program logic. In theory this is OK, as (contrary to his initial statement), ARC's zeroing weak behavior is reliable/deterministic. But, it does mean that you have to ensure that targets are always deallocated when they're no longer on the playing field (or whatever). This is a bit fragile. Zeroing weak references are intended as a way to avoid retain cycles (essentially a form of memory leak), rather than as a way to implement logic the way you're doing. The gist of trojanfoe's solution, where you explicitly register and unregister targets as necessary, is probably a more robust solution.
There may be something that I have overlooked here, but to check if the target2 property is nil, just do:
if ( self.target2 == nil ) {
// Something
}
I think you are relying too heavily on the implementation of ARC in that you only know if an object has been removed if the pointer is nil. This is non-portable and can you make any guarantee between the object being released and the pointer becoming nil?
Instead, use a central dictionary of objects, mapped against their unique ID and store just this unique ID rather than the object pointer itself. In this example I'm using a NSNumber for the key using an incrementing integer, but there are probably better keys that can be used. Also Object is the base class of any object you want to store in this dictionary:
// Probably ivars in a singleton class
unsigned _uniqueId = 1;
NSMutableDictionary *_objects;
- (NSNumber *)addObject:(Object *)object
{
NSNumber *key = [NSNumber numberWithUnsignedInt:_uniqueId++];
[_objects setObject:object forKey:key];
return key;
}
- (void)removeObjectForKey:(NSNumber *)key
{
[_objects removeObjectForKey:key];
}
- (Object *)getObjectForKey:(NSNumber *)key
{
return [_objects objectForKey:key];
}
And in your target, simply store the building key:
#property (strong) NSNumber *buildingKey;
and get the building via the methods provided:
Building *building = (Building *)[objectDictionary objectForKey:buildingKey];
if (building != nil)
{
// building exists
}
else
{
// building does not exist; throw away the key
buildingKey = nil;
}
Since target is a weak reference, your code should work "as-is", assuming that [_target removeFromParentAndCleanup:YES]; removes all strong references to the target.
When the last strong reference is removed, all of the weak properties pointing to it will automatically be set to nil. If they are not automatically set to nil, then there is still a strong reference to the target somewhere.
Find and remove that reference, and this will work fine.

iOS: why can't I access a #property from a global Block (outside of method)

I'm trying to create a global block that can be used from any method. I want this block to access #properties of the class. But when I try to do this I get "use of undeclared identifier self" accessing the backing variables _myVar also doesn't work.
Why doesn't this work? And what work around would give me a block that I can access from any method? Thanks.
An example:
#interface myClass()
#property (nonatomic,assign) BOOL subjectSex;
#end
#implementation
// these returns will get: use of undeclared identifier
int (^myBlock) = ^{
if(self.subjectSex) return 1;
return (!_subjectSex);
}
#end
You must define the block inside an instance method. You can then have a static block pointer that all your methods can access. When you assign the block to the pointer you must copy it:
s_blockPtr = [block copy];
Alternatively, you can send the this pointer as argument to the block. It's simpler to understand but may be more typing.
It doesn't so much have to do with property access as that you are using self. (You can use properties of other objects that you have access to without problem.) self does not exist in that scope. self is an implicit parameter in methods. Since your block definition is not inside a method, there is no variable named self (unless you define a global variable named self, but that would probably be a bad idea).
Blocks are nothing it's just a function pointer.If you want to access property in block you have to explicitly set __block in the property i.e.
you have to tell the compiler this is a block type property.
Let's take a closure look on blocks:
suppose there are two threads T1 and T2
//T1 Thread
void fun(int (*funptr)(int a,int b))
{
funptr(2,3);
}
//T2 Thread
int add(int a,int b)
{
return a+b;
}
//Main Thread
fun(&add);
printf("Hello");
From the above code ,assume main thread address is 20004 and executing fun having address 20006 and that fun taking function pointer as an argument which pointing to another function of thread T2 at an address of 20064 which means context switching from Thread T1 and T2, while executing T2 thread,function don't know about the global variables which are in code segment thats why they can't access because they are in different thread and this is the reason blocks are running on different thread.
that's solve.
You can access any object in a block by declaring it block type
#property(nonatomic,retain) __block NSString *strName;
dispatch_async(dispatch_get_current_queue(), ^(void)
{
self.strName= #"XYZ";
});

Initializing an object inside a block

I'm trying to initialize a dict variable but I don't understand why one way works, while the other does not.
In case 1 everything is alright and I can use dict later.
In case 2 it will be released very soon (It will becomes a zombie) and If I try to use it later (outside a block) the program crashes.
Here's some code from my class (c++ mixed with objective-c) written for ios.
Inside the block i tried to initialize variable dict in two different ways.
class Data
{
public:
NSMutableDictionary *dict;
void DoSomeStuff()
{
[NSSomeFrameworkTool doSomeStuffWithCompletionHandler:^(NSError *err) {
// case 1 - OK
dict = [[NSMutableDictionary alloc] initWithDictionary:[NSKeyedUnarchiver unarchiveObjectWithFile:#"dict.dat"]];
// case 2 - will crash later if i try to use dict
dict = [NSKeyedUnarchiver unarchiveObjectWithFile:#"dict.dat"]; }];
}
}
This class has class variable dict, which is initialized in the DoSomeStuff() method.
That method calls a method from the ios framework that uses block (as a callback) to inform me that some task is done.
I was wondering why case 1 and case 2 work different. Maybe it is forbidden to use references outside the block, that was initialized inside this block?
What's wrong with doing this the way shown in case2?
In first case you don't release your dict, and in second case it is autoreleased so you should retain it.
dict = [[NSKeyedUnarchiver unarchiveObjectWithFile:#"dict.dat"] retain];
I think you can use a block variable here.
__block NSMutableDictionary *dict;
Variables are immutable inside of the block. They are a constant copy, a snapshot of the variable at the time of "block creation" so it can not be modified inside the block. The block variable will move the variable to the 'Heap' from the 'Stack' allowing you to change it's state. I'm by no means an expert on blocks, being that they are relatively new to Objective c.But there are some good articles if you google around to learn from.
http://pragmaticstudio.com/blog/2010/7/28/ios4-blocks-1

Resources