I enjoy using the new Optional class in Java. Is there an equivalent in Objective C?
I need something that can hold a small value like nil until I try to get its value, at which point it is initialized and has the new value cached for next time I read it. I don't want to check if the object is nil at every point where I try to read its value.
You can lazy load the variable using a getter.
- (MyClass *) something {
if(!_something) {
_something = [MyClass new];
}
return _something;
}
Thus, each time you use instance.something, it will do the checking for you and load the object if it's not there already.
If it's a simple one-liner and you simply don't want to use if, you can skip out the keyword (I hear this is quicker, but can't verify that now):
- (MyClass *) something {
return _something ?: (_something = [MyClass new]);
}
This is very similar to the unwrapping in Swift where myObject?.aValue will return aValue only if myObject != nil. Or the if let statement: if let value = myObject?.aValue
In objective C, there is no specific syntax dedicated to this however you can easily test for existence using simple if statement e.g.: if(myObject). Because Objective-C objects are pointers and the address of a NULL pointer is 0x0 this if statement will evaluate to false if myObject is NULL (or nil if you like).
If you try to read a property of a nil object you will likewise get nil (for properties that are also objects). And if you try to set a nil object's property, nothing will happen.
I like to use the ternery operator as much as possible e.g.:string != nil ? [textField setText:string] : NULL;
As suggested in previous answers you can use lazy instantiation in your specific situation.
Related
Iam in the first phase of Objective-C learning curve, so please bear with me :).
Iam reading Apple documentation and doing exercises there also. Ive come to a problem with initialization of particular object, because I get unexpected (In my opinion) behavior in my Xcode.
To the point (lets make it simple):
STEP 1:
I have declared simple variable from a my custom class named XYZPerson. It looks like this:
XYZPerson *randomPerson;
"If" check for initialized object returned out "Not Initialized" (as expected).
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
STEP 2:
I have allocated the memory for this object using "alloc" word on this variable. As I understand in this phase, memory for this variable gets allocated. It also returns the pointer?
Line of code looks like this:
XYZPerson *randomPerson = [XYZPerson alloc];
Checking for "nil" surprised me: (It executes "else" statement).
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
I can also send messages to this object and they are executed just fine, without calling "init" method.
How can this be? Am I missing something? How can "randomPerson" variable be initialized before calling "init" method on this instance? Why is "init" there in the first place then?
Furthermore, the accepted answer here Difference between ! and nil check on Objective-C object , says that ALL object are set to nil in the alloc method!
Iam coming from Java and there is one way of declaring an object using "new" keyword. Without using "new" the instance of object will be always "null", and calling methods on that instance will result "NULLPOINTEREXPECTION Error".
P.S Is "nil" in objective C, same as "NULL" in Java?
Whole project looks like this:
main method:
XYZPerson *randomPerson = [XYZPerson alloc];
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
**XYZ Person Class:**
#import "XYZPerson.h"
#implementation XYZPerson
-(void)sayHello {
//[self saySomething];
}
-(void)saySomething:(NSString *) greeting {
NSLog(#"%#", greeting);
}
#end
I post an answer additional to that one linked in the comment for two reasons:
A. Your Q is slightly different.
B. I do not confirm with the linked answer in details.
First of all to your additional Qs: Yes, nil is Objectice-C's NULL. But there are some differences:
A. In most cases (using ARC) a reference to a pointer (not the object itself) is initialized with nil.
XYZPerson *person; // Usually automatically set to nil.
B. A message to nil is allowed in Objective-C, does not throw an exception and returns NO, 0, 0.0, nil, or whatever the representation of null resp. zero (if it does not have a null representation like integers) is for that type. Sometimes this is done intentionally and you can rely on that.
Two your main Q:
After creating an instance object with +alloc all instance variable (ivars) are set to NO, 0, 0.0, nil, or whatever the representation of null resp. zero is for that type. You should not set such ivars explicitly to that value.
For example, if the instances of the class XYZPerson has an ivar for the name typed NSString*, the ivar will be nil. So, one might think that an naked -init does not have any meaning, because it does not take parameters and therefore does nothing. But you simply do not know: Maybe something else is done in -init. And, that's probably a surprise for a Java developer, -init returns an object reference, so you cannot know, whether -init replaces the instance object, for example for twin toning. So even you do not see any meaning in it, the first message to an instance object has to be init. (Yes, in many case you would not see any difference, but you do not know, whether there is one or not or will be in future. It is a part of the API contract, so do it.)
In disagree with the linked answer in one point:
Sending +new… instead of +alloc -init is the better way to do it.
A. If you use a simple +new it is correct that it sends +alloc -init in many cases. Therefore it is obvious that this is not better or worse than sending +alloc -init. It is what it does. You always have to have a look at the documentation, whether a naked initialization, using +new or +alloc -init is allowed. But in such a case you likely do not want to do a naked initialization, because it is meaningless.
B. Sometimes it is for the implementor of a class easier to receive a new message to the class object.
I have a lot of code that looks like this:
id myObjectRaw = getObject();
if(![myObjectRaw isKindOfClass:[MyClass class]]) return nil;
MyClass * myObject = myObjectRaw;
...
Here id getObject() can return several kinds of object. However the above code feels clunky to me. Is it safe to write this?
MyClass * myObject = getObject();
if(![myObject isKindOfClass:[MyClass class]]) return nil;
...
The compiler doesn't complain, but I'm not sure that I'm not treading on undefined behaviuor if getObject returns an object not related to MyClass.
(And no, I can't use a super class, or interface, since I dont actually have control over all the classes that get returned.)
You can do it. Nothing undefined. The only danger is that if the type is wrong and you forget to check the type, it may crash due to unrecognized selector exception.
In compiled code, id, MyClass * and NSString * have no difference, they just a pointer to a ObjC object.
Both versions will work. The first feels clunky, but there are problems with the second one as well: Putting something into a variable of a specific type implies knowledge of its type, and checking the class of something that seems to be known already looks redundant. If someone (it might be you) looks at that code next year, he may find the class check superfluous and remove it.
I've been in a similar situation, and I went with a helper method that gives a properly typed result or nil, i.e.
-(Rectangle)getRectangleObject {
id data = getObject();
if ([data isKindOfClass:[Rectangle class]]) return data;
return nil;
}
This simplifies code and communicates the intention clearly.
If you need several different type checks, you can go with several methods, or pass the class to this helper method.
As long as all types of returned objects conform to NSObject protocol (Classes that inherit from NSObject class do) it is safe to use isKindOfClass: method.
So make sure getObject() method only returns objective-c classes that inherit from NSObject
EDIT
While compiler is fine with it, as #Eiko mentions someone reading the code will probably think the isKindOfClass: check is unnecessary. It is better to use the former code to let the reader know that getObject() might also return other types of objects.
When you use id myObjectRaw you are NOT defining what kind of object myObjectRaw is, thus the compiler won't know if MyClass * myObject = getObject(); is a valid operation or not. THe compiler assumes you know what you are doing. If getObject() returns an object that is different than MyClass or it's not a subclass of it your app may crash. This is a runtime error.
If getObject() returns different objects, you should be expecting at least one object of the kind of objects that can be returned. If need to handle different objects, you can always use if-else-if instructions like:
id myObjectRaw = getObject();
if([myObjectRaw isKindOfClass:[MyClass1 class]])
{
MyClass1 objectClass1 = myObjectRaw;
}
else if([myObjectRaw isKindOfClass[MyClass2 class]])
{
MyClass2 objectClass2 = myObjectRaw;
}
However, if the object returned is a MyClass2 object, and this class is a subclass of MyClass1 the first condition will be true. Therefore, the object will be saved as a MyClass1 object. If that's the case you need to establish priorities and put them accordingly in the nested if-else-if statement.
I know in Objective-C and during programming on iOS SDK, pointers are used all the way around.
What is the best way to learn whether a pointer was initialized or not in Objective-C?
Check if it is nil?
CSomeClass *p;
//....
if(p==nil)
??
PS: in other words what are the default values in Objective-C for variables? Pointers?
UPDATE
Actually I have the following situation.
Imagine I have some pointers Pointer *p1, Pointer *p2 in some class. Then imagine someone calls this class, i.e., it is a view and must be displayed. Then in my class I want to check that if none had initialised p1 and p2 (e.g., p1 == nil? p2==nil?) I want to display empty text.
Are these some sort of comparisons done in Objective-C? For example, what are the default values of p1 and p2 if they were not initialised? Do values by default get initialized to something in Objective-C? Maybe to null?
What is the best way to learn whether a pointer was initialized or not
in Objective C? Check if it is nil ???
Yes, you are correct(By initializing, I am assuming that you meant allocation and not actual initialization of setting default properties). You can check for nil if you have declared it as CSomeClass *p; in ARC. In non-ARC, you should initialize it as CSomeClass *p = nil;.
So here you can do it as,
if (p) { //or if (p != nil)
//do your operations
} else { //same as if (!p) or if (p == nil)
//display error message
}
Actually I have following situation. Imagine I have some pointers
Pointer *p1, Pointer *p2 in some class. Then imagine someone calls
this class, i.e., it is a view and must be displayed. Then in my class
I want to check that if none had initialized p1 and p2 (e.g., p1 ==
nil? p2==nil?) I want to display empty text. Are these sort of
comparisons done in ObjC?
Yes, that is fine in Objective C. You can check it as if (p1 && p2) or if ((p1 != nil) && (p2 != nil)). Both are fine. In the else part, you can add the empty text which should be displayed.
For example what are the default values of p1 and p2 if they were not
initialized? Do values by default get initialized to something in
ObjC?? maybe to null?
In ARC, it will be nil. In non-ARC you should equate to CSomeClass *p1 = nil; before doing this or else it will be a dangling pointer with some garbage value.
Here is the documentation on ARC.
Something important to understand here is that Objective-C uses reference counting - this is why the terminology of saying "a pointer is initialized" is a bit problematic.
The way to know if an object even exists (Doesn't mean it's initialized!)
if (!object) {
NSLog(#"Object is nil");
}
If you wish to release an object, it's always best practice to nil it out. This way, others won't send a message to deallocated instance (causes a nasty crash):
[object release],object = nil;
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
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!
}
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.
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?
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.
Here are the possibilities for why this doesn't work:
the label you pass in to checkRedColorText is not the one you think it is.
the comparison result is always coming out the same way.
... 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.
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.