This concept seems to trouble me. Why does an NSError object need its pointer passed to a method that is modifying the object? For instance, wouldn't just passing a reference to the error do the same thing?
NSError *anError;
[myObjc doStuff:withAnotherObj error:error];
and then in doStuff:
- (void)doStuff:(id)withAnotherObjc error:(NSError *)error
{
// something went bad!
[error doSomethingToTheObject];
}
Why doesn't the above work like most other object messaging patterns work? Why must instead we use error:(NSError **)error?
Quite simply:
if you pass a pointer to an object to your function, the function can only modify what the pointer is pointing to.
if you pass a pointer to a pointer to an object then the function can modify the pointer to point to another object.
In the case of NSError, the function might want to create a new NSError object and pass you back a pointer to that NSError object. Thus, you need double indirection so that the pointer can be modified.
The NSError** pattern is used when a method normally returns some value but instead may need to return an error object (of type NSError*) if it fails. In Objective-C a method can only return one type of object, but this is a case where you want to return two. In C-like languages when you need to return an extra value you ask for a pointer to a value of that type, so to return an NSError* you need an NSError** parameter. A more realistic example would be this:
// The method should return something, because otherwise it could just return
// NSError* directly and the error argument wouldn't be necessary
- (NSArray *)doStuffWithObject:(id)obj error:(NSError **)error
{
NSArray *result = ...; // Do some work that might fail
if (result != nil) {
return result;
} else {
// Something went bad!
// The caller might pass NULL for `error` if they don't care about
// the result, so check for NULL before dereferencing it
if (error != NULL) {
*error = [NSError errorWithDomain:...];
}
return nil; // The caller knows to check error if I return nil
}
}
If you only had an NSError* parameter instead of an NSError** then doStuff would never be able to pass the error object back to its caller.
An old question, but still I think its worth putting this here -
The actual culprit is NSError. If you look at its class reference, there are no setter methods for any of its attributes, i.e. domain, code or userInfo. So there is no way, you can just alloc and initialize a NSError, pass it to the method and then populate information on the passed NSError object. (Had there been a setter method, we could have just passed a NSError * and done something like error.code = 1 in the method.)
So in case there is an error, you have to generate a new NSError object in the method and if you are doing so the only way to pass it back to the caller is by having a NSError ** argument. (For the reason explained in the above answers.)
Alternate statement of what n8gray said:
Because you're not receiving an object to send messages to; you're creating the object and returning it. You generally need the pointer-to-an-NSError *-variable argument because you can only use the return statement on one thing at a time, and you're already using it with NO.
I still did not get the full picture by reading all the answers above. The layman exercise I did below, finally helped me understand what is happening. Just putting it out there in case it helps other beginners.
Suppose you have following
#interface Class X
-(void) methodX:(NSMutableArray *)array;
#end
In some other part of the code you have the following sequence
ClassX *objectX = [[ClassX alloc] init];
NSMutableArray *arrayXX = [#[#(1), #(2)] mutableCopy];
//What is stored in arrayXX is the address in the heap at which the NSMutableArray object starts, lets call this address ZZZ
//array starting at address ZZZ in the heap now contains NSNUmbers #1,#2
[objectX methodX:array]
When you invoke [objectX methodX:array], what is being received by the method is a copy of array. Since array contains an address (i.e is a pointer), the copy is special in that what is received is another variable with address ZZZ in it.
So, if methodX does [array removeObjectAtIndex:0], then the object starting at address ZZZ gets affected (now only contains one NSNUmber #(2)). So, when the method returns, the original array also gets affected.
Suppose instead methodX does array = [#[#(2)] mutableCopy]; then original array does not get affected. This is because you did not go into address ZZZ and change something. Instead you overwrote the ZZZ in the copy received by the method to a different address YYY. YYY address is the start of a NSMUtableArray object with one element NSNUmber #(2). The original ZZZ address still contains a NSMUtableArray with two elements. #(1) and #(2). So, when method returns, the original array is unaffected.
Related
I have some code in Swift 3.0 like so for trying to update the property in a array of elements...
for point in listOfPoints {
var pointInFrame : Float = Float(point.position.x * sensorIncomingViewPortSize.width) + Float(point.position.y)
point.status = getUpdateStatus( pointInFrame )
}
However I get a compile error of:
'Cannot assign to property: 'point' is a 'let' constant' [for line 3]
Is there anyway to make the iterator (point) mutable in Swift, like how you can use 'inout' for a function parameter?
Or are you supposed to do this task another way?
Thanks in Advance.
Stan
Just change it to var instead of let where you declare point. let is a constant.
Another way to accomplish this:
for i in 0 ... myStructArray.count - 1 {
var st = myStructArray[i]
st.someStringVariable = "xxx" // do whatever you need with the struct
st.someIntVariable = 123 // do more stuff
// last step (important!):
myStructArray[i] = st // necessary because structs are VALUE types, not reference types.
}
If you only need to make one change, you can omit the steps of defining the local variable (st in the example) as the array element and then afterwards setting the array element as equal to the local variable. But if you're going to make lots of changes to the element, if may be cleaner to make the local variable, make all the changes to it, and then assign that variable back to the array element.
If the array was of a Class rather than a Struct, that last step of assigning back wouldn't be necessary (reference type -- Class, vs value type -- Struct).
I'm writing a Swift program that needs to interoperate with a C library.
This C Library consists of a set of functions that use callbacks.
I resolved the problem of how to pass a Swift func to these C functions, but I'm having difficulties to convert Swift native types to the appropriate C types.
Specifically, I have these 2 callbacks (the signature MUST be this in order to be accepted by C library):
func peer_name_handler_swift_test(p: peer_wr) -> UnsafePointer<CChar>
{
return nil;
}
func peer_ver_handler_swift_test(p: peer_wr) -> UnsafePointer<CUnsignedInt>
{
return nil;
}
Well, despite my efforts, I could not return the correct types from String Swift type and from a simple CUnsignedInt array.
The 2 data I would like to return in these callbacks are these one:
var BLZ_SWIFT_TEST_PEER_VER: [CUnsignedInt] = [0,0,1,0];
var BLZ_SWIFT_TEST_PEER_NAME: String = "test_swift_peer";
Can you help me?
Coercing data into the correct pointer types can be done in various ways. The tricky part to this question is deciding how to manage the memory you are giving out a pointer to.
If you are declaring values known statically at compile time, you could do it like so:
// I _think_ you need to manually null-terminate the string here
let BLZ_SWIFT_TEST_PEER_NAME: StaticString = "test_swift_peer\0"
func peer_name_handler_swift_test(p: peer_wr) -> UnsafePointer<CChar>
{
return UnsafePointer(BLZ_SWIFT_TEST_PEER_NAME.utf8Start)
}
// tuples are the closest approximation to C fixed-size arrays
var BLZ_SWIFT_TEST_PEER_VER
= (0,0,1,0) as (CUnsignedInt,CUnsignedInt,CUnsignedInt,CUnsignedInt)
func peer_ver_handler_swift_test(p: peer_wr) -> UnsafePointer<CUnsignedInt>
{
return withUnsafePointer(&BLZ_SWIFT_TEST_PEER_VER) {
UnsafePointer($0)
}
}
The extra UnsafePointer occurrences in both function bodies are pointer conversion. In the string case, because utf8 is UInt8 but CChar is an alias for Int8. In the array case, because you want a pointer to the first CUnsignedInt rather than a pointer to a 4-tuple.
If you want to change the values at runtime, you need to decide who’s going to create/free the memory, whether you need to allocate multiple bits of memory because the caller of your callback is going to store the pointer and then if you change it, you are going to need to allocate a new bit of memory rather than overwrite the one you already handed out etc., and if so, how you track that in order to free it later to avoid leaking. If you want a single static string, but one that you want to create at runtime, say from a config file, you could do this:
// prior to the callback getting called
let namePtr = strdup(BLZ_SWIFT_TEST_PEER_NAME)
func peer_name_handler_swift_test(p: peer_wr) -> UnsafePointer<CChar>
{
return UnsafePointer(namePtr)
}
// then some time later, if you want to clean up
free(namePtr)
I have a function written in C
FindBeginKey(KeyListTraverser, BeginPage, BeginKey, key1);
BeginKey is a pointer before function invoking, and I didn't initiate it, like
BeginKey = NULL;
In the FindBeginKey() function, I assign BeginKey to another pointer, and try to print out the current address of BeginKey in the function, it works correct.
But when code returns from function, I try to print out the address of BeginKey again, it shows 0x0.
Why does this happen, and if I want to preserve the address assigned in the function, what should I do?
To pass a value out of a function you have to pass by reference rather than by value as is normally the case with C functions. TO do this make the parameter a pointer to the type you want to pass out. Then pass the value into the call with the & (address operand).
e.g.
FindFoo(FOO** BeginKey);
and call it:
FindFoo(&BeginKey);
and in the function:
*BeginKey = 0xDEADC0DE;
From what I understand, you are calling the function like:
FindBeginKey(KeyListTraverser, BeginPage, BeginKey, key1);
However, when you try to write at the BeginKey address, you're basically passing in a pointer to 0x00. Rather, you need to pass a pointer to BeginKey.
FindBeginKey(KeyListTraverser, BeginPage, &BeginKey, key1);
If this is isn't what you meant, it would certainly help if you posted a code sample.
If you want to modify a parameter in a subroutine, you should pass a pointer of the thing you wanna modify.
void subroutine(int* x) {
*x = 5; // will modify the variable which x points to
x = 5; // INVALID! x is a pointer, not an integer
}
I don't know what all the C parameter passing rules are now, so this answer might be a little dated. From common practice in building applications and libraries that those applications called, the return from a C function would contain status, so the caller of the function could make a decision depending on the status code.
If you wanted the function to modify its input parameters, you would pass those parameters by reference &my_val, where int my_val;. And your function must dereference my_val like this *my_val to get its value.
Also, for performance reasons, and address (by reference) might be preferable, so that the your application did not bother copying the parameter's value into a local variable. That prolog code is generated by the compiler. Single parameters, char, int, and so on are fairly straight forward.
I am so used to C++ that passing by reference in C++ does not require dereferencing. The compiler's code takes care of that for you.
However, think about passing a pointer to a structure.
struct my_struct
{
int iType;
char szName[100];
} struct1;
struct my_struct *pStruct1 = &struct1;
If the structure contains lookup data that is filled in once on initialization and then referenced throughout your program, then pass a pointer to the structure by value pStruct1. If you are writing a function to fill that structure or alter already present data, then pass a pointer to the structure by value. You still get to alter what the structure pointer points to.
If on the other hand you are writing a function to assign memory to the pointer, then pass the address of the pointer (a pointer to the pointer) &pStruct1, so you will get your pointer pointing to the right memory.
I noticed something interesting when using for-in constructs in cfscript: It appears that the variable in struct argument in the for(in) loop is set as an independent variable, and has no reference to its parent array key.
If you run the following code you will see the array doesn't change on output. The variable local.i inside the loop is being assigned the new value, but the array remains unchanged.
function arrayTest()
{
local.foo = ["bar-one","bar-two", "bar-three"];
for (local.i in local.foo)
{
local.i = "I am #local.i#";
// Dump local.i; its output will be 'I am bar-[one[two]] etc...'
}
// Dump local.i; its output will as above
// Dump the array; its keys remain unchanged: "bar-one, bar-two, -bar-three"
writeDump(local.foo);
}
So why is this? I know arrays are passed by reference in CF, but I'm not passing an array here. I'm just using one in a for-in construct. There is a difference, no?
It's a bit misleading to call the argument variable in structure. I see local.i as a shortcut to local.foo[ local.i ]. It sounds like the var is indeed the array key and we can modify it. The solution here is to use a plain for() loop, instead.
I would not expect the underlying array to change unless i was a complex object of some sort or something passed by reference. For example if foo were
local.foo = [{a="b"},{a="c"}];
then modifying local.i.a = "I am key #local.i.a#"; would modify the object within the array, and those changes would be reflected in your dump.
Update:
Ultimately this comes down to pointers or references. In loose terms, local.i is just a pointer to objects within the array. So resetting local.i just points that variable at some other object in memory. It has no impact on the array. Notice the change in hashcode value?
// example
local.foo = [ "bar-one" ];
for (local.i in local.foo)
{
WriteOutput("local.i (before) =#local.i.hashCode()#<br>"); //-335192660
WriteOutput("local.foo[1] = #local.foo[1].hashCode()#<br>");//-335192660
local.i = "I am key #local.i#";
WriteOutput("local.i (after) = #local.i.hashCode()#<br>"); //1075915694
}
writeDump(local.foo);
I have the following problem with a program which I wrote in Visual C++ and I hope that anyone can help me please:
typedef struct spielfeld
{
int ** Matrix;
int height;
int width;
Walker walker;
Verlauf history;
} Spielfeld;
void show(Spielfeld fieldToShow); //Prototype of the Function where I have this
//problem
int main(int argc, char *argv[])
{
int eingabe;
Spielfeld field;
//Initialize .. and so on
//Call show-Function and pass the structure with Call by Value
show(field);
//But what's happened? field.Matrix has changed!!
//can anyone tell me why? I don't want it to become changed!
//cause that's the reason why I pass the field as Call by Value!
}
void show(Spielfeld fieldToShow)
{
//Here is the problem: Alltough the parameter fieldToShow has been passed
//with call by value, "fieldToShow.Matrix[0][0] = 1" changes the field in
//main!!
fieldToShow.Matrix[0][0] = 1;
//Another try: fieldToShow.walker.letter only affects the local fieldToShow,
//not that field in main! That's strange for me! Please help!
fieldToShow.walker.letter = 'v';
}
When you pass the structure in, you are passing it in by value. However, the matrix within it is implemented as a pointer to pointer to int. Those pointers are references, and so when you modify the value referenced by them in your function, the same value is referenced by the original structure in main.
If you want to pass these objects by value, you need to do a deep copy yourself, in which you allocate a new matrix, and copy all of the values from the original matrix into it.
As Drew points out, in C++, the preferred way to implement that deep copy is via a copy constructor. A copy constructor allows you to perform your deep copy any time your object is passed by value, without having to explicitly copy the object yourself.
If you are not ready for classes and constructors yet, you can simply write a function, perhaps Spielfeld copySpielfeld(Spielfeld original), that will perform that deep copy; it will essentially be the same as your initialization code that you elided in your example, except it will take values from the Spielfeld passed in, instead of creating a new Spielfeld. You may call this before passing your field into the show function, or have the show function do it for any argument passed in, depending on how you want your API to work.
You're copying the pointer when you pass fieldToShow. Pass-by-value does not perform a deep copy, so both the Spielfeld in an invocation of show(...) and main(...) (although distinct) have the same value for Matrix.
Fixing this is non-trivial. Probably the easiest thing to do would be to change show(...) to pass-by-reference (using a Spielfeld* basically) and make an explicit copy at the start of the function.
When your Spielfeld object is copied:
The copy has its own "walker", which is a copy of the original's "walker". Since walker is a struct, that means you have two structs.
The copy has its own "Matrix" member, which is a copy of the original's "Matrix" member. But Matrix is a pointer, which means you have two pointers. A copy of a pointer points to the same thing the original points to.
So, modifications to the contents of the copy's walker don't affect the original, because they have different walkers. Modifications to the contents of the copy's matrix do affect the original, because they share the same matrix.
The structure is begin passed by value, but since it contains a pointer (the matrix) what that pointer is pointing to can be changed by anyone that has access to the structure. If you don't want this to happen, you can make the pointer const.
As interesting trivia: this is how call by value works in java. Object references are always passed by value. If you manipulate the objects to which these references point tough it will feel like call by reference happened.
Has really nothing to do with your question but maybe you find that interestring.
Happy hacking