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
Related
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.
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];
I know the instance variable in ARC are by default __strong. How can I release an instance variable when the containing class is still retained. In the following example v is __strong
and c is allocated when object of A is created some where and retained. I want to release the
c instance variable. How to should I do that?, What should be in releaseC method that will release the c instance variable.
#interface A {
Obj *c;
}
#implementation A {
- (id)init {
if((self = [super init])){
c = [[Obj alloc] init];
}
return self;
}
- (void)releaseC {
//what should be here?
}
}
Obj *c; = [[Obj alloc] init];
- (void)releaseC {
c = nil;
}
You cannot directly control when an object is released BUT you can indirectly cause it to happen. How? Remember what ARC does EXACTLY. Unlike human coding convention, ARC parses your code and inserts release statements AS SOON AS OBJECTS CAN be released. This frees up the memory for new allocations straight away, which is awesome/necessary. Meaning, setting an object to nil, or simply allowing a variable to go out of scope ... something that CAUSES A 0 RETAIN COUNT forces ARC to place its release calls there. It must ... because it would leak otherwise.
- (void)releaseC {
c = nil;
}
c = nil;
But some would argue it isn't productive from an efficiency standpoint. And while the release will be immediate in the sense it isn't any longer usable, the memory may not be freed immediately.
there is no need to release the variable in ARC. it done automatically
You are probably miss understanding what you want to do. I suppose you want to release the variable for memory issues. All you have to do is nil it. Instance variables are pointers to objects. As long as an object is pointed by something it is kept alive. As soon as you dont need something you can "stop pointing at it" and it will be released automagically.
As for the design, I am not so sure why you would have a public method that releases an instance variable. (I'm assuming its public because if it was not you would just nil it without actually having to write a method). If you do indeed intend to be able to release an instance variable from outside the class, I would simply make the Instance variable public and release it from anywhere setting it as nil.
I have a 2D array of pointers to Objective-C instances to keep track of game objects on a map grid.
Now I am transitioning my code to ARC, and Xcode pointed the error. I knew pointers to objects aren't allowed as struct members, but this one caught me (almost) off guard.
I understand the rationale behind the ARC constrains, but:
I can't afford the overhead of objective-C arrays when looking up objects in the grid, and
The objects themselves are already owned by an NSArray ivar defined in the same class that has the C-style grid as an ivar; the c-style array is only a conveniently structured shortcut. Futhermore, when objects are removed from the owning NSArray, I set the corresponding grid slot to NULL.
That is, the 2D array (grid) is just a collection of fast (but dumb) pointers to objects safely retained somewhere else (the NSArray ivar).
Is there a way to get away with this using casts? For example, define and alloc my grid as:
void*** _grid;
instead of
MyMapObjectClass*** _grid
and use (appropriately bridged) casts between void* <-> MyMapObjectClass* when setting or getting the pointers in each slot?
EDIT: So here is how I solved it
I changed the ivar declaration as described above. In addition, when setting an entry of my look-up grid, I did this:
// (Done **Only Once** at map initialization)
// _objectArray is an instance of NSMutableArray
MyMapObjectClass* mapObject = [[MyMapObjectClass alloc] init];
// ...configure map object, etc...
// Add to Obj-C array:
[_objectArray addObject:mapObject];
// Add pointer to 2D C array:
_grid[i][j] = (__bridge void*)mapObject;
When accessing the object at (x,y), I do the opposite:
MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];
[object performSomeMethod];
// etc...
When removing the object from the map, I do this:
MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];
[_objectArray removeObject:object];
_grid[x][y] = NULL;
Map objects are created once at the beginning of the game, and removed according to game progress. If I need to replace a map object for another, I would do this:
MyMapObjectClass* oldObject = (__bridge MyMapObjectClass*) _grid[x][y];
// (should mark as weak?)
[_objectArray removeObject:oldObject];
_grid[x][y] = NULL;
MyMapObjectClass* newObject = [[MyMapObjectClass alloc] init];
[_objectArray addObject:newObject];
_grid[x][y] = (__bridge void*)newObject;
Circumventing ARC using casts is generally a bad idea. The better way would be to disable ARC for your map.m (or break out just the lookup part into a separate class).Then do manual memory management inside it with retain / release and the C structures you like, as long as you do it correctly it will work fine and you will be able to call it from other classes, avoiding the overhead of nested NSArrays etc..
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