Copy Object Values - objective-c

I have a method that receives an array and then stores this in the NSObject properties.
- (void)updatePoints:(NSArray *)pointArrayPassed
{
pointArray = pointArrayPassed;
pointCount= pointArray.count;
}
The following code works but obviously keeps the pointer of pointArrayPassed so when I can that it reflects down the call stack. However if I use a copy of the pointArrayPassed then the app starts to leak heavily!
Is there a way in the function to pass just the values as such instead of the pointer?

You can't just keep sticking copies in an iVar without releasing the current object. Otherwise you've lost the pointer to which you can send the release message - which is why it leaks all over the place.
This is a better replacement.
- (void)updatePoints:(NSArray *)pointArrayPassed
{
if (pointArray == pointArrayPassed) {
//the new array is the same as the current one. Do nothing
return;
}
[pointArray release];
pointArray = [pointArrayPassed copy];
pointCount = pointArray.count;
}
But it's not the most elegant way of doing it.
A better way is to declare pointArray as an property with copy as its memory management semantic (which is obvious as you have a mutable/immutable class cluster). And have a separate method called -pointCount which returns the count when required.

Related

AFNetworking: is it safe to access variable outside block?

I'm trying to debug an issue when storing variable outside a block.
- (void) setObj : (NSString *) abc {
[self postURL:#"..." params:#{"abc" : abc} completionHandler:^(id response) {
[[SharedPref sharedInstance] setX:response];
[[SharedPref sharedInstance] setAbc:abc]; <-- can we safely do this?
} failureHandler:^(SBError *error) {
}];
}
I've seen cases where when in later time I try to access abc, I'm getting empty string.
[[SharedPref sharedInstance] getAbc]; <-- this return empty string
It should be safe as long as the abc property in SharedPref is strong or copy. For NSString* types, it's preferred to use copy.
#interface SharedPref : NSObject
#property (copy,nonatomic) NSString* abc;
#end
The difference is as follows:
strong:
strong indicates that the class owns the property
strong increases the reference count of the property by 1
instance will not be released until its reference count is 0.
copy
copy assigns a shallow copy when assigning the property by calling [copy]
copy ensures that you're always dealing with an immutable property. If a mutable property is passed in, it will copy it. If a immutable property is passed in, it will retain it (you would need to dealloc it).
There is nothing wrong with what you are doing. The local variable abc (which is a pointer to an object) is captured by the block and abc inside the block will be a pointer to the same object. Assuming it's an immutable string or you never mutate the string, it should be the same string that is passed to setAbc:.
Given that postURL: is an asynchronous operation (i.e. the completion block is called at some undetermined later time), I am suspecting that you are making false assumptions about the ordering of operations. The completion block that does setAbc: might not have been called yet by the time you do getAbc, and so what you get is the initial value before you set it.

Autoreleasing blocks in NSMutableArray retained by their creator

