So I have something like :
- (void) printString:(NSString *)string
{
NSLog(#"%#", string);
}
NSString *string = #"Blach";
[self printString:string];
Would string be passed in by value or reference? What about something like NSError?
NSError *error = [NSError errorWithDomain:someDomain
code:-101
userInfo:someInfo];
-(NSString *) doSomething:(BOOL) val withError:(NSError **)error {
if(!val)
{
*errorPtr = [NSError errorWithDomain:something
code:-101
userInfo:someInfo];
}
}
You can never refer to an Objective-C object by anything other than a pointer, so for all intents and purposes, everything is pass-by-reference. That said, the actual pointer values are passed by value, just like in C.
Everything of every type in Objective-C is pass-by-value only.
The question is invalid because "objects" are not values in Objective-C and therefore cannot be "passed" -- you cannot have a variable or expression whose value "is an object" (a bare object type is not allowed in the language) -- instead, "objects" must always be manipulated behind pointers to objects. Since "objects" are not values and you cannot "pass" them, it is meaningless to ask about whether they are "pass-by-value" or "pass-by-reference".
Objects are passed by reference. The important distinction, to my mind, is whether the called method has a reference to the same object as the caller or if it has a copy. It has a reference to the same object.
If the object is mutable and the called method mutates it, it affects the object that the caller has, too (because it's the same object).
In the case of an NSError** parameter, it is actually the pointer (not an object) which is being passed by reference. A method with such a parameter can actually modify the caller's pointer, making it point to a different object.
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 creating a pointer does not create a Class object.
But it can be send method to execute in another instance method.
I think just a pointer to the object can be asked to execute a method.
Why the "t" can use setter in the method like this image?
This happens because Xcode only checks if declared type of t variable has visible readwrite property named a. It doesn't care if t is actually object or something else at this point. For example, it cannot know if t is object or nil, or anything else in such situation
- (void) method:(One*) t{
t.a = #"Some string";
}
You can call it like this
[two method:nil];
Xcode won't complain. You can even do this
One *t = (One *)[[NSObject alloc] init]; //warning here
t.a = #"Some t"; //Xcode doesn't care, that t is actually NSObject and doesn't respond to setA:; But you get a runtime error
nil in ObjC responds to all selectors - you can call any visible method on nil object and you won't get a warning nor error. Just silently passes by
I'm trying to implement the countByEnumeratingWithState:objects:count: method from the NSFastEnumeration protocol on a custom class.
So far I have it iterating through my objects correctly, but the objects that are returned aren't Objective-C objects but rather the core foundation equivalents.
Here's the part of the code that sets the state->itemsPtr:
MyCustomCollection.m
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *)state
objects: (id __unsafe_unretained *)buffer
count: (NSUInteger)bufferSize {
// ... skip details ...
NSLog(#"Object inside method: %#", someObject);
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)someObject;
// ... skip details ...
}
Then I call the 'for..in' loop somewhere else on like this
SomeOtherClass.m
MyCustomCollection *myCustomCollection = [MyCustomCollection new];
[myCustomCollection addObject:#"foo"];
for (id object in myCustomCollection) {
NSLog(#"Object in loop: %#", object);
}
The console output is:
Object inside method: foo
Object in loop: __NSCFConstantString
As you can see, inside the NSFastEnumeration protocol method the object prints fine, but as soon as it gets cast to id __unsafe_unretained * I lose the original Objective-C corresponding class.
To be honest I'm not quite sure how the (__unsafe_unretained id *)(__bridge void *) casting works in this case. The (__unsafe_unretained id *) seems to cast to match the right type itemsPtr needs. The (__bridge void *) seems to cast to a pointer of type void with __bridge used to bridge the obj-c world to the CF world. As per the llvm docs, for __bridge:
There is no transfer of ownership, and ARC inserts no retain operations
Is that correct?
From my understanding __NSCFConstantString is just the core foundation equivalent of NSString. I also understand that with ARC you need to bridge from Objective-C objects to CoreFoundation equivalents because ARC doesn't know how to manage the memory of the latter.
How can I get this working so that the objects in my 'for..in' loop are of the original type?
Also note that in this case I'm adding NSStrings to my collection but in theory it should support any object.
UPDATE
Rob's answer is on the right track, but to test that theory I changed the for loop to this:
for (id object in myCustomCollection) {
NSString *stringObject = (NSString *)object;
NSLog(#"String %# length: %d", stringObject, [stringObject length]);
}
In theory that should work since the objects are equivalent but it crashes with this error:
+[__NSCFConstantString length]: unrecognized selector sent to class
It almost looks like the objects returned in the for loop are classes and not instances. Something else might be wrong here... Any thoughts on this?
UPDATE 2 : SOLUTION
It's as simple as this: (thanks to CodaFi
state->itemsPtr = &someObject;
You're incorrectly casting someObject. What you meant is:
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)&someObject;
(Let's get rid of those awful casts as well)
state->itemsPtr = &someObject;
Without the address-of, your variable is shoved into the first pointer, which is dereferenced in the loop. When it's dereferenced (basically, *id), you get the underlying objc_object's isa class pointer rather than an object. That's why the debugger prints the string's value inside the enumerator call, and the class of the object inside the loop, and why sending a message to the resulting pointer throws an exception.
Your code is fine the way it is. Your debug output is revealing an implementation detail.
NSString is toll-free-bridged with CFString. This means that you can treat any NSString as a CFString, or vice versa, simply by casting the pointer to the other type.
In fact, under the hood, compile-time constant strings are instances of the type __NSCFConstantString, which is what you're seeing.
If you put #"hello" in your source code, the compiler treats it as a NSString * and compiles it into an instance of __NSCFConstantString.
If you put CFSTR("hello") in your source code, the compiler treats it as a CFStringRef and compiles it into an instance of __NSCFConstantString.
At run-time, there is no difference between these objects in memory, even though you used different syntax to create them in your source code.
I'll provide a simple method and then explain how I see it, if this is incorrect, please let me know and correct me. I feel like I understand 'self' but still doubt my self.
-(NSString *)giveBack {
NSString *string = [NSString stringWithFormat:#"Hi there!"];
return string;
}
-(IBAction)displayIt {
NSString *object = [self giveBack];
[myView setText:object];
}
the "myView" is a UITextView object.
Now as for the 'self'..
I'm basically saying in my -displayIt method that I'm creating a NSString object called 'object' and storing within it a method that returns a string which says "Hi there".
And this method (named 'giveBack') is performed ON the name of my class (whatever I named the project). Is this correct?
No, you are not creating an object called object and then storing a method within it etc. You are creating a variable which can hold a reference to an object and storing within it a reference to an object obtained by calling a method.
[Note: The following assumes you are using automatic memory management (ARC or garbage collection), no mention will be made of reference counts. If you are using manual memoery there is more to consider...]
Adding line numbers to your sample:
1. -(NSString *)giveBack
{
2. NSString *string = [NSString stringWithFormat:#"Hi there!"];
3. return string;
}
4. -(IBAction)displayIt
{
5. NSString *object = [self giveBack];
6. [myView setText:object];
}
Declares giveBack as an instance method of the class, to be invoked it must be called on a particular instance.
The RHS ([NSString stringWithFormat:#"Hi there!"]) calls a class method which creates an object of type NSString and returns a reference, of type NSString *, to that object. The LHS declares a variable (string) which can hold a reference to an NSString object. The assignment (=) stores the reference returned by the RHS into the variable declared by the LHS.
Return the value in string as the result of the method
Declare an instance method called displayIt
RHS: call an instance method (giveBack) on the object instance self - self is a reference to the current object instance when within an instance method (in this case displayIt). LHS: declare a variable, object of type NSString *. Assignment: store the reference to an NSString returned by the method call on the RHS into the variable declared on the LHS.
Call the instance method setText: on the object instance referenced by the variable myView passing it the reference to an NSString found in variable object.
I think, you are generally correct.
But in below mention:
And this method (named 'giveBack') is performed ON the name of my class (whatever I named the project)
I can't understand your meaning.
A class name is just a symbol (that is text for human readers).
Methods of an Objective-C class are indicated by - notation in the beginning of method declaration.
In other words, all method declarations start with - within #implementation CLASS_NAME ... #end block are instance method of CLASS_NAME class.
When we call another instance methods (within a instance method) we use self keyword. Because all Objective C method call must designate target object and, in this case, we are calling ourselves (current CLASS_NAME instance itself). So we use self keyword.
Sorry for my confusing words.. It's harder to explain I thought :-(
you're storing the string returned by 'giveBack', not the method itself. the method is part of the class. 'self' is the instance of the object that you're calling 'giveBack' (and 'displayIt' for that matter) on.
Sometimes I encounter code that has *, sometimes **. Can anyone explain what they mean in Objective C? (I used to be a Java programmer, with experience in C/C++.)
The * denotes that you are using a pointer to a variable, and is most commonly used to store a reference to an Objective-C object, objects which can only live on the heap and not the stack.
Pointers are not a part of Objective-C exclusively, but rather a feature of C (and therefore its derived languages, of which Objective-C is one of them).
If you are questioning the difference between * and **, the first denotes a pointer, whereas the second denotes a pointer to a pointer; the advantage of the latter to the former is that when passing in an object using ** in a method parameter, the method can then change this parameter and the new value is accessible in the calling method.
Perhaps the most common use of ** in Cocoa is when using NSError objects. When a method is called that can return an NSError object on failure, the method signature would look something like this:
- (id)someMethodThatUsesObject:(id)object error:(NSError**)error;
What this means is that the calling function can pass in a pointer to an NSError object, but someMethodThatUsesObject: can change the value of error to another NSError object if it needs to, which can then be accessed by the calling method.
This is often used as a workaround for the fact that functions can only return one value.
A * in Objective-C means exactly the same as in C; and you'll usually see it (or not) in these situations:
// Method signatures:
// Here the asterisk (*) shows that you have a pointer to an NSString instance.
+ (NSString *)stringWithString:(NSString *)aString;
// Method signatures, part two:
// Here the double asterisk (**) signifies that you should pass in a pointer
// to an area of memory (NSError *) where outError can be written.
- (BOOL)writeToURL:(NSURL *) atomically:(BOOL) error:(NSError **)outError;
// Method signatures make for good examples :)
// Here the asterisk is hidden; id is a typedef for void *; and it signifies that
// a pointer to some object of an indeterminate class will be returned
- (id)init;
// And a forth example to round it all out: C strings!
// Here the asterisk signifies, you guessed it, a pointer! This time, it's a
// pointer to the first in a series of const char; terminated by a \0, also known
// as a C string. You probably won't need to work with this a lot.
- (const char *)UTF8String;
// For a bit of clarity, inside example two, the outError is used as follows:
// Here the asterisk is used to dereference outError so you can get at and write
// to the memory it points to. You'd pass it in with:
// NSError *anError;
// [aString writeToURL:myURL atomically:YES error:&anError];
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atom error:(NSError **)outError {
// do some writing, and if it went awry:
if (outError != NULL)
*outError = [NSError errorWithName:#"NSExampleErrorName"];
return NO;
}