How to initialize a string object with null value? - objective-c

Is it possible to initialize NSString object with null value?
Something like:
NSString *strNullTest = [[NSString alloc]initWithString:[NSNull null]];
When I am doing this it's giving warning:
instance method -"null" not found return type default to id.
Does anyone knows the right way to initialize a string object with null?

there is no "null string" exist in objc. why you want a null string?
either you can use an empty string #"" or just use nil
if you want to put the string in a array or dictionary, [NSNull null] is good. all you need to do is to compare the value with [NSNull null] fist by object == [NSNull null]

You cannot initialize an NSString with using the NSNull instance.
It's not clear what you want, exactly. Perhaps you want a nil pointer:
NSString *str = nil;
nil is the same as NULL, but we usually use it where an object pointer is required.
Or maybe you want an empty (zero-length) string:
NSString *str = #"";
This string is a real object (str != nil) but contains no characters.

How about this:
NSObject *obj1 = #"string";
NSObject *obj2 = [NSNull null];
Then to check for null you use isKindOfClass: method.

Related

How to create an inline conditional assignment in Objective-C?

My Pet class has 2 properties: BOOL isHungry and NSNumber *age.
I want to put the properties of Pet myPet into NSMutableDictionary *myMap.
This is my code is Java. I am trying to write an equivalent in Objective-C
myMap.put("isHungry", myPet == null ? null : myPet.isHungry);
myMap.put("age", myPet == null ? null : myPet.age);
This is my current Objective-C version:
[myMap addEntriesFromDictionary:#{
#"isHungry" : myPet ? myPet.isHungry : (NSInteger)[NSNull null],
#"age" : myPet ? myPet.age : [NSNull null],
}];
The error for the second line is the following:
Incompatible operand types ('int' and 'NSNull * _Nonnull')
The compiler stopped complaining about the first line when I added (NSInteger).
If I put the same on the second line, the error goes away, but the compiler complains about the first line again:
Collection element of type 'long' is not an Objective-C object
I am a noob in Obj-C and I am totally lost. I would also like to know the best practice for Obj-C.
Dictionaries in Objective C can only store objects, and only existing objects.
You turn a boolean or arithmetic value like myPet.isHungry into an NSNumber object by writing #(myPet.isHungry). You create an object that can stand in for nil by writing [NSNull null].
When you try to extract a value from a dictionary, you get an object or nil. You check if the object represents nil by checking
if (value == nil || value == [NSNull null])
The second comparison works because there is always ever only one NSNull object.
If you know that the value is an NSNumber object, you can use boolValue or integerValue etc. to extract the value.
Your isHungry is a BOOL. Arrays and dictionaries can store only objects. But BOOL and NSInteger are primitive types and not objects (that's why you get the error). But you can convert it to an object (NSNumber in this case) and add it to a dictionary.
You can convert BOOL value to NSNumber in two ways, by adding # in front of a value or by using numberWithBool:
Example:
NSNumber *isHungry = #(myPet.isHungry); // OR
NSNumber *isHungry = [NSNumber numberWithBool:myPet.isHungry];
You can do it inline so your code will look (and work) like:
[myMap addEntriesFromDictionary:#{
#"isHungry" : myPet ? #(myPet.isHungry) : [NSNull null],
#"age" : myPet ? myPet.age : [NSNull null],
}];
When you retrieve data from the dictionary you'll get an NSNumber you stored before. But you can convert it back to a BOOL if needed.
// getting BOOL back
NSNumber *isHungryObj = myMap[#"isHungry"]; // it must be NSNumber not NSNull!
BOOL isHungry = isHungry.boolValue;
But in the case above you have to be sure that your stored object is actually a NSNumber and not NSNull. Because in the case of NSNull the app will crash because NSNull is not NSNumber and doesn't respond to boolValue.
So to avoid that you'll either:
always have to check the returned object against NSNull (not the best solution, and storing two different types of objects under the same key in a dictionary is not the best practice)
depending on your needs it may be wiser to store instead of NSNull some default values in the case if there's no myPet. Like setting #NO for isHungry and #0 for age
or you can check the existence of myPet before adding values and if it doesn't exist then just don't add anything to myMap. In this case if you don't add anything to myMap, then calling myMap[#"isHungry"] will return nil.
It is another variant of null in Objective-C. It's easier to check for nil than NSNull and nothing bad will happen even if you send some message to nil. In Objective-C sending messages to nil is allowed. You can't store nil in a dictionary as you can do with NSNull, but you can compare objects to nil.
Sample code for the 3rd option:
// adding to a dictionary, does the same thing as your code
if (myPet != nil) // OR if (myPet)
{
myMap[#"isHungry"] = #(myPet.isHungry);
myMap[#"age"] = myPet.age;
}
// retrieving
if (myMap[#"age"])
{
// number exists, you can do something with it
}
And since nil can have messages sent to it without a problem, sometimes you don't even need to check for nil, for example in such case:
if ([myMap[#"age"] integerValue] == 5) // returns YES if it's 5 and NO in any other case even if #"age" wasn't set and is nil
Hope this helps.
As you have a class Pet with #property BOOL isHungry; and #property NSNumber *age; and your myMap is NSMutableDictionary your solution should look like..
Pet *myPet = [[Pet alloc] init];
myPet.age = #(2);
myPet.isHungry = YES;
NSMutableDictionary *myMap = [[NSMutableDictionary alloc] init];
if (myPet!=nil) {
[myMap addEntriesFromDictionary:#{
#"isHungry" : #(myPet.isHungry),
#"age" : myPet.age
}];
}
// with this you store only the values of Pet
NSLog(#"%#",myMap.description);
// but that goes even easier..
NSMutableDictionary *myDict = [NSMutableDictionary new];
myDict[#"Pet1"] = myPet;
NSLog(#"%#",myDict.description);
Pet *petInDict = myDict[#"Pet"];
NSLog(#"age=%# isHungry=%#",petInDict.age, (petInDict.isHungry ? #"YES":#"NO") );
// should be age=(null) isHungry=NO
// because we stored with key myDict[#"Pet1"] and not myDict[#"Pet"]
// ok lets take the key we used
Pet *pet1 = myDict[#"Pet1"];
NSLog(#"age=%# isHungry=%#",pet1.age, (pet1.isHungry ? #"YES":#"NO") );
As there are generic data types that are not subclasses of NSObject you cant store them in dictionarys without making them to objects.
#(yournumber) // converts to NSNumber
#(YES) // converts to NSNumber = 1
#(NO) // converts to NSNumber = 0
#[#(1),#(2),#(3)] // converts to an NSArray with 3 NSNumbers
#{} // this one you know allready, its a NSDictionary
#"hello" // well NSString of course
#selector(name:) // thats a pointer to a method with name:, of type SEL
...
#{#"key1":#YES, #"key2":#NO}
// it is possible to convert BOOL directly
you can also initiate this way, but you see it can become looking strange
NSMutableDictionary *syntaxsugar = [(#{#"isHungry":#(myPet.isHungry), #"age":myPet.age}) mutableCopy];
mutableCopy generates a mutable copy of the leading Datatype which is NSDictionary.

null, nill is string type in Objective-C

//result: ok fine
NSString *email = [dataDic objectForKey:#"email"];
if([email isEqualToString:#"null"])
email = nil;
if((![email length]) == 0)
self.emailLbl.text = email;
// result: not fine
NSString *email = [dataDic objectForKey:#"email"];
if((![email length]) == 0 || (email != nil) )
self.emailLbl.text = email;
In dictionary, email property value contained null value. My question is what is the type of nil? -> if comparison email == nil || email == [NSNull null] ?
There are several values that are different from Objective-C perspective here:
nil (aka null in other languages)
[NSNull null] (a special marker value object)
#"" (empty string)
#"null" (just a string with 4 characters)
If you write your dataDic from your app, and you know that your app handles it well, you don't have to check all the cases. Check only the ones you expect. For example, if your app only writes non-empty strings to the dictionary, but sometimes "email" is not there, you only have to check nil, because objectForKey returns nil if the value is not inside the dictionary.
On the other hand if you have obtained dataDic from a 3rd party API, decoded from JSON for example, then you should do the full checking:
[NSNull null] is placed inside the dictionary if JSON has null originally like {"email":null}
If the server API changed you might get some other structure than NSString there (although quite unlikely here).
nil is returned if you don't have the key/value at all.
You can rule out all the 3 checks at once by doing:
NSString *emailStr = nil;
id emailObj = [dataDic objectForKey:#"email"];
if ([emailObj isKindOfClass:[NSString class]]) {
emailStr = emailObj;
}
Note that you might not have to check for an empty string or nil before assigning to UILabel text, because those work fine and just erase the label text:
self.emailLbl.text = #"";
self.emailLbl.text = nil; // another way to erase
You should check whether the key #"email" contain any value or not like (if its a String)
NSString *email=dic[#"email"];
if (email) {
//do anything with email
}
if you want to check your object is Nil Or Null then do
if (!email || email == (id)[NSNull null]) {
//email unavailable
}
Alternately you can call [Obj isKindOfClass:[NSNull class]] on any object.

reassigning a bool value to NSArray

i have an NSArray with bool values:
NSArray* boolResults = [super foo:values];
how can i change the value in cell 0?
i tried the following:
boolResults[0] = #NO;
this results in an error: Expected method to write array element not found on object of type 'NSArray *'
and also this:
BOOL* b = &[[array objectAtIndex:i] boolValue];
got the following error: Address expression must be an lvalue or a function designator
i don't wish to convert this NSArray to NSMutableArray in order to set this value, is there a normal way to do this?
Thanks
If the array isn't mutable, then you can't change that value. The solutions are two:
Make the array mutable;
Let the array contain mutable objects.
Since you don't want to use a mutable array, I'll make you an example with the second solution. Since there isn't a mutable number in the standard framework, I'll wrap it into NSMutableData. The example supposes that you have an array with a single object, with value #YES, and you want to change it to #NO:
NSNumber* number= #YES;
NSMutableData* data=[[NSMutableData alloc]initWithData: [NSKeyedArchiver archivedDataWithRootObject: number]];
NSArray* array= #[data]; // Now you have an array with a single value
// You want to change the first value to #NO:
number= #NO;
[array[0] setData: [NSKeyedArchiver archivedDataWithRootObject: number]];
No. NSArrays are immutable. You could reassign your pointer to the array with a modified NSArray.
NSArray *anArray = [super foo:values]
NSMutableArray *mutableCopy = [anArray mutableCopy];
// change your mutable copy and then reassign
anArray = [mutableCopy copy];
And just like NSArray, NSNumbers are also immutable, so something like [anArray[0] setBoolValue:NO] does not exist.

Why does fast enumeration not skip the NSNumbers when I specify NSStrings?

I thought that I knew how to use fast enumeration, but there is something I don't understand about it. If I create three NSString objects and three NSNumber objects and put them in an NSMutableArray:
NSString *str1 = #"str1";
NSString *str2 = #"str2";
NSString *str3 = #"str3";
NSNumber *nb1 = [NSNumber numberWithInt:1];
NSNumber *nb2 = [NSNumber numberWithInt:2];
NSNumber *nb3 = [NSNumber numberWithInt:3];
NSArray *array = [[NSArray alloc] initWithObjects:str1, str2, str3, nb1, nb2, nb3, nil];
then I make do fast enumeration on all NSString objects, like this:
for (NSString *str in array) {
NSLog(#"str : %#", str);
}
In the console, I get this result :
2011-08-02 13:53:12.873 FastEnumeration[14172:b603] str : str1
2011-08-02 13:53:12.874 FastEnumeration[14172:b603] str : str2
2011-08-02 13:53:12.875 FastEnumeration[14172:b603] str : str3
2011-08-02 13:53:12.875 FastEnumeration[14172:b603] str : 1
2011-08-02 13:53:12.876 FastEnumeration[14172:b603] str : 2
2011-08-02 13:53:12.876 FastEnumeration[14172:b603] str : 3
I logged only the NSStrings, but I get a line for every object in the array, even the NSNumbers and I don't understand why. Does fast enumeration always use every object contained in an array?
When you write a forin loop like that, it casts every object in the array as an NSString, then prints them out as requested.
If you want only the NSStrings, you would need to write something like this:
for (id obj in array) {
if ([obj isKindOfClass:[NSString class]]) {
NSLog(#"str: %#", obj);
}
}
The for all loop doesn't know the difference between NSStrings and Integers -- it will simply go through the entire array, cast each as an NSString, and print them out as you asked.
I'm pretty sure that fast enumeration returns all objects in the array- all that you're doing in for (NSString *str in array) is typecasting str to an NSString. In the body of the loop you need to check the class of the returned object to make sure that it is an NSString.
for(NSString *str in array)
{
if([str isKindOfClass:[NSString class]])
NSLog(#"str : %#", str);
}
Objective-C is dynamically typed, meaning that at runtime (when the loop actually runs), objects are all effectively one type (id) with different classes. The language allows optional compile-time static typing, but all that does is check whether the messages you're sending are valid for the type you've marked. It doesn't actually change the behavior of your program. If you cast an object to be a different type than it actually is, all you're doing is lying to the compiler and defeating its type-checker.
Every object that descends from NSObject implements the method - (NSString)description, %# in Objective-C formate string will take the corresponding argument for the %# and call its description method, Most subclasses of NSObject will implement there own version of - (NSString)description. The same thing happens when you type
> po anObject
in the debugger.
for (NSString *str in array) {
is a way to enumerate through all the elements in array.
You expectative that by specifying NSString you get only the objects of that type is not correct. Rather, all the objects pointers are cast to that type (NSString*).
Have a look at Fast Enumeration in The Objective-C Programming Language guide.
I don't understand where is the unexpected behavior, using the enhanced for loop in an NSMutableArray will just iterate thru every single object in the array which in your case is 6, the result is correct and expected.
The numbers will just get casted to Strings.
in fast enumeration no typecasting,just assigning the pointer into new object

Check if property of object instance is 'blank'

I am trying to implement the code below without success. Basically, I want to set the display name to use thisPhoto.userFullName if it is not 'Blank", else show thisPhoto.userName instead.
UILabel *thisUserNameLabel = (UILabel *)[cell.contentView viewWithTag:kUserNameValueTag];
NSLog(#"user full name %#",thisPhoto.userFullName);
NSLog(#"user name %#",thisPhoto.userName);
if (thisPhoto.userFullName && ![thisPhoto.userFullName isEqual:[NSNull null]] )
{
thisUserNameLabel.text = [NSString stringWithFormat:#"%#",thisPhoto.userFullName];
}
else if (thisPhoto.userFullName == #"")
{
thisUserNameLabel.text = [NSString stringWithFormat:#"%#",thisPhoto.userName];
}
Currently, even if userFullName is blank, my userName is still not displayed on the screen.
I'd prefer
if([thisPhoto.userFullName length])
Use -length. This will be 0 whenever the string is nil or the empty string #"". You generally want to treat both cases identically.
NSString *fullName = [thisPhoto userFullName];
thisUserNameLabel.text = [fullName length]? fullName : [thisPhoto userName];
I see a few points here
First - if your userFullName instance variable is NSString* then doing simple comparison with nil is enough:
if (thisPhoto.userFullName)
Unless, of course, you explicitly set it to be [NSNull null], which then requires the condition you wrote.
Second - comparing strings is done with isEqualToString: method so second condition should be rewritten as:
if ([thisPhoto.userFullName isEqualToString:#""]) {
...
}
Third - there's logic flaw - If your userFullName IS equal to empty string (#"") the code would still fall to the first branch. I.e. empty string (#"") is not equal to [NSNull null] or simple nil. Hence you should write to branches - one to handle empty string and nil, other one for normal value. So with a bit of refactoring your code becomes like this:
thisUserNameLabel.text = [NSString stringWithFormat:#"%#",thisPhoto.userFullName];
if (!thisPhoto.userFullName || [thisPhoto.userFullName isEqualToString:#""]) {
// do the empty string dance in case of empty userFullName.
}
If, as I suppose, thisPhoto.userFullName is a NSString you may try
[thisPhoto.userFullName isEqualToString:#""]
The other two answers are correct, and beat me to it. Rather than just repeat what they have said - I'll point out something else.
[NSNull null] is used to store nil values in collection classes (NSArray, NSSet, NSDictionary) that don't allow nil values to be stored in them.
So unless you're checking values that you get from a collection - there is no point checking against [NSNull null]
// this assumes userFullName and userName are strings and that userName is not nil
thisUserNameLabel.text = [thisPhoto.userFullName length] > 0 ? thisPhoto.userFullName : thisPhoto.userName;
"Blank" means #"", but also #" " or #"\n". So I would trim userFullName and check the length of that string.
if ([[thisPhoto.userFullName stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) {
// it's blank!
}