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".
Related
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.
Every thread I've come across with keywords 'block' and 'self' seem to be in regards to retain cycles. But that's not the issue here...
My question is: Why are changes made to properties of self outside the block visible in the block? I thought values were meant to be 'captured'? That is, what the block references is a copy of the original object?
Here's a bit of code to illustrate my dilemma:
int a = 1;
self.someProperty.name = #"Foo";
[self.someProperty someMethodWithCompletionHandler:^() {
NSLog(#"%d", a);
NSLog(#"%#", self.someProperty.name);
}];
a = 2;
self.someProperty.name = #"Bar";
The output I get is:
1
Bar
Values are captured, that's why a is 1 inside the block.
But your string is an object. The pointer (just an address really) is captured but the value of the string it is pointing to has changed. That's why you see "Bar".
C, and by extension, Objective-C uses by-value semantics. That is, when passing parameters to a function, a copy of the variable's value is passed to the callee. The way to allow the callee to change the original variable that would have been copied is to pass a pointer. The callee now has a way to directly access the memory location referred to by the original variable.
int a = 1; // a is stored at memory location 0xABCD
f(a); // The value of a (1) is passed to f(). This copy is at 0xCDEF.
void f(int a) {
// The value at 0xCDEF is now 10, but 0xABCD (the "original") is untouched.
a = 10;
}
g(&a); // The address of "a" is passed. This is the value 0xABCD.
void g(int *a) {
// This dereferences the pointer and changes the value at that location to 10.
// As the value of the pointer is the address of "a", the original variable "a"
// now has the new value of 10.
*a = 10;
}
Blocks in Objective-C work the same way. The captured values are the same sort that would have been passed as parameters to a function. This means that passing non-pointers will not allow the original variable to change, while passing pointers will allow the memory at those locations to be altered. As objects can only be accessed through pointers, every object variable is a pointer. This allows the block to manipulate the object the same way g() in the example above can manipulate the "original" a variable.
We all know that blocks retain objects they capture. We also know we can avoid this by passing a weak reference to an object into a block. But why it works this way? To retain an object means to increase its retain count by one. Why it makes difference to pass a weak reference? Being weak or strong it will still point to the same object, and this object's retain count will be increased by block. Am I right? So why object's retain count doesn't get increased if we pass a weak reference to an object inside a block? How does it work inside?
Weak references do not increase the retain count, weak references are simply pointers to the object, and if the object no longer exist, then the weak property is set to nil, ARC handles this. I don't believe that the objects retain count will be increased by a weak reference inside the block.
Because a weak reference does not keep a strong hold on the instance it refers to, it is possible for that instance to be deallocated while the weak reference is still referring to it. Therefore, ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated.
Documentation link: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
As far as specific info regarding how ARC works with blocks I found this from apple, which wasn't much help with your question:
https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
However, this paragraph might be helpful in understanding how blocks retain their local variables:
Blocks are called closures in other languages such as Python, Ruby and Lisp, because they encapsulate state when they are declared. A block creates a const copy of any local variable that is referenced inside of its scope.
From: http://www.raywenderlich.com/9438/how-to-use-blocks-in-ios-5-tutorial-part-2
You can sort of think of a block as an object which has an "instance variable" for each captured variable, which is initialized with the value of the corresponding captured variable at the time the block is created.
In ARC, the block's "instance variables" have the same ownership specifier as the corresponding captured variable. So if a captured variable of object-pointer type is __strong (the default), the block's "instance variable" is also __strong, so it retains the object pointed to for the lifetime of the block. If a captured variable of object-pointer type is __strong, the block's "instance variable" is also __weak, hence it is a zeroing weak reference to the object pointed to.
You might want to understand how closure works in general.
Take the following code as an example,
var name = "NSNoName"
NSLog("Original name: %# <%p>", name, name)
let takeName: (String) -> Void -> () = {
name in
return {
NSLog("Name inside block: %# <%p>", name, name)
}
}
let returnName = takeName(name)
name = "NSNoFame"
returnName()
NSLog("Changed name: %# <%p>", name, name)
Initially, the value of name variable is "NSNoName". When I print the name at this time, I get the result,
Original name: NSNoName <0x7f8fb86004a0>
I have a simple closure which takes the string as a parameter. I call the closure with the same name object, as a result the block creates its own copy of the object. Then, I go ahead and change the name object, and now, if I call the block, to print the name, the block has the same original value which was passed to it. But, the object is different, which means that block created a new object with the same value.
Name inside block: NSNoName <0x7f8fb8602510>
The last NSLog, prints a different value since it is already changed and has some different value,
Changed name: NSNoFame <0x7f8fb8603ae0>
This is the reason that you want to tell a block, to create a weak reference to the object, which means if the original object does not exist any more, nil the reference object created inside block.
While with Objective C, it seems to be little bit different though,
#interface TestViewController ()
#property (nonatomic, strong) NSString *name;
#end
#implementation TestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = #"NSNoName";
NSLog(#"Original name: %# <%p>", self.name, self.name);
typedef void(^ReturnNameBlock)();
ReturnNameBlock (^takeName)(NSString*) = ^ReturnNameBlock(NSString *name) {
return ^{
NSLog(#"Name inside block: %# <%p>", name, name);
};
};
ReturnNameBlock returnName = takeName(self.name);
self.name = #"NSNoFame";
returnName();
NSLog(#"Changed name: %# <%p>", self.name, self.name);
}
#end
My Log appears like this,
Original name: NSNoName <0x103ae34c0>
Name inside block: NSNoName <0x103ae34c0>
Changed name: NSNoFame <0x103ae3520>
If you look at the log, the block owns the original self.name object as both have the same, memory address. Although viewController does not own this anymore, when we change the self.name = "NSNoFame", the block still retains the same instance of the object.
The difference in swift and objective is that, Objective C block retains the original instance of the object passed to it, while swift closure creates a copy of the original instance variable.
I'm just getting started on Objective-C and I came across this example on creating a singleton:
+ (BNRItemStore *) sharedStore
{
static BNRItemStore *sharedStore = nil;
if (!sharedStore)
sharedStore = [[super allocWithZone:nil] init];
return sharedStore;
}
I understand what's he's trying to do - which is to return the same instance if it's existing and create a new one if it's not. What bothers me is this line:
static BNRItemStore *sharedStore = nil;
Won't this line reset the sharedStore to a nil value everytime the method is called? I don't see how the method will be able to return the previously existing instance if this line always sets it to nil.
Thanks in advance.
This is an element which Objective-C inherits from standard C. Any variable with static storage duration (which the static type specifier explicitly declares) is only initialized once, and the c standard says that this happens before the program starts.
6.2.4 3) An object whose identifier is declared with external or internal linkage, or with the storage-class specifier static has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
Note that it also mentions that if the variable with static storage duration is of 'pointer type', then it is automatically set to a NULL pointer (which is what nil is), so if you want, you can omit the = nil part of the declaration if you think it improves the readability of your function.
Won't this line reset the sharedStore to a nil value everytime the method is called?
Because sharedStore is static, it will be initialized (the = nil bit) the first time it is called. Subsequent calls will skip these instructions.
I don't see how the method will be able to return the previously existing instance if this line always sets it to nil.
Because it is static the variable and its value will remain in memory after the method exits.
Basically, you can think of this as a global variable, but it is accessible only to +sharedStore.
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.