Check object if it's equal to nil or to [NSNull null] - objective-c

if object set to nil and I want to check for its value.
if object != nil {
// do something
}
or
if object != [NSNull null] {
// do something
}
I guess the second condition will be triggered but I am not sure why.
Can someone explain please?

As others have pointed out, object != nil && object != NSNull.null would give you the expected behaviour, however it might be tedious and error prone to write this pair of conditions every time.
Alternatively you can use inverted logic, by adding an nonNullValue method to virtually almost all objects in the system:
// in some header file
#interface NSObject(NullExtension)
- (instancetype)nonNullValue;
#end
// in some implementation file
#implementation NSObject(NullExtension)
// regular objects are not null (right?)
- (instancetype)nonNullValue {
return self;
}
#end
#implementation NSNull(NullExtension)
// let's make NSNull report as being null
- (instancetype)nonNullValue {
return nil;
}
#end
// adding this just for the sake of completeness, it's highly unlikely
// that JSON's will decode NSProxy instances
#implementation NSProxy(NullExtension)
- (instancetype)nonNullValue {
return self;
}
#end
you can then simply use it on your pointers:
// since we're in Objective-C, a nil pointer will always fail the check
// and thanks to the NSNull extension, NSNull instances will behave the same
if ([object nonNullValue] != nil) {
// NSNull won't get here
// nils won't get here
// other objects will get here
}
This approach is a little bit invasive as it touches all NSObject subclasses, however it eliminates the need of writing multiple conditions.

If you want to make sure object isn't nil and it isn't NSNull null then do:
if (object && object != [NSNull null]) {
// do something with object
} // else it's either nil or NSNull null

The difference between [NSNull null] and nil is that nil is an empty object that has completely disappeared from memory, and we use [NSNull null] when we want to express the idea that "we need a container that has nothing in it," which is "an object whose value is null." If you look up the development documentation you'll see that the class NSNull inherits NSObject and has only one "+ (NSNull *) null;" Class methods. This means that the NSNull object has a valid memory address, so any reference to it in the program will not cause the program to crash.

Related

Checking if [NSObject : AnyObject]! is NSNull in Swift

My project uses both Swift and Objective C. I have a singleton written in Objective C which as a property of the type NSDictionary.
#property(nonatomic, strong) NSDictionary *currentDictionary;
This is an old class that's being used throughout my project. I am trying to use this class in a Swift class like this
if let dict = DataManager.sharedManager().currentDictionary
{
//
}
The problem i am facing is that currentDictionary is being set using data from the server. At times this might be
In Objective C classes, i can handle the situation with the following check
if ([currentDictionary isKindOfClass: [NSNull class]])
{
// Do nothing
}
But i am not sure how to implement a similar check in Swift. I tried the following
if let data = DataManager.sharedManager().currentDictionary as? NSNull
{
return
}
But it doesn't work and also i get a compiler warning :
"Cast from "[NSObject : AnyObject]!" to unrelated type "NSNull" always fails"
This is different from checking for Null values within the dicitonary as they will be 'AnyObject's and i can try casting them into the type i want to check.
Can someone please provide any pointers on how to handle this situation properly
First of all, if the variable can contain something else that NSDictionary, don't set its type to NSDictionary. Swift is type safe and it will trust the declared type.
The easiest workaround would be to make it id in Objective-C.
Then in Swift you can simply:
guard let data = DataManager.sharedManager().currentDictionary as? NSDictionary else {
return
}
If you can't change the original code, just create a Swift accessor with correct type using a category, e.g.
#interface DataManager (Swift)
// solution 1, will be converted to AnyObject in Swift
- (id)currentDictionaryForSwift1;
// solution 2, let's handle NSNull internally, don't propagate it to Swift
- (NSDictionary *)currentDictionaryForSwift2;
#end
#implementation DataManager
- (id)currentDictionaryForSwift1 {
return self.currentDictionary;
}
- (NSDictionary *)currentDictionaryForSwift2 {
if (self.currentDictionary == [NSNull null]) {
return nil;
}
return self.currentDictionary;
}
#end
I would recommend you to handle NSNull internally. There should be no need to for other code to handle nil and NSNull separately.
You could actually solve it already in the getter:
- (NSDictionary *)currentDictionary {
if (_currentDictionary == [NSNull null]) {
return nil;
}
return _currentDictionary;
}
or in the setter
- (void)setCurrentDictionary:(NSDictionary *)currentDictionary {
if (currentDictionary == [NSNull null]) {
_currentDictionary = nil;
} else {
_currentDictionary = currentDictionary;
}
}
As you can see, there are multiple solutions but the best solution should improve even your Obj-C code. The difference between NSNull and nil should be handled locally and not propagated.
If you want to validate wether currentDictionary is nil or not, you can use:
guard let currentDictionary = DataManager.sharedManager().currentDictionary else {
return
}
Replace guard-else statement with if-else if you don't want to return early.
If you want to validate contents of currentDictionary is NSNull or not:
if let value = DataManager.sharedManager().currentDictionary["key"] {
// do something with value
}