I'm trying to write a category based on node.js EventEmitter, which can take a number of blocks, store them weakly in an array, and execute them later if the instance creating the block isn't deallocated (in which case they would be removed from the array). This is in order not to keep filling the array with old, unused blocks.
The problem is that the blocks seem to be copied by the class, and thusly never released, even though the instance creating the block is deallocated.
So the implementation looks something like this;
Usage
[object on:#"change" do:^(id slf, NSArray *args) {
NSLog(#"something changed");
}];
Implementation (WeakReference class found here, courtesy of noa)
- (void)on:(NSString *)eventType do:(Callback)callback
{
NSMutableArray *callbacks = self.emitterEvents[eventType];
__weak Callback wcb = callback;
// Wrap the callback in NSValue subclass in order to reference it weakly
WeakReference *cbr = [WeakReference weakReferenceWithObject:wcb];
callbacks[callbacks.count] = cbr;
}
- (void)emit:(NSString *)eventType withArgs:(NSArray *)objArgs
{
NSInteger idx = 0;
NSMutableIndexSet *indices = [NSMutableIndexSet indexSet];
callbacks = (NSMutableArray *)callbacks;
for (WeakReference *cbv in callbacks) {
__weak id cb = [cbv nonretainedObjectValue];
if (cb) {
Callback callback = (Callback)cb;
__weak id slf = self;
callback(slf, objArgs);
} else {
[indices addIndex:idx];
}
idx++;
}
[callbacks removeObjectsAtIndexes:indices];
}
I read something about blocks being copied when used past their scope, but frankly, reading about all these block semantics is kind of making my head spin right now.
Is this way of approaching the problem even possible?
In Objective-C, blocks are objects, but unlike other objects, they are created on the stack. If you want to use the block outside of the scope it was created you must copy it.
[object on:#"change" do:^(id slf, NSArray *args) {
NSLog(#"something changed");
}];
Here, you are passing a pointer to a block on the stack. Once your current stack frame is out of scope, your block is gone. You could either pass a copy to the block, making the caller the owner of the block, or you could copy the block in the receiver.
If you want the caller to own the block, then you have to keep a strong reference to the block in the caller (e.g. as a property). Once the caller gets deallocated, you lose your strong reference and your weak reference is set to nil.
copy a block which is already copied is same as retain it, so if the caller of the method copy the block first then pass it to the method, it should works as you expected. but this means you cannot simply use the method as you described in your usage section.
you have use it like this
typeofblock block = ^(id slf, NSArray *args) {
NSLog(#"something changed");
};
self.block = [block copy]
[object on:#"change" do:self.block];
to actual solve the problem, you have to figure out owns the block. the caller of on:do:, or the object been called?
sounds to me you want to remove the block when the caller is deallocated, which means the owner of the block is the caller. but your on:do: method does not aware the owner of the block, and cannot remove the block when the caller is deallocated.
one way is to pass the owner of the block into the method and remove the block when it deallocated. this can be done use associate object.
- (void)on:(NSString *)eventType do:(Callback)callback sender:(id)sender
{
// add the block to dict
// somehow listen to dealloc of the sender and remove the block when it is called
}
another way is to add new method to remove the block, and call the method in dealloc or other place to remove the block manually.
your approach is similar to KVO, which require the observer to unregister the observation, and I think is a good practice that you should follow.
Thanks for the answers, I realize I was a little bit off on how blocks are managed. I solved it with a different approach, inspired by Mike Ash's implementation of KVO with blocks & automatic dereferencing, and with xlc's advice on doing it in dealloc.
The approach is along the lines of this (in case you don't want to read the whole gist):
Caller object assigns listener to another object with on:event do:block with:caller
Emitter object creates a Listener instance, with a copy of the block, reference to emitter & the event-type
Emitter adds the copied block to an array inside a table (grouped by event-types), creates an associated object on the caller and attaches the listener
Emitter method-swizzles the caller, and adds a block to its dealloc, which removes itself from the emitter
The caller can then choose to handle the listener-instance, which is returned from the emit-method, if it wants to manually stop the listener before becoming deallocated itself
Source here
I don't know if it is safe for use, I've only tested it on a single thread in a dummy-application so far.

returning an nsmutuableArray as a pointer that is an mutuablecopy

I dont want to return manch because if i autorelease before i return it ,it becomes invalid to others. so i was thinking of this :
classA
-(NSMutableArray*)set:(NSMutableArray*)data
{
manch= [[data mutableCopy]autorelease] ;
int count=2*[data count]; //to not enter infinity loop
for(int k=0;k< count;k=k+2)
{
if(k==count-1)
[manch addObject:[NSNumber numberWithInt:![[manch objectAtIndex:k] integerValue] ] ];
}
data=[manch mutuableCopy];
return data;
}
My goal is to create a class that gets an NSMutuableArray do some calculations, than return it, and NOT TO BE DEPEND on this class anymore .
EDIT :
As people here ask.
in another classB(the user of the method above), i have in the interface :
NSMutuableArray *data ;
and on the .m file init method i have
data=[[NSMutuableArray alloc]init];
to use the function from my question, i do :
mIns=[[classA alloc]init];
data= [mIns set:[self decimalToBinary:autoWord]];
than i loose data later.
I dont want to return manch because if i autorelease before i return it ,it becomes invalid to others. so i was thinking of this:
This is an incorrect statement, you can return an autoreleased object, that's a sane thing to do. It's worth noting that you should design your method names correctly to inform the user what sort of object is returned. Any method whose name begins with alloc, new, copy, or mutableCopy will return a retained object. (Source)
In your case, your method name is set:, which informs the user of this method that it will return a non retained object (almost always an autoreleased object). This is because it isn't prefixed with any of those words mentioned above.
In that case, the issue you have is with the user of the method; they are not retaining a reference to the object being returned. As such, the user of the method should use it as so:
#interface ClassName () {
NSMutableArray* m_ivarArray;
}
#property (nonatomic, retain) NSMutableArray* propertyArray;
#end
NSMutableArray* data = ...;
// If using a property with retain, setting via "self." will retain it for you
self.propertyArray = [self set:data];
// If using an ivar (which doesn't do the retain for you)
m_ivarArray = [[self set:data] retain];
You can avoid these issues by using Automatic Reference Counting (ARC, More Information), which will handle this sort of memory management for you. It is still important that you use the correct naming conventions, as ARC will judge how to manage your memory based on this (in certain situations)
Update: After seeing your update, I can see the problem.
data=[[NSMutuableArray alloc]init];
This is creating a new instance of NSMutableArray, one which is correctly retained (due to what I mentioned before).
data= [mIns set:[self decimalToBinary:autoWord]];
This is replacing the object held in data with a new NSMutableArray, one that is autoreleased. The previous instance you created has been lost, and you've replaced it with another one. This new instance has not been retained, and as such, will be released unexpectedly.
To fix, you need to use this instead:
NSMutableArray* data = [[mIns set:[self decimalToBinary:autoWord]] retain];
You don't need to alloc/init a variable if it will be populated by some other object later on. I strongly suggest brushing up on how this all works, this might be a good start.

Why is this C array NULL when indirectly called in my Objective-C code? (cocos2d)

i have two Objective-C classes, say ParentLayer and ChildLayer. in my child instance, i want to access a C-Array in my parent instance. so i have something like this in my cocos2d code:
#define kNumOfElements 10
#implementation ParentLayer{
int array[kNumOfElements];
}
-(id)init{
//...
for(int i=0;i<kNumOfElements;i++){
array[i] = i;
}
[self addChild:childLayer];
[childLayer initializeValues];
//...
}
-(int *)getArray{
return array;
}
#end
//meanwhile in my child layer...
//...
-(void)initializeValues{
int *arr = [(ParentLayer *)[self parent] getArray];
//NSLog(#"%d",arr[0]); <------- this gives you bad exec access point, and looks like it's 0x00 for memory address
}
//...
what's the proper way to do this?
maybe i dont understand the right memory management behind C Arrays.
i was under the impression that C Arrays didn't need to be allocated,
and that they could be passed by value, on the stack?
also, shouldn't my parent instance still be around? i thought if i
put a C Array as an ivar of my parent, it shouldn't get destroyed
any help is appreciated. thanks!
what's the proper way to do this?
Ideally, you should never pass a C-style array pointer outside of the object that owns it. You open yourself up to all sorts of problems if a piece of code tries to use the array after the object is deallocated, or writes past the end, or something else. It is easier to guarantee that none of this happens if you can make sure the reference never leaves the object's source file.
maybe i dont understand the right memory management behind C Arrays. i was under the impression that C Arrays didn't need to be allocated, and that they could be passed by value, on the stack?
It is not that simple.
A C-style array is just a memory address. That's it. It doesn't carry around the other useful information that an object might, such as number of elements, retain count.
If you declare an array like this:
int array[100];
Then the memory is allocated in either the stack or the heap, depending on where you put the declaration. If it's a local variable inside a function or method, it's on the stack. If it's in global scope or a member variable of an object, it's on the heap.
Furthermore, if it's an instance variable, you're actually setting aside 100 ints worth of memory inside the block of memory allocated to hold the object. It isn't a separate thing.
Since array is just a memory address, you are basically passing it around by reference. Technically, you are passing the address by value, but any changes you make to the memory will be seen by anyone looking at the same address, so it acts like pass by reference.
also, shouldn't my parent instance still be around? i thought if i put a C Array as an ivar of my parent, it shouldn't get destroyed
The way you have coded it, that array will be valid as long as the parent object is around. Once the parent gets deallocated, that memory could be reclaimed. Since the array variable is just a memory address, however, you have no way of knowing whether the data it points to is valid or not. This is the danger of using C-style arrays rather than objects.
Since the last line is giving you NULL (0) address, my guess is that [self parent] is nil. That would put a 0 in arr; when you try to dereference NULL, you will get an exception.
In Objective C, you can use property for this.
#define kNumOfElements 10
#interface ParentLayer: NSObject
{
int *array;
}
#property(nonatomic, assign) int *array;
#end
#implementation ParentLayer
-(id)init{
//...
self.array =(int*)malloc(sizeof(int) * kNumOfElements);
for(int i=0;i<kNumOfElements;i++){
self.array[i] = i;
}
[self addChild:childLayer];
[childLayer initializeValues];
//...
}
//-(int *)getArray{
// return array;
//}
-(void)dealloc
{
if(self.array)
{
free(self.array); self.array = NULL;
}
[super dealloc];
}
#end
-(void)initializeValues{
ParentLayer *player = (ParentLayer *)[self parent] ;
int *arr = player.array;
//NSLog(#"%d",arr[0]); <------- this gives you bad exec access point, and looks like it's 0x00 for memory address
}
can't seem to add a reply to benzado's post. but depending on how to declare your object, it might be automatically deallocated. to ensure that it is retained, use a retain keyword.
[obj retain];
especially using the cocos2d framework, they have quite a number of auto release objects. typically initWith shouldn't be auto release.

Is this if needed?

This method is generated by Xcode 3.2 using "Accessor defs to clipboard"
- (void)setBodyMass:(int)newBodyMass {
if (bodyMass != newBodyMass) {
bodyMass = newBodyMass;
}
}
Could I just as easily write this as you see below? It seems to be doing a conditional test to save it doing a possible redundant assignment.
- (void)setBodyMass:(int)newBodyMass {
bodyMass = newBodyMass;
}
cheers -gary-
Normally you do a check like that in a mutator method because you're working with objects that have to be released. Say you have a mutator method without that check:
- (void)setObject:(MyObject *)anObj
{
[obj release];
obj = [anObj retain];
}
Imagine (for some reason) you have a chunk of code like this that uses that method:
MyObject *o = [MyObject object]; // Auto-released
[anotherObject setObject:o];
[anotherObject setObject:o];
On Line 1, you can assume o has a retain count of 0 (since it's autoreleased). On Line 2, o has been passed to setObject:, which retains it and stores it in the instance variable obj. Since we're working with pointers, o and obj point to the same object in memory, which now has a retain count of 1.
On Line 3, you pass the same object to setObject: again. But right away in that method, you release anObj, which is the same object that both o and obj point to! This means that o, obj, and anObj have a retain count of 0. When you set obj to [obj retain], you're making obj point to an object that has been released already.
This is obviously bad, so when working with mutator methods that deal with objects, you should always use that guard, which effectively checks to see if obj and anObj point to the same object in memory; if they do, nothing happens.
However, this guard isn't necessary in your example, because you're passing an int -- not a pointer -- and ints, of course, never get released (since they're not objects).
I'd do it your way; assigning an int is very cheap. The check makes sense if the assignment is to some large data structure or might have unintended side effects, neither of which is true for int.
Does the assignment cause something to trigger (event)? Doesn't seem so. You can compare but for a simple int I do not think it's an obligation to verify if the value is the same or not. Of course, if you want to display something to the user concerning that he has entering the same value, you might check the value, otherwise, I would not check it.