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.
Related
I'm a bit confused about ARC behaviour when setting variable that is an input pointer, and is expected to remain valid outside function scope.
considering the following example that uses openDirectory framework.
#interface bbb
-(bool)doSomethingWithADRecord:
-(void)obtainADRecord(NSString*)user
-(NSString*)getADrecord:(ODAttributeType)attr fromRecord:(ODRecord*)record;
#end
#interface bbb {
ODRecord *_myRecord;
}
#end
#implementation bbb
-(void)doSomethingWithADRecord:
{
// here we access _myRecord and expect it to be valid.
}
-(bool)obtainADRecord:(NSString*)user
{
...
// here I call the method that will set the member _myRecord from type ODRecord*
// whose scope related to the lifespan of the containing class (bbb)
[self getADrecord:attr toRecord:_myRecord];
}
// the following function should set the variable record to be used by the caller.
-(NSString*)getADrecord:(ODAttributeType)attr fromRecord:(ODRecord*)record {
...
// here a set an ODQuery object.
ODQuery *query = [[ODQuery alloc] initWithNode ...
// queryResults is an array of items from type ODQuery*
NSArray* queryResults = [query resultsAllowingPartial:NO error:&err];
for(ODRecord *item in queryResults) {
if (/*some logic*/)
{
//option 1: just regular set operator, expecting the ARC will do the retain itself
record = item;
//option 2: explicits take a reference on that item.
record = [[item retain] autorelease];
return #"found item";
}
}
}
#end
To Clarify my question, I seek to know which one of the 2 options I stated above is the correct one , in terms of passing the reference to record and eventually to _myRecord, so it will store the correct value even after the temporal list of queryResults will be cleaned.
Notice that in both options I simply setting the pointer value without initiate new object from type ODquery and copying the data to this new object.
thanks !
I'd like to know whether simply doing record = item will be enough for the data pointed by this object to last beyond the scope of the function getADrecord
You are misunderstanding how parameters work; a parameter, such as record, is essentially a local variable which is initialised to the value passed in the call.
Therefore any assignment of an object reference to record will have zero effect on the lifetime of the referenced object outside of the scope of getADrecord as record is local to the function.
To return a value of type T via a parameter the type of the parameter must be of type "pointer to a variable of type T". An example with a simple value type:
- (void) add:(int)value // an int value
to:(int *)ptrToVariable // a pointer to an int variable
{
// note the need to indirect (`*`) through pointer stored in
// `ptrToVariable` to access the pointed at variable
*ptrToVariable = *ptrToVariable + value;
}
int x = 31;
[self add:11 to:&x]; // &x creates a pointer to the variable x
// x = 42 after call
Now you don't want to return a simple value type but a value which is a reference to an object and you wish ARC to manage the lifetime correctly. This is a little more complicated.
Under ARC a variable which holds a reference to an object has both a type and an ownership attribute; this attribute informs ARC how to handle storing references in the variable. The common ownership attributes are __strong and __weak, without an explicit attribute __strong is assumed. So your instance variable declaration is shorthand for:
ODRecord __strong *_myRecord;
This declaration means that for any reference to an ODRecord stored into _myRecord ARC will keep the referenced ODRecord alive at least as long as _myRecord exists and the reference is not overwritten by a different reference or nil. It is "at least as long" as the same reference could be stored elsewhere and these will also effect the lifetime.
Almost there! To return a reference to an ODRecord via a parameter the type of the parameter must be "pointer to a variable of type strong reference to ODRecord, i.e.:
- (NSString *)getADrecord:(ODAttributeType)attr
fromRecord:(ODRecord * __strong *)record
now an assignment such as:
*record = item;
will result in an assignment to the pointed-at variable and as that variable is of type ODRecord __strong * ARC will ensure the referenced ODRecord will live at least as long as a reference to it is stored in the pointed-at variable.
Your call to this method must pass a pointer to your variable:
[self getADrecord:attr toRecord:&_myRecord];
Notes:
"out" parameters are not often used in Objective-C with the notable exception of error returns – these are of type NSError * _autoreleasing * and Apple names this usage as "call-by-writeback".
For a deeper explanation of ARC and returning values via parameters see Handling Pointer-to-Pointer Ownership issues in ARC and NSError and __autoreleasing
Important:
As pointed out by #matt in the comments your code contains retain and autorelease calls which are forbidden in ARC and therefore if your code is compiling you DO NOT have ARC enabled. For new projects ARC will be enabled, for existing projects you may need to enable it your project's Build Settings, the setting is called "Objective-C Automatic Reference Counting".
A call to "autorelease" means the object has an additional retain count that will go away when you leave the current autorelease scope, which is typically when the current event is finished.
record = item is obviously not enough, because record's retain count goes away when records leaves scope, that is when the function returns.
But what you do - calling autorelease for each item makes sure that all the items remain allocated for a while, not just "record".
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.
So the short of it is I want to define a global string variable that I can reference whenever. The function that I reference it in, it returns a string. As soon as I store it and reference it in another function it outputs as <CGPath 0x5bbf50>
What the heck? The code is below and keep in mind this is a module for Titanium.
First, the definition of the global variable..
#interface ComTestModule : TiModule <CBCentralManagerDelegate, CBPeripheralDelegate>
{
NSString * teststring;
}
The next part is the function where I first send the string variable from titanium to xcode..
-(void)setService:(id)args{
ENSURE_ARG_COUNT(args, 2);
teststring = [args objectAtIndex:0];
NSLog(teststring);
}
The output of the NSLog displays the actual string that was passed.
Now the final function where I call the string again and attempt to output it to the log..
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
NSLog(#"---%#", teststring);
}
As I said before, during this step it outputs as ---<CGPath 0x3ef4e0>
I'm really not sure what's going on.. Any help at all about getting this to return as the original string instead of the CGPath would be great!
Effects like this typically happen when you store a pointer to an object that was released and deallocated. The runtime will then replace that chunk of memory that previously held an NSString instance with, in this particular case, a CGPath instance.
If you want to ensure that your NSString stays alive, you need to take ownership of it. You can do that by either retaining it or copying it. Copying is the preferred method when talking about strings, so try replacing this line:
teststring = [args objectAtIndex:0];
With this:
teststring = [[args objectAtIndex:0] copy];
Now just be sure to release it when you're done.
The other poster's suggestion was good. You might want to make your testString variable into a copied property:
In your .h file:
#property (nonatomic, copy) NSString teststring;
And in your .m file
#synthesize teststring;
Then when you assign to it, use code like this:
self.teststring = [args objectAtIndex:0];
That "dot syntax" invokes the setter for your property rather than changing the instance variable directly. Since you declared your property with the "copy" qualifier, the setter method copies the string before putting it into the instance var.
Finally, you would add this code to your dealloc method:
self.teststring = nil;
The setter method also releases any old value in the property before assigning a new value, so setting it to nil releases the old value.
I'm declaring an NSString property in a class and objective-c is complaining that:
NSString no 'assign', 'retain', or 'copy' attribute is specified
It then casually lets me know that "assign is used instead".
Can someone explain to me the difference between assign, retain and copy in terms of normal C memory management functions?
I think it is drawing your attention to the fact that a assign is being used, as opposed to retain or copy. Since an NSString is an object, in a reference-counted environment (ie without Garbage Collection) this can be potentially "dangerous" (unless it is intentional by design).
However, the difference between assign, retain and copy are as follows:
assign: In your setter method for the property, there is a simple assignment of your instance variable to the new value, eg:
- (void)setString:(NSString*)newString
{
string = newString;
}
This can cause problems since Objective-C objects use reference counting, and therefore by not retaining the object, there is a chance that the string could be deallocated whilst you are still using it.
retain: this retains the new value in your setter method. For example:
- (void)setString:(NSString*)newString
{
[newString retain];
[string release];
string = newString;
}
This is safer, since you explicitly state that you want to maintain a reference of the object, and you must release it before it will be deallocated.
copy: this makes a copy of the string in your setter method:
- (void)setString:(NSString*)newString
{
if(string!=newString)
{
[string release];
string = [newString copy];
}
}
This is often used with strings, since making a copy of the original object ensures that it is not changed whilst you are using it.
Cocoa uses reference counting to manage memory. Objects with a reference count of 0 are deleted.
assign - does nothing to reference count simply points your variable to the data
retain - points your variable to data and adds 1 to reference count, data is guaranteed to be there while your variable is still alive
copy - makes a copy of data, points your variable at it and makes the retain count 1
More detail here, at Apple's own documentation.
assign - the ivar is set by doing a simple assignment. Implementation:
- (void) setFoo:(NSString *)newFoo {
foo = newFoo;
}
retain - the ivar is sent the retain message before doing the assignment. Implementation:
- (void) setFoo:(NSString *)newFoo {
if (foo != newFoo) {
[foo release];
foo = [newFoo retain];
}
}
copy - the ivar is sent the copy message before doing the assignment. Implementation:
- (void) setFoo:(NSString *)newFoo {
if (foo != newFoo) {
[foo release];
foo = [newFoo copy];
}
}
I understand that any init... method initializes a new object and that NSString stringWithString makes a copy of the parameter string as a new object. I also understand that being the objects' owner, I can control the release/deallocation of any objects that I allocate. What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
The "Programming in Objective C" book by Kochan (1st ed) uses the following code (see pages 342-344) to explain that the initWithString is preferable to stringWithString because the AddressCard class would own the name variable contents. Also, I don't get any errors making repeated calls to the setName version with the stringWithString method. TIA!!
//header file has appropriate declarations but not included here:
#import "AddressCard.h"
#implementation AddressCard;
-(NSString *) name
{
return name;
}
//Recommended code:
-(void) setName: (NSString *) theName
{
[name release]
name = [[NSString alloc] initWthString: theName];
}
//Incorrect code according to Kochan:
-(void) setName: (NSString *) theName
{
[name release]
name = [NSString stringWthString: theName];
}
//rest of class implementation code snipped
#end
What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
What? No.
The rules are simple:
Any object returned by alloc, copy, copyWithZone, or new has a retain count of 1.
retain increases the receiving object's retain count.
release decreases the receiving object's retain count.
autorelease tells the current autorelease pool to send the receiving object the release message “later”.
Any factory method that doesn't have “new” or “copy” in the name (e.g., stringWithString:) returns an object that it has autoreleased on your behalf.
Or, digested a bit:
Any method whose name contains copy, alloc, retain, or new returns an object that you own.
Any method that doesn't, returns an object that you don't own.
To own an object, retain it.
The incorrect implementation of setName: that you show is incorrect because it stores an autoreleased object in an instance variable, when you mean to own the object. You should retain it or, in this case, copy it. One way is to simply use alloc and initWithString:, as in the correct example you show; the other way would be copy.
The Memory Management Programming Guide for Cocoa explains everything. Every Cocoa or Cocoa Touch programmer should read or re-read it from time to time.
Actually, both setters are wrong. The 'incorrect' one is wrong for general memory management reasons (which are well-expounded elsewhere). The 'recommended' one is wrong for 2 reasons:
if (theName == name), then you're
likely to deallocate your object in
the first line, and then attempt to
use the deallocated object as a
parameter to -initWithString: on the
second line, resulting in undefined
behavior.
-initWithString: does not handle being passed nil gracefully.
The 'correct' (IMHO) method is:
-(void) setName: (NSString *) theName
{
if (theName == name) return; // if they're equal, no need to do anything further
[name release];
name = [theName copy]; // sets name to nil if theName is nil
}
For most objects you'll actually want to -retain instead of -copy on that third line, but for strings it's almost always better to copy.
The difference between initWithString and stringWithString is that stringWithString returns an auto-released pointer. This means that you don't need to release it specifically, since that will be taken care of next time that the auto-release pool cleans up any auto-released pointers.
initWithString, on the other hand, returns a pointer with a retain count of 1 - you do need to call release on that pointer, or else it would result in a memory leak.
See https://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoa for some reasons as why you should use auto-release vs release.
In the Incorrect code above, the next time name is referenced after setName is called, you'll get an exception error, since the object will have been released. You can use either the "Correct" code, or wrap your stringWithString call in an explicit retain call:
name = [[NSString stringWithString: theName] retain];
What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
A string created with stringWithString: isn't owned by the NSString, it is owned by the NSAutoreleasePool (although multiple places can retain an object, making ownership shared).
With stringWithString:, the string will become invalid when the autorelease pool is next processed (normally during the application's next event loop) because the NSAutoreleasePool will release its pointer. If you have not retained the string before then, any pointer you have to it (name in the case of your class) will be invalid (the variable name will still exist but it will point to garbage).
Autorelease is good, if you don't intend to keep any pointers to the NSString but since you do intend to keep a pointer, you'll need to retain the NSString. initWithString: gives you a retain count of 1 automatically.