Initializing an object inside a block - ios

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

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.

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 (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.

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];

EXC_BREAKPOINT: Message sent to deallocated instance

I get the above message in XCode 4.6. I've done a pretty thorough search and but nothing seems to match the exact circumstances surrounding my issue. Admittedly, I'm relatively new to iOS dev, and memory-management has never been my strong suit, but this just has me completely miffed.
I have an instance variable theLink which is defined in the class Game as follows:
#interface Game : NSObject
// Class objects
#property(nonatomic,retain) NSMutableArray *queryItems;
#property(nonatomic,retain) NSMutableArray *theArray;
#property(nonatomic,retain) NSString *theLink;
#property(nonatomic,retain) NSString *thePath;
theLink is set in the makeGame method which is called in the method initialiseGame in my view controller:
- (void) initialiseGame
{
bool gameCreated = FALSE;
while (!gameCreated)
{
gameCreated = [theGame makeGame:#"ptl"];
}
[loadingIndicator stopAnimating];
[loading setText:#"Tap to Start"];
[self performSelector:#selector(setLabels) withObject:nil afterDelay:0.0];
}
(Note: the performSelector afterDelay is used to allow the view to update before continuing. Bit of a hack but I couldn't work out a better way!)
The app then loads the game, and when the user taps the screen to start, the next method which is called from the view controller is:
- (void) setupLink
{
...
for(int i=0; i<[theGame.theLink length]; i++) {
...
}
}
It is on this reference to theGame.theLink where I'm am getting the crash.
What has me most confused is that if I call theGame.theLink from inside the initialiseGame method, it is displays correctly, and also calling any other variable from the Game class (such as thePath or theArray works perfectly, so theGame object has not been deallocated in it's entirety, only the variable theLink.
It seems to me that the variable is being deallocated somewhere as the view controller is being updated. I haven't released the variable, and can't work out why only this variable is being deallocated. As I said at the start, memory-management is not my strength!
Any help/ideas would be hugely appreciated. Let me know if you require any more details.
Thanks heaps,
Andrew
EDIT: Setting of theLink within makeGame
- (bool) makeGame:(NSString*)gameType
{
...
[self getLink];
}
- (void) getLink
{
...
if (... && ((arc4random() % 10) > 8))
{
theLink = #"Animals";
}
}
There are many different ways theLink may be set, depending on random numbers and other factors. This is the most basic form which simply sets it to a static string. It doesn't matter how theLink is set or what it is set to, the program always crashes at the same point.
If theLink is being set to the parameter being passed to it ,#"ptl" or some similar temporary string, it will give you a problem, because it is just a pointer pointing at the current location that is holding #"ptl". After the makeGame method is completed, your system will assume that it is all done with #"ptl" and just free it up.
When you make an #"stringwhatever" in your code, it is supposed to be the equivalent of making an NSObject that is an immutable literal instance of #"stringwhataver". It should, in theory handle all the reference counting in a nice way, but when you are doing your own memory management, there are so many ways to lose count of your references.
There's a pretty simple rule to follow. If you've declared properties, access them via the property. To do otherwise (as you are doing above, with theLink = ...) bypasses all of the memory management built into the property accessors.
self.theLink = ...
Would have solved this problem under MRC. Switching to ARC has "solved" your problem without you understanding the root cause.

Should I Release Object in Primitive C-Array

Say you have class Block
Class Block : NSObject
Now, you want to create array in C-style
Block *blocks[5]; // or calling malloc()
Block *aBlock = [[Block alloc] init];
blocks[0] = aBlock;
// at this point, aBlock will be hand over to array blocks slot.
// not like NSArray, object of 'Block' will not retain by #property(retain)
// or should I call retain before hand over the value into its array and release afterward?
// should I still call below code to release object ?
// [aBlock release];
Can someone explain to me should I still need to release the aBlock object afterward?
No because the primitive array won't be retaining each Block object. So if you release it, all Block objects will be cleaned up the moment the function exits scope

Resources