I am making a chess game in Objective-C. In this game I need to use BOOL* instead of BOOL because I need a pointer to a boolean variable. When I try to use BOOL*, it gives me a warning when I try to do this:
BOOL *isWhiteTurn;
isWhiteTurn = YES;
The warning is:
Incompatible integer to pointer conversion assigning to 'BOOL *' (aka
'signed char *') from 'BOOL' (aka 'signed char')
A pointer is exactly what is sounds like, it points to some other memory.
Lets take this simple example:
BOOL actualVariable = FALSE;
BOOL *pointerVariable = &actualVariable;
That makes pointerVariable point to actualVariable. Using the derefernece operator (unary *) you can get the value of what a pointer points to:
printf("Value of *pointerVariable = %d\n", *pointerVariable);
That should print
Value of *pointerVariable = 0
More graphically you can look at it this way:
+-----------------+ +----------------+
| pointerVariable | ----> | actualVariable |
+-----------------+ +----------------+
You can also use the dereference operator to change the value of where the pointer points:
*pointerVariable = TRUE;
If you declare a pointer, and don't make it point anywhere, then attempting to dereference the pointer (i.e. get what the pointer points to) will result in undefined behavior.
Now regarding your warning. A pointer variable is actually a simple integer, whose value is the address of where it points. That means you can in theory assign any integer value to it, and the program will think that the value is an address of something valid. Most of the time it is not something valid though.
You get the warning because usually using an integer value to initialize a pointer is the wrong thing to do, you should initialize the pointer with another pointer to the same type.
As it became apparent in the comments, you have some function
taking a BOOL * parameter, for example
void foo(BOOL *boolPtr) {
*boolPtr = NO;
}
and you need to pass the address of your BOOL variable to
that function:
BOOL isWhiteTurn = YES;
foo(&isWhiteTurn);
// Now isWhiteTurn == NO
Related
Here is my method createNewRectangleWithHeight and the parameter is heightParam and widthParam. My problem is i can't use the parameter in the method.
I am getting an error something like this
Bad receiver type 'NSUInteger *' (aka 'unsigned int *')
-(BOOL)createNewRectangleWithHeight:(NSUInteger *)heightParam width:(NSUInteger *)widthParam{
if ([[heightParam length] == 0] || [widthParam length]==0]) {
NSLog(#"The height and width must no be 0");
}
}
Error is in if condition
You can only call methods on objects. A pointer to an unsigned int is not an object; it's just the address of a number.
You don't need to pass addresses unless you're changing the value inside your method and you can just check the value rather than treating the number as an object.
-(BOOL)createNewRectangleWithHeight:(NSUInteger)heightParam width:(NSUInteger)widthParam {
if (heightParam == 0 || widthParam == 0) {
NSLog(#"The height and width must not be 0");
}
}
Learn to look at the error messages you get. In this case the error message is telling you exactly what's wrong. The syntax [object message] is sending a message to an object. NSInteger is a scalar type, not an object type.
BTW, your method does not return a result, and is badly named. It should be called something like heightAndWidthAreNotZero.
Others have already pointed out that you should be using (NSUInteger), not (NSUInteger *), as your parameter types. Actually, if you're getting ready to create a CGRect, you should probably be using CGFloat, not NSUInteger, since the different values of a CGRect are CGFloat type.
Finally, there is a built-in system function CGRectIsEmpty() that takes a CGRect as input and returns TRUE if the rectangle is empty, and FALSE if it's not empty.
I'm trying to use CFStringTokenizerCopyBestStringLanguage. The docs say:
The range of string to use for the test. If NULL, the first few hundred characters of the string are examined.
But passing NULL yields an error:
Passing 'void *' to parameter of incompatible type 'CFRange'
What is the correct way of doing this?
NSString *language = (NSString *)CFBridgingRelease(CFStringTokenizerCopyBestStringLanguage((CFStringRef)text, NULL));
It looks like an error in the documentation.
NULL is typically defined as something like
#define NULL ((void*)0)
so it's a pointer.
On the other hand CFRange is defined as
struct CFRange {
CFIndex location;
CFIndex length;
};
typedef struct CFRange CFRange;
so it's a struct, i.e. a non-pointer type.
A struct cannot be assigned to NULL, since they have incompatible types, therefore technically speaking a CFRange cannot be NULL.
Back to your specific problem, you may want to do something like
CFStringRef text = //your text
CFRange range = CFRangeMake(0, MIN(400, CFStringGetLength(text)));
NSString *language = (NSString *)CFBridgingRelease(CFStringTokenizerCopyBestStringLanguage(text, range));
I picked 400 since the documentation states
Typically, the function requires 200-400 characters to reliably guess the language of a string.
UPDATE
I reported the error to Apple.
How do I specify that I want to 'enumerateObjectsAtIndexes:` without any options? When I try to pass nil to the options argument, Xcode gives me this warning. What is the equivalent value that won't cause a complaint?
opts is an enumeration which (in this case) is basically and unsigned int, so pass 0.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/Reference/reference.html#//apple_ref/doc/c_ref/NSEnumerationOptions
Read the NSArray docs on this method, then click the NSEnumerationOptions link in the description.
NSEnumerationOptions
Options for Block enumeration operations.
enum {
NSEnumerationConcurrent = (1UL << 0),
NSEnumerationReverse = (1UL << 1),
};
typedef NSUInteger NSEnumerationOptions;
Clearly it is an NSUInteger bit masked.
0 or one or both of the above, though the NSEnumerationConcurrent is noted to be a hint only and not guaranteed.
nil is not an NSUInteger. Not all method parameters are objects. nil should only be passed when an object parameter is optional.
I have a code as following
ApplicationSetting.h
FOUNDATION_EXPORT BOOL *const TEST_MODE;
ApplicationSetting.m
#ifdef DEBUG
BOOL *const TEST_MODE = YES;
#else
BOOL *const TEST_MODE = NO;
#endif
The above .m file's code gives me this warning
Incompatible integer to pointer conversion initializing 'BOOL *const'
(aka 'signed char *const') with an expression of type 'signed char';
But, if I change it to be come like this
#ifdef DEBUG
BOOL *const TEST_MODE = NO;
#else
BOOL *const TEST_MODE = YES;
#endif
It works just fine without any warning.
Do you have any idea how could this happens?
You really meant to write a value:
FOUNDATION_EXPORT const BOOL TEST_MODE;
…BOOL is not an objc object, it is a signed char.
as far as the error, the compiler complains because you are assigning numeric values to the pointer value -- where only 0 (aka NULL) is acceptable to the compiler, and any other number (YES is 1) will produce the error/warning.
P.S. Just use bool.
Just to explain in more detail:
Incompatible integer to pointer conversion …
You tried to convert an integer value—a number—to a pointer. This can be done, but it's usually a bad idea and consequently requires a high level of explicitness. It's hard to do by accident (nowadays/on this compiler), and there are reasons for that.
… initializing 'BOOL *const' (aka 'signed char *const') …
This is the type of variable you declared. As this part of the message explains, BOOL is also known as signed char (i.e., the one is typedef'd to the other).
char is the smallest of the integer types in C, so you've declared this variable to hold a pointer to an integer.
… with an expression of type 'signed char';
The expression in this case is the initializer from your declaration. It's the part that you changed between the two versions of the declaration: YES in one case, NO in the other.
The Objective-C headers define NO as 0 and YES as 1, both cast to BOOL (which, as noted above, is defined as signed char).
So:
Your initializer is a BOOL value (as justin rightly pointed out, BOOL with no *), which is an integer.
Your variable holds a BOOL *—a pointer.
The compiler will not let this fly without you being very explicit that this is something you mean to do.
Even if you did convince the compiler to go along with this, it would not be correct code.
As justin already established, you should leave out the *. This will declare the variable as holding a BOOL value, not a pointer.
I also second his suggestion of using bool instead. Unlike BOOL, a bool can never be any value except true (1) or false (0), unless you try very hard.
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.