Invalid Operands to Binary Expression (Int and NSNumber *) - objective-c

I'm learning objective C from CodeSchool and there's one section of learning OOP which I don't understand why it won't work but seems so simple to fix.
The Code:
- (void) decreaseBatteryLife:(NSNumber *)decreaseBy
{
self.batteryLife = #([self.batteryLife intValue] - decreaseBy);
}
The error which points to the minus symbol before decreaseBy:
^invalid operands to binary expression ('int' and 'NSNumber *')

"decreaseBy" is a NSNumber object while the other value is a "int". There's a difference (the first is an Objective C object, the other is a raw C type).
You need to get the raw "intValue" of your "decreaseBy" number.
Something like this:
self.batteryLife = #([self.batteryLife intValue] - [decreaseBy intValue]);

Related

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 {
// ...
}

Objective-C can't convert NSNumber to int

I have a simple code that will iterate through an array of integers but when I tried using a "for in" loop, it said I needed to have an object so I used an NSNumber but in order for my code to work, it had to be an int so after the loop, I had it convert the NSNumber to an int. It gave me another error saying I need to declare the variable _strong for it to work so I did that but now it gives me these errors: "Incompatible pointer to integer conversion sending 'NSNumber *_strong' to parameter of type 'int'" and "Implicit conversion of 'int' to 'NSNumber *' is disallowed with ARC" Here is the code that gives an error:
for(__strong NSNumber *i in factors) {
i = [i intValue];
You've already declared "i" as an Objective C object (specifically a NSNumber object).
If you want to do the conversion, declare a separate and different variable, e.g.
int j;
and set your integer to that, like:
j = [i intValue];

What does the "at sign" # mean in this objective C code

I have been using Objective C for quite a few years but I didn't know # sign can be used like this (line 6 inside the for loop):
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeInteger:mti forKey:#"mti"];
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:N];
for (int i = 0; i < N; i++)
[arr addObject:#(mt[i])];
[coder encodeObject:arr forKey:#"mt"];
}
What does it mean? surprisingly I can remove it and the compiler does not complain and the code looks like working fine?!
This is part of MTRandom https://github.com/preble/MTRandom/blob/master/MTRandom/MTRandom.m#L115
In this context, the # operator converts a C numeric value (int, long, float, double, etc) into an instance of NSNumber. It's most often used with numeric literals (eg #3.5), but also applies to expressions as in your example.
This enhancement to the Objective-C language was introduced with Xcode 4.4.
It's a new syntax for boxing values with less typing. Assuming mt[i] is a numeric type, #(mt[i]) places it in an NSNumber object.
This is new to objective-C it turns the primitive integer into an NSNumber, there are also equivalents for NSArrays #(..) and NSDictionary #{...}

Objective-C: How to check if a variable is an object, a struct or another primitive

I want to write a function or a directive like NSLog() that takes any kind of variable, primitives and objects. In that function I want to distinguish those.
I know how it works for objects:
- (void)test:(id)object {
if ([object isKindOfClass:[NSString class]])
...
but how do I distinguish objects from structs or even integer or floats.
Something like:
"isKindOfStruct:CGRect" or "isInt"
for example?
Is this possible?
I thought since you can send everything to NSLog(#"...", objects, ints, structs) it must be possible?
Thanks for any help!
EDIT
My ultimate goal is to implement some kind of polymorphism.
I want to be able to call my function:
MY_FUNCTION(int)
MY_FUNCTION(CGRect)
MY_FUNCTION(NSString *)
...
or [self MYFUNCTION:int]...
and in MY_FUNCTION
-(void)MYFUNCTION:(???)value {
if ([value isKindOf:int])
...
else if ([value isKindOf:CGRect])
...
else if ([value isKindOfClass:[NSString class]])
...
}
I know that isKindOf doesn't exists and you can't even perform such methods on primitives. I'm also not sure about the "???" generic type of "value" in the function header.
Is that possible?
#define IS_OBJECT(T) _Generic( (T), id: YES, default: NO)
NSRect a = (NSRect){1,2,3,4};
NSString* b = #"whatAmI?";
NSInteger c = 9;
NSLog(#"%#", IS_OBJECT(a)?#"YES":#"NO"); // -> NO
NSLog(#"%#", IS_OBJECT(b)?#"YES":#"NO"); // -> YES
NSLog(#"%#", IS_OBJECT(c)?#"YES":#"NO"); // -> NO
Also, check out Vincent Gable's The Most Useful Objective-C Code I’ve Ever Written for some very handy stuff that uses the #encode() compiler directive (that) returns a string describing any type it’s given..."
LOG_EXPR(x) is a macro that prints out x, no matter what type x is, without having to worry about format-strings (and related crashes from eg. printing a C-string the same way as an NSString). It works on Mac OS X and iOS.
A function like NSLog() can tell what types to expect in its parameter list from the format string that you pass as the first parameter. So you don't query the parameter to figure out it's type -- you figure out what type you expect based on the format string, and then you interpret the parameter accordingly.
You can't pass a C struct or primitive as a parameter of type id. To do so, you'll have to wrap the primitive in an NSNumber or NSValue object.
e.g.
[self test: [NSNumber numberWithInt: 3.0]];
id is defined as a pointer to an Objective-C object.
#alex gray answer did not work(or at least did not work on iOS SDK 8.0). You can use #deepax11 answer, however I want to point how this 'magic macro' works. It relies on type encodings provided from the system. As per the Apple documentation:
To assist the runtime system, the compiler encodes the return and argument types for each method in a character string and associates the string with the method selector. The coding scheme it uses is also useful in other contexts and so is made publicly available with the #encode() compiler directive. When given a type specification, #encode() returns a string encoding that type. The type can be a basic type such as an int, a pointer, a tagged structure or union, or a class name—any type, in fact, that can be used as an argument to the C sizeof() operator.
To break the macro apart, we first get "typeOf" our variable, then call #encode() on that type, and finally compare returned value to 'object' and 'class' types from encoding table.
Full example should look like:
const char* myType = #encode(typeof(myVar));//myVar declared somewhere
if( [#"#" isEqualToString:#(myType)] || [#"#" isEqualToString:#(myType)] )
{
//myVar is object(id) or a Class
}
else if ( NSNotFound != [[NSString stringWithFormat:#"%s", myType] rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:#"{}"]].location )
{
//myVar is struct
}
else if ( [#"i" isEqualToString:#(myType)] )
{
//my var is int
}
Please note that NSInteger will return int on 32-bit devices, and long on 64-bit devices. Full list of encodings:
‘c’ - char
‘i’ - int
’s’ - short
‘l’ - long
‘q’ - long long
‘C’ - unsigned char
‘I’ - unsigned int
’S’ - unsigned short
‘L’ - unsigned long
‘Q’ - unsigned long long
‘f’ - float
‘d’ - double
‘B’ - C++ bool or a C99 _Bool
‘v’ - void
‘*’ - character string(char *)
‘#’ - object(whether statically typed or typed id)
‘#’ - class object(Class)
‘:’ - method selector(SEL)
‘[<some-type>]’ - array
‘{<some-name>=<type1><type2>}’ - struct
‘bnum’ - bit field of <num> bits
‘^type’ - pointer to <type>
‘?’ - unknown type(may be used for function pointers)
Read more about Type Encodings at Apple
#define IS_OBJECT(x) ( strchr("##", #encode(typeof(x))[0]) != NULL )
This micro works which I got somewhere in stack overflow.
It's important to note that id represents any Objective-C object. And by Objective-C object, I mean one that is defined using #interface. It does not represent a struct or primitive type (int, char etc).
Also, you can only send messages (the [...] syntax) to Objective-C objects, so you cannot send the isKindOf: message to a normal struct or primitive.
But you can convert a integer etc to a NSNumber, a char* to a NSString and wrap a structure inside a NSObject-dervied class. Then they will be Objective-C objects.

Why am I getting an integer to pointer conversion error in objective-c?

I am looping through an NSString object called previouslyDefinedNSString and verifying if the integer representing the ASCII value of a letter is in an NSMutableSet called mySetOfLettersASCIIValues, which I had previously populated with NSIntegers:
NSInteger ASCIIValueOfLetter;
for (int i; i < [previouslyDefinedNSString length]; i++) {
ASCIIValueOfLetter = [previouslyDefinedNSString characterAtIndex:i];
// if character ASCII value is in set, perform some more actions...
if ([mySetOfLettersASCIIValues member: ASCIIValueOfLetter])
However, I am getting this error within the condition of the IF statement.
Incompatible integer to pointer conversion sending 'NSInteger' (aka 'int') to parameter of type 'id';
Implicit conversion of 'NSInteger' (aka 'int') to 'id' is disallowed with ARC
What do these errors mean? How am I converting to an object type (which id represents, right?)? Isn't NSInteger an object?
You want to make it an NSNumber, as in:
NSInteger ASCIIValueOfLetter;
for (int i; i < [previouslyDefinedNSString length]; i++) {
ASCIIValueOfLetter = [previouslyDefinedNSString characterAtIndex:i];
// if character ASCII value is in set, perform some more actions...
if ([mySetOfLettersASCIIValues member: [NSNumber numberWithInteger: ASCIIValueOfLetter]])
Now you're going to have the result you're looking for.
These errors mean that member: expects an object. id is a pointer to an Objective-C object, and instead of an object, you're passing in a primitive type, or scalar (despite its NS- prefix, NSInteger is not an object - just a typedef to a primitive value, and in your case, an int). What you need to do is wrap that scalar value in an object, and specifically, NSNumber, which is a class specifically designed to handle this.
Instead of calling member: with ASCIIValueOfLetter, you need to call it with the wrapped value, [NSNumber numberWithInteger:ASCIIValueOfLetter], as Maurício mentioned.