How to test if a #property from ObjC object is nil in Swift?

I have a model object written in Objective-C, which has a property of the type NSMutableArray.
It can either be nil or has a valid object reference.
I am using bridging header and I have a few files written in Swift.
In the Swift file I want to iterate through the objects in the array, only if it has something in it.
How can I achieve this?
I have tried things such as:
if let a = MyObj.myArray {
}
if(MyObj.myArray != nil) {
}
if(MyObj.myArray != NSNull()) {
}
I finally got the hang of optionals in Swift code alone, but I am not able to understand the behavior when I am passing around objects written in Objective-C in Swift code.
My Actual Code Looks like this :
Code :
if let values = attribute.values {
for val in values {
print(val);
}
return true;
}
Exception:
-[NSNull countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance
The error message tells us the problem:
-[NSNull countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance
Your attribute.values isn't nil or an NSMutableArray. It's [NSNull null].
NSNull is a real object that's used instead of nil in collections, because the common Foundation collections (NSArray and NSDictionary) cannot store nil as a value.
We most often come across NSNull when decoding a data structure from JSON. When the JSON decoder finds null (a JSON literal) in an array or object, it stores [NSNull null] in the corresponding NSArray or NSDictionary.
Something like this should work:
if let values = attribute.values where values != NSNull() {
for val in values {
print(val);
}
return true;
}
You should use the new Nullability annotation syntax for your Objective-C properties. These annotations help communicate to Swift whether you intend for an object to be nil or not. For example:
#property (nullable) NSMutableArray* myArray;
With these annotations, your Objective-C objects should work just like a native Swift object. You can do an if let or any other nil check.
if let arr = myObject.myArray {
// Do something with arr
}
else {
// Object is nil do something else
}
if myObject.myArray == nil {
// Array is nil, handle it.
}
You can read more about Nullability annotations for Objective-C at the Apple Swift blog.
https://developer.apple.com/swift/blog/?id=25

Passing nil object pointer in Objective-C

I create an object which I set to nil by default before including it in a method as a parameter. I do a check later if the object is still nil or not. The object is set to change in the method but the result is still nil. I'd expect the method to be able to alter the nil object. If I have the code that's inside the method replace the method call, it results in it being NOT nil.
MyObject *objectTemp = nil;
[self methodCallWithObject:objectTemp];
if (objectTemp == nil) NSLog(#"nil");
else NSLog(#"NOT nil");
// always results in "nil"
method:
-(void) methodCallWithObject:(MyObject)objectTemp {
objectTemp = [NSValue valueWithCGPoint:CGPointMake(5.3f, 2.6f)];
}
In order to change objectTemp outside of the method, you have to pass a pointer to objectTemp, which means that methodCallWithObject actually needs to take a pointer to a pointer:
-(void) methodCallWithObject:(MyObject **)objectTemp {
*objectTemp = [NSValue valueWithCGPoint:CGPointMake(5.3f, 2.6f)];
}
(However, it would probably make more sense to have methodCallWithObject just return a new object.)

Inserting nil objects into an NSDictionary

If we have an API that requires only 2 out of an objects 5 properties and iPhone app doesn't require them to instantiate an object, when the object is used in the params NSDicitionary the app will crash. I was told NSDictionary will not let you assign nil values, as when it reaches nil it thinks its finished. Does objective-c have a way to spit out an objects non-nil properties into an NSDictionary?
Example:
[Drunk alloc] init];
drunk.started_drinking = [NSDate date];
drunk.stopped_drinking (we don't set this because he is still a drunk)
drunk.fat = YES;
drunk.dumb = YES;
parameters:#{
#"auth_token" :token,
#"name" : drunk.name, #"date_started" : drunk.started_drinking,
#"date_stopped" : drunk.stopped_drinking,
#"prescribing_doctor" : drunk.fat,
#"pharmacy" : drunk.dumb
}
This will crash when it gets to the stopped_drinking property. Any suggestions on how to handle this?
It's a bit long winded but you could do
static id ObjectOrNull(id object)
{
return object ?: [NSNull null];
}
parameters:#{
#"auth_token" : ObjectOrNull(token),
#"name" : ObjectOrNull(drunk.name),
#"date_started" : ObjectOrNull(drunk.started_drinking),
#"date_stopped" : ObjectOrNull(drunk.stopped_drinking),
#"prescribing_doctor" : ObjectOrNull(drunk.fat),
#"pharmacy" : ObjectOrNull(drunk.dumb),
}
You cannot insert nil into collections (dictionaries, arrays, index sets, etc).
You can, however, insert [NSNull null] into them as this is what they made it for
Inserting objects into the dictionary becomes quite easy (if the property is nil, insert an NSNull instead). Then, when pulling things out of the dictionary, a quick if(myReturnedObject == [NSNull null]) will tell you if the returned value is valid, as NSNull is a singleton and thus every NSNull is in fact the same object.
Edit: Paul.s has an excellent example of insertion behavior for your case, complete with ternary operator usage.
Edit Again: Despite the below comment, it is factually confirmed in the Apple docs linked above that NSNull does not crash when added to collections.

Are NULL and nil equivalent?

Actually my question here is: are null and nil equivalent or not?
I have an example but I am confused when they are equal when they are not.
NSNull *nullValue = [NSNull null];
NSArray *arrayWithNull = [NSArray arrayWithObject:nullValue];
NSLog(#"arrayWithNull: %#", arrayWithNull);
id aValue = [arrayWithNull objectAtIndex:0];
if (aValue == nil) {
NSLog(#"equals nil");
} else if (aValue == [NSNull null]) {
NSLog(#"equals NSNull instance");
if ([aValue isEqual:nil]) {
NSLog(#"isEqual:nil");
}
}
Here in the above case it shows that both null and nil are not equal and it displays "equals NSNull instance"
NSString *str=NULL;
id str1=nil;
if(str1 == str)
{
printf("\n IS EQUAL........");
}
else
{
printf("\n NOT EQUAL........");
}
And in the second case it shows both are equal and it displays "IS EQUAL".
Anyone's help will be much appreciated.
Thank you,
Monish.
nil and NULL are essentially the same, nil is something like (NSObject *)0, while NULL is more like (void *)0. But both are pointers with an integer value of zero. You can send messages to nil without raising an error.
NSNull and NULL (or nil, of course) are different things, however. You just use NSNull as a helper to add an empty object to an NSArray or another container class, since you can't add nil to them. So instead, you use [NSNull null] as a replacement, and you have to check if an array element is NSNull, not if it's nil (it will never be equal to nil).
From http://www.iphonedevsdk.com/forum/iphone-sdk-development/34826-nil-vs-null.html
nil and NULL are 100% interchangeable.
From:
NULL is for C-style memory pointers.
nil is for Objective-C objects.
Nil is for Objective-C classes.
Whenever you're writing Objective-C code, use nil
Whenever you're writing C code, use NULL
But ultimately they're all defined as the same thing -- (void *)0, I think -- so in practice it doesn't really matter.
The concept is the same, with the difference that it's valid to send messages (call method) to nil.
NSNull is a real (singleton) class, that can be used for arrays or dictionnaries, who don't accept NULL or nil values.
Biggest difference between them: sending a message to an NSNULL object is probably going to cause a crash, whereas it's cool to send any message to nil. For example, if you use a key path to get an array, like so:
NSArray *departmentNames = [departments valueForKey:#"name"];
Then you will have an NSNULL object for any department whose name is nil. So, this is going to cause a crash:
for (NSString *name in departmentNames)
NSLog(#"%#", [name lowercaseString]);
whenever name is NSNull, because you just sent an unknown selector (lowercaseString) to an NSNull.
Lesson: check for the NSNull object in an array before sending any message to its elements.
for (NSString *name in departmentNames)
if (name != [NSNull null])
NSLog(#"%#", [name lowercaseString]);
No, NSNull and nil are not the same. They both represent a lack of value, and you might want to treat them the same, but they are still not equal.
The NSNull object instance represents a null value, for example when you read data from a database that has null values.
The nil value is a null pointer, i.e. it doesn't point to any object instance.
In your second code you don't have any NSNull instance. An NSString pointer that contains a null pointer is not an NSNull instance, it's still just a null pointer. You are comparing one null pointer to another, and they are of course equal.
Make sure you typecast [NSNull null] to object type that you are comparing
NSArray list;
if(list==(NSArray *)[NSNull null])
// do something
otherwise you will receive a warning message saying "Comparison of distinct pointer types('type *' and 'NSNull *')