id dynamic type documentation - objective-c

Can someone point me to the specific "id type documentation" ? I've been through the dynamic typing docs, but I want to know how to use the type id. Specifically how to check if id is null.

The id type is directly related to the Objective-C language itself, rather than the Cocoa/Cocoa Touch frameworks as your original tags implied. There is also no dynamic typing involved. Here's a little introduction in Apple's docs.
To answer your specific question, quoted from the above link:
The keyword nil is defined as a null object, an id with a value of 0. id, nil, and the other basic types of Objective-C are defined in the header file objc/objc.h.
nil and NULL are equivalent (zero pointers), and therefore interchangeable.
In a basic if statement, you just do this:
id myId = [[something alloc] init];
// Short for if (myId == nil)
if (!myId) {
// myId is nil
} else {
// myId is not nil
}

See The Objective-C Programming Language — specifically, the chapter on Objects, Classes and Messaging.
In Objective-C, object identifiers are of a distinct data type: id. This type is the general type for any kind of object regardless of class and can be used for instances of a class and for class objects themselves. […]
The keyword nil is defined as a null object, an id with a value of 0. id, nil, and the other basic types of Objective-C are defined in the header file objc/objc.h.
To compare variables by value, you simply use the == operator. So to test for nil, you do:
someVariable == nil

I've not seen it clearly stated in any other answers so I'll say it here:
an id is defined as a pointer to an object.
nil is zero cast as an id - (id)0 - as a result the following code:
NSString * myString = nil;
id idString = myString;
if (idString == nil) NSLog(#"idString == nil");
if (idString == NULL) NSLog(#"idString == NULL");
if (idString == (id)0) NSLog(#"idString == (id)0");
will have the following output:
2011-09-12 07:25:57.297 Sample Project[22130:707] idString == nil
2011-09-12 07:25:57.298 Sample Project[22130:707] idString == NULL
2011-09-12 07:25:57.299 Sample Project[22130:707] idString == (id)0

Sorry if I misunderstood your question, but wouldn't you just do something like:
// given
id sender;
if (sender == nil) {}
Basically you use id to catch any object that might be assigned to it. So you can do something like:
id name = [NSString stringWithString:#"john"];
and now name will be an NSString object, which you can verify by calling [name class].
Take a look at the id section of this page for more information.

Related

Detect if NSNumber is zero, nil or 0

I have a variable in core data. I want to detect for the cases where it is nil zero, null or otherwise does not have a nice value such as 222 or 333.
This should be trivial but I am getting caught up in Objective-C's syntax.
Following code is not working:
if (_item.id!=nil && _item.id!=0) {
//do something
}
Of note id should be an NSNumber.
It is defined as
#property (nonatomic, retain) NSNumber * id;
I should clarify that it is not working when the value logs to console as 0.
Given the way variable types and core data work, I cannot tell you what causes the variable to log to console as '0' but something is causing it to do so. Basically, I want to exclude cases where the value is anything other than a non-zero integer (in mathematical, not computer science terms).
To check the numeric value stored in an NSNumber, you have to call one of the methods which give you a primitive type.
e.g. integerValue, unsignedLongLongValue, doubleValue
To correctly check for nil and a value of 0, you need the following:
if (_item.id != nil && [_item.id intValue] != 0) {
// code here
}
Because sending a message to a nil reference returns 0, you can take a shortcut:
if ([_item.id intValue] != 0) ...
This works because _item.id has to be non-nil to return a non-zero value from intValue.
As this NSManagedObject is of type NSNumber, simply check the intValue.
if (!_item.id.intValue){
//Method will stop in here if the id is nil/0 etc.
}
However, it is not recommended to name a variable id, I suggest you rename it to itemId
In the same way you shouldn't name something 'string', or 'new' etc as these conflict with Apple's own native naming policies
To check the numeric value stored in an NSNumber, you have to call one of the methods which give you a primitive type.
e.g. integerValue, unsignedLongLongValue, doubleValue
To correctly check for nil and a value of 0, you need the following:
if (_item.id != nil && [_item.id intValue] != 0) {
// code here
}
Because sending a message to a nil reference returns 0, you can take a shortcut:
if ([_item.id intValue] != 0)

Logically ANDing NSUInteger and String Type?

I've searched Stackoverflow and other sites, but I can't seem to find this answer.
In Apple Text Editor source, they have at least one routine that does some apparently strange logical ANDing between two non-boolean variables. Casting them as Bools CAN be done, but doesn't make much sense. I'm learning Swift and much less familiar with Objective-C, but for the life of me, I can't figure out how they are trying to achieve the goal stated as "Build list of encodings, sorted, and including only those with human readable names."
Here is the code:
/* Return a sorted list of all available string encodings.
*/
+ (NSArray *)allAvailableStringEncodings {
static NSMutableArray *allEncodings = nil;
if (!allEncodings) { // Build list of encodings, sorted, and including only those with human readable names
const CFStringEncoding *cfEncodings = CFStringGetListOfAvailableEncodings();
CFStringEncoding *tmp;
NSInteger cnt, num = 0;
while (cfEncodings[num] != kCFStringEncodingInvalidId) num++; // Count
tmp = malloc(sizeof(CFStringEncoding) * num);
memcpy(tmp, cfEncodings, sizeof(CFStringEncoding) * num); // Copy the list
qsort(tmp, num, sizeof(CFStringEncoding), encodingCompare); // Sort it
allEncodings = [[NSMutableArray alloc] init]; // Now put it in an NSArray
for (cnt = 0; cnt < num; cnt++) {
NSStringEncoding nsEncoding = CFStringConvertEncodingToNSStringEncoding(tmp[cnt]);
if (nsEncoding && [NSString localizedNameOfStringEncoding:nsEncoding]) [allEncodings addObject:[NSNumber numberWithUnsignedInteger:nsEncoding]];
}
free(tmp);
}
return allEncodings;
}
The line in question contains the "&&." Any guidance would be appreciated.
Objective-C is a strict superset of C, so the same rules for logical
operators apply. In contrast to Swift, which is much more strict with
types, the logical operators in C take arbitrary scalar operands.
(The boolean type bool did not even exist in early versions of C,
it was added with the C99 standard.)
The C standard specifies (see e.g. http://port70.net/~nsz/c/c11/n1570.pdf, which is a draft of the C11 standard):
6.5.13 Logical AND operator
Constraints
2 Each of the operands shall have scalar type.
Semantics
3 The && operator shall yield 1 if both of its operands compare
unequal to 0; otherwise, it yields 0. The result has type int.
In your case, in
if (nsEncoding && [NSString localizedNameOfStringEncoding:nsEncoding])
the left operand has type NSUInteger (which can be unsigned long
or unsigned int, depending on the platform), and the right
operand has type NSString *, which is a pointer type. Therefore
the above expression is equivalent to
if (nsEncoding != 0 && [NSString localizedNameOfStringEncoding:nsEncoding] != 0)
where the zero in the right operand is the null pointer constant
which is usually written as NULL, or nil for Objective-C pointers:
if (nsEncoding != 0 && [NSString localizedNameOfStringEncoding:nsEncoding] != nil)
Some more information how this relates to Swift
Cocoa/Cocoa Touch Objective-C methods which return an object pointer
usually return nil to indicate an error
(compare Handling Error Objects Returned From Methods
in the "Error Handling Programming Guide"). So
[NSString localizedNameOfStringEncoding:nsEncoding] != nil
would mean "no localized name for the encoding could be determined".
The Swift equivalent would be a method returning an optional string,
and you could check the success with
NSString.localizedNameOfStringEncoding(nsEncoding) != nil
However, this does not compile, and here is the reason why: If you option-click on the Objective-C localizedNameOfStringEncoding method
in Xcode to show its declaration then you'll see
+ (NSString * _Nonnull)localizedNameOfStringEncoding:(NSStringEncoding)encoding
Here _Nonnull indicates that the method is not expected to return
nil. This kind of nullability annotations were introduced to
improve the mapping of Objective-C methods to Swift, see for example
"Nullability and Objective-C" in the Swift Blog.
Because of this _Nonnull annotation, the method is imported to Swift
as
public class func localizedNameOfStringEncoding(encoding: UInt) -> String
So testing the return value in Objective-C can be done but makes no
sense because the method always returns a non-nil value.
In Swift the compiler assumes that the return value is never nil
and returns a non-optional String.
The translation of that if-statement to Swift would therefore just be
if nsEncoding != 0 {
// ...
}

How do I test for null in NSDictionary from SBJSON?

I've got an API returning a JSON encoded string of data that returns a real number or "null" as a value. As long as the JSON contains a numeric or string value, everything works as expected. If the key:value pair value is null, the code below crashes.
How do I properly test NSDictionary objectForKey when it's getting a NULL from SBJSON?
When the API returns a null for filetype, the code below crashes at the if() line.
My Objective-C code attempts to test for expected values:
if (1 == [[task valueForKey:#"filetype"] integerValue]) {
// do something
} else {
// do something else
}
The API JSON output:
{"tclid":"3","filename":null,"filetype":null}
The NSLog() output of the NSDictionary is:
task {
filename = "<null>";
filetype = "<null>";
tclid = 3;
}
When transferring data from JSON to a Cocoa collection, the NSNull class is used to represent "no value", since Cocoa collections can't have empty slots. <null> is how NSNull prints itself.
To test for this, you can use someObject == [NSNull null]. It's a singleton -- there's only one instance of NSNull per process -- so pointer comparison works, although you may prefer to follow the usual Cocoa comparison convention and use [someObject isKindOfClass:[NSNull class]].
You're getting the crash because you're sending integerValue to that NSNull object. NSNull doesn't respond to integerValue and raises an exception.
You should first test if there is a value is null, if it is null performing the intValue method may crash your application.
Doing this should do.
if ([[task valueForKey:#"filetype"] isKindOfClass:[NSNumber Class]] && 1 == [[task valueForKey:#"filetype"] integerValue]) {
// do something
} else {
// do something else
}
I hope it helps.

Objective C: how to compare primitive types with objects

I'm trying to write a method that compares 2 objects. The problem I have is: how do I know if a property of a primitive type?
+(BOOL)isObject:(void*)object1 equalTo:(void*)object2
{
if (object1 == nil && object2 == nil)
return TRUE;
if ((object1 != nil && object2 == nil) ||
(object1 == nil && object2 != nil) ||
([object1 class] != [object2 class]))
return FALSE;
if (object1 != object2)
{
u_int count;
Ivar* ivars = class_copyIvarList([object1 class], &count);
for (int i=0; i < count; i++)
{
id v1 = object_getIvar(object1, ivars[i]);
id v2 = object_getIvar(object2, ivars[i]);
if (![ObjectComparer isObject:v1 equalTo:v2])
return FALSE;
}
}
return TRUE;
}
That will work for objects but fail for primitive types. Another thing is I want to pass parameters as something general such as id but does not work for primitives. At least i need to know that it is a primitive type and convert it into id.
Any ideas?
Answering strictly on properties (as in class_copyPropertyList), not on instance variables (as per your current code's class_copyIvarList)...
People usually dodge around the issue by using key-value coding. If you use valueForKey: then the runtime automatically promotes primitives to object types.
If you were to implement that at the Objective-C level you'd get the NSMethodSignature using -methodSignatureForSelector: and check the methodReturnType property, which is in the standard Objective-C type encoding form (ie, to match #encode).
Working directly with the C runtime, I imagine you'd use method_copyReturnType on the getter.
As for instance variables, I'm not sure there's a way to query their type.
Note also that the normal way of handling this is to have objects themselves implement isEqual:. It's part of NSObject so guaranteed always to be defined. Classes themselves should implement the logic they need to perform comparison by value.

How to determine if an object is in an array based on property value / how to use selectors?

I'm still trying to get my head around how selectors and dynamic typing work in objective c. I'm essentially trying to implement this method (shown below in python/pseudocode, same thing really :P)
def isInArray(value, array, test):
for item in array:
if test(item) == value:
return True
return False
test = lambda obj: obj.property
My intended objective-c code was:
+ (BOOL)value:(id)value:
isInArray:(NSMutableArray *)array
usingSelector:(SEL)selector {
for (id item in array) {
if (value == [item selector]) {
return YES;
}
}
return NO;
}
However, this throws a compile error, complaining that I"m comparing two pointer types (id and SEL) in the if statement.
Shouldn't that if statement be comparing the object value with the object returned from running SEL selector on the object arritem? In other words, why does it think that it's comparing an object with a SEL (I don't see what would be returning a SEL there)
Checkout performSelector in NSObject. The conditional would look like,
if (value == [item performSelector:selector])
By using Key Value Coding (KVC), your code could become even simpler. Say you have an array named people, where each person is an object of the class Person. A Person has firstName and lastName properties, and you want to get all people whose first name matches "John" from the people array. You could get an array of all first names, and then lookup the name "John" in that array.
NSArray *firstNames = [people valueForKey:#"firstName"];
if ([firstNames containsObject:#"John"]) {
..
}
Even though we're using valueForKey, it's actually making a call to the firstName method, and not directly accessing the value.
at least one mistake I see in method definition. There are no need to add colon after variable name (in your case value). And you should use performSelector: to make object check with your selector.
+ (BOOL)value:(id)value
isInArray:(NSMutableArray *)array
usingSelector:(SEL)selector {
for (id item in array) {
if ([item performSelector:selector] == value) {
return YES;
}
}
return NO;
}
In some cases you will need use isEqualTo: or isEqualToString: (if you work with strings) instead of == because == will return true if it's the same object.
Also, if you will need just check presence of some object in NSArray, than you could use containsObject: (return true if it is) and indexOfObjectPassingTest: (return index of object or 'NSNotFound')