25

I'm kind of new with objective c and I'm trying to pass an argument by reference but is behaving like it were a value. Do you know why this doesn't work?

This is the function:

- (void) checkRedColorText:(UILabel *)labelToChange {
    NSComparisonResult startLaterThanEnd = [startDate compare:endDate];
    if (startLaterThanEnd == NSOrderedDescending){
        labelToChange.textColor = [UIColor redColor];
    }
    else{
        labelToChange.textColor = [UIColor blackColor];
    }

}

And this is the call:

UILabel *startHourLabel; // This is properly initialized in other part of the code
[self checkRedColorText:startHourLabel];

Thanks for your help

Community
  • 1
  • 1
Rafael
  • 614
  • 2
  • 7
  • 13

5 Answers5

74

Objective-C only support passing parameters by value. The problem here has probably been fixed already (Since this question is more than a year old) but I need to clarify some things regarding arguments and Objective-C.

Objective-C is a strict superset of C which means that everything C does, Obj-C does it too.

By having a quick look at Wikipedia, you can see that Function parameters are always passed by value

Objective-C is no different. What's happening here is that whenever we are passing an object to a function (In this case a UILabel *), we pass the value contained at the pointer's address.

Whatever you do, it will always be the value of what you are passing. If you want to pass the value of the reference you would have to pass it a **object (Like often seen when passing NSError).

This is the same thing with scalars, they are passed by value, hence you can modify the value of the variable you received in your method and that won't change the value of the original variable that you passed to the function.

Here's an example to ease the understanding:

- (void)parentFunction {
    int i = 0;
    [self modifyValueOfPassedArgument:i];
    //i == 0 still!
}

- (void)modifyValueOfPassedArgument:(NSInteger)j {
    //j == 0! but j is a copied variable. It is _NOT_ i
    j = 23;
    //j now == 23, but this hasn't changed the value of i.
}

If you wanted to be able to modify i, you would have to pass the value of the reference by doing the following:

- (void)parentFunction {
    int i = 0; //Stack allocated. Kept it that way for sake of simplicity
    [self modifyValueOfPassedReference:&i];
    //i == 23!
}

- (void)modifyValueOfPassedReference:(NSInteger *)j {
    //j == 0, and this points to i! We can modify i from here.
    *j = 23;
    //j now == 23, and i also == 23!
}
Pier-Olivier Thibault
  • 3,907
  • 2
  • 33
  • 33
  • 34
    Technically, yes, you only pass the primitives by value. However this is entirely misleading in that while the pointer to an object is passed by value, that pointer is pointing to the same object. As there is NO OTHER WAY TO PASS (or deal with) OBJECTS, *objects* are always passed by reference. The only time they behave as if they're not is if OP said labelToChange = nil; or something like that, but that's something everyone here knows wouldn't work and aren't discussing. – Jared Pochtar Oct 22 '11 at 16:49
  • 6
    this is so misleading .... Objects are passed by reference in objective-c.. to add more fun to it question was asked by object and answered with explanation of int that is passed by value. And If thats not enough fun you have 60+ votes to "Objective-C only support passing parameters by value." – hariszaman Jun 30 '17 at 09:38
  • 1
    It's not misleading. It's 100% correct. *You can only pass by value in Objective-C.* Full stop. End of story. Objects *are not* passed by reference. What's passed is a pointer to an object and that pointer is passed by value. The end. These terms have meanings independent of whatever you *think* they should mean. – Wayne Mar 07 '20 at 02:55
  • @Wayne is right. passed by reference, the caller and the callee use the same variable not that it's a reference to an object. Here is a good overview https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value – Kyle C Jul 14 '20 at 00:05
11

Objective-C, like Java, only has pass-by-value. Like Java, objects are always accessed through pointers. "objects" are never values directly, hence you never assign or pass an object. You are passing an object pointer by value. But that does not seem to be the issue -- you are trying to modify the object pointed to by the pointer, which is perfectly allowed and has nothing to do with pass-by-value vs. pass-by-reference. I don't see any problem with your code.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • But I think, it will be different when you're manipulating a mutable objects passed by parameters. : ) The raw types like integer, float, double act a really different way from the object does. – Neal.Marlin Jul 26 '19 at 04:05
  • 1
    @Neal.Marlin: There is no difference semantically between passing mutable and immutable objects. The language doesn't have a concept of "mutability" of objects. An immutable object is just one that happens not to have any methods that change its internal state. – newacct Jul 26 '19 at 18:33
  • 1
    Yes, semantically no different. But as method parameters raw types are implicitly copied, objects are not. Cuz we use a pointer to hold an object. Of course, reference is not surpported in C. Thanks for your explanation. – Neal.Marlin Jul 27 '19 at 01:01
0

In objective-c, there is no way to pass objects by value (unless you explicitly copy it, but that's another story). Poke around your code -- are you sure checkRedColorText: is called? What about [startDate compare:endDate], does it ever not equal NSOrderedDescending? Is labelToChange nil?

Jared Pochtar
  • 4,925
  • 2
  • 29
  • 39
  • In the method, NSOrderedDescending sometimes is true as I expected. The label is no nil. Actually, if I wired labelToChange to startHourLabel it works... It seems to me that is behaving like if I were passing the label as a value and not as a reference :S .. I don't understand.. Thanks for your help! – Rafael May 24 '10 at 07:25
  • 2
    What do you mean by " wired labelToChange to startHourLabel"? You can't wire a method argument to anything except by passing it as a parameter. – JeremyP May 24 '10 at 09:49
  • I mean by wiring to don't use labelToChange and use the startHourLabel variable directly(just for debugging puroposes). The startHourLabel is global and it could be reach in the method. But I want to use the same method for other labels in the view. That's why I added the labelToChange parameter. Thanks! – Rafael May 24 '10 at 17:48
  • gah! Global Variables == BAD. Refactor the code, then come back to this. Basically, I stand by what I said before; it is impossible to pass objects by value in objective-c; therefore this is not the problem. Far more likely it has something to do with your using of a global variable. – Jared Pochtar May 24 '10 at 20:05
  • Actually, I just went back and read the problem and i think @kubi might have been partially right -- does the line 'UILabel *startHourLabel;' actually exist in the same method as the checkRedColorText: call? If so, and it is a global variable, you would be redeclaring the variable in local scope, and basically that means it ignores the previous definition. If so, it would not be pointing to the real label. If the variable is declared globally in the same file as the calling method, just leave out the line redeclaring it. If not, replace it with 'extern UILabel *startHourLabel;' – Jared Pochtar May 24 '10 at 20:13
0

Did you edit out code between this line

UILabel *startHourLabel;

and this line?

[self checkRedColorText:startHourLabel];

If not, the problem is that you're re-declaring your startHourLabel variable, so you're losing any sort of initialization that was there previously. You should be getting a compiler error here.

kubi
  • 48,104
  • 19
  • 94
  • 118
  • Presumably that was for illustrative purposes, because if nothing else, there would (likely) be an EXE_BAD_ACCESS if you did anything with an uninitialized local variable. – Jared Pochtar May 23 '10 at 16:50
  • Hello, thanks. No the startHourLabel is not there, the initialization is other part of the code. I declared it in the interface and I made a constructor for it. I'm not having errors there... – Rafael May 24 '10 at 07:24
0

Here are the possibilities for why this doesn't work:

  1. the label you pass in to checkRedColorText is not the one you think it is.
  2. the comparison result is always coming out the same way.
  3. ... actually, there is no 3.

You claim you initialised startHourLabel elsewhere, but, if it is a label from a nib file, you should not be initialising it at all. It should be declared as an IBOutlet and connected to the label in the nib with interface builder.

If it is not a label in the nib i.e. you are deliberately creating it programmatically, you need to check the address of the label you initialise and check the address of the label passed in to checkRedColorText. Either NSLog its address at initialisation and in checkRedColorText or inspect it with the debugger.

JeremyP
  • 84,577
  • 15
  • 123
  • 161