I am trying to set a BOOL within Xcode and for some reason it is plain refusing to work. Nothing else is setting this bool, just this one instance. My code is below:
.h
#interface SuspectsViewController : UIViewController
{
BOOL boolContentChanged;
}
#property (nonatomic) BOOL boolContentChanged;
.m
#synthesize boolContentChanged;
-(IBAction)buttonPressed:(id)sender
{
boolContentChanged = true;
}
I have also tried using self.boolContentChanged but nothing happens either. To try and debug this I used po boolContentChanged and get the following output, the first po is before boolContentChanged = true and the second is after.
(lldb) po boolContentChanged
(BOOL) $4 = '\0' <nil>
(lldb) po boolContentChanged
(BOOL) $7 = '\0' <nil>
Does the $ indicate that it's pointing to a certain address, or is that purely for debugging reference?
Also, is there any reason this would be nil? Surely it doesn't need implicitly setting if it is a bool and not a pointer?
Any advice on this is much appreciated as I can't work it out,
Thanks in advanced,
Elliott
"po" in the debugger (gdb) is short for "print-object". The BOOL type is not an Objective-C object. Use "p" or "print" to display the value of BOOL, int, char, etc.
The dollar-number ("$4") output by the debugger in response to your "po" command is assigning the result to a variable in the debugger which you can use in later commands.
As to the problem you describe, can you confirm that your action method is actually getting invoked? Try adding:
NSLog( #"In %#", NSStringFromSelector( _cmd ));
to your -buttonPressed method. If your action is actually getting invoked, you'll see this in the debugger:
In buttonPressed:
You can also have the NSLog() output the values of your BOOL:
NSLog( #"Before: %d", (int)boolContentChanged );
I finally managed to figure out what was happening. In regards to the debugger it did make a practical difference using YES / NO as opposed to true / false, the values were still being set if I used p to explicitly find them, but when using YES / NO they would show automatically. So once again it seems to be the intellisense that is failing to update in certain situations.
Thanks for the help all.
When you use BOOL you need to use YES or NO not true and false.
Related
It seems that the default behavior in XCode is to silently allow redefinition of local variables if they are declared in a deeper scope, but throw an error or warning otherwise. For example, XCode produces an error for "Redefinition of 'var'" if it is redefined in the exact same scope:
- (void) doStuff
{
NSString *var = #"Hello World";
NSString *var = #"Goodbye"; // Error on this line
}
Similarly, if I have an ivar called 'var', and I try to re-declare 'var' in a local method, XCode will produce a warning for "Local declaration of 'var' hides instance variable" when I try to use it:
//MyClass.h
...
#interface MyClass : NSObject
{
NSString *var;
}
...
//MyClass.m
...
- (void) doStuff
{
NSString *var = #"Hello World";
NSLog(#"%#",var); // Warning thrown on this line
}
So far this is what I would expect. However, if var is redefined in a deeper scope, such as an if block or for loop, XCode allows it, and the outer declaration is silently ignored:
NSString *var = #"Hello World";
if (TRUE)
{
int var = 0;
NSLog(#"%d",var); //prints '0', No errors or warnings
}
NSLog(#"%#",var); //prints 'Hello World'
Why is the last example silently allowed, but the other two are caught? Is there some option or flag I can toggle in XCode so that an error or warning would also be created in the last example? If XCode won't catch it for me, is there some code I could write to make sure variables are never redefined? Or is it just my responsibility to make sure I'm not re-using my variable names?
In the build settings (Xcode 5 & 6, at least) you can set a warning for Hidden Local Variables to YES.
The last example is behavior that Objective-C inherits from standard C. A variable's scope is determined by the bracing level. It's been that way since the earliest days for C. It's called variable shadowing, and it's actually pretty useful in ensuring that code keeps working even in the face of API changes in system libraries.
As far as why it's allowed, but the earlier examples aren't, that's a consequence of how Objective-C implements instance variables. The instance variables are essentially treated as local variables of each of the class's methods. So when you declare a local variable in a function that shadows an instance variable, it gets flagged as an error. Basically the first and second cases are treated as equivalent.
To get a warning for these cases, set the LLVM warning option Hidden Local variables to Yes.
.h
#property (nonatomic,assign) BOOL dontSendDelegate;
.m
#synthesize dontSendDelegate;
- (id) initWithSession:(AVCaptureSession *)aSession outputFileURL:(NSURL
*)anOutputFileURL
{
self = [super init];
if (self != nil)
{
self.dontSendDelegate = NO;
}
return self;
}
if (self.dontSendDelegate == YES)
{
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
Thats all my code in that class relating to the BOOL.
It always prints YES.
What is it that I'm not understanding? I expect it to always print NO.
EDIT
Used Xcode to search 'dontSendDelegate'
It only appears in the code I've shown. Which is copy and paste.
Changed it to an int and assigned 0 instead of the 'NO' and did the comparison == 1 instead of == YES and it works as you would expect. But I'm still lost as to why BOOL was not working.
Thanks for all the help and discussion about the problem.
When you put the mutable part of an expression (your bool instance variable) before a comparison operator such as ==, sometimes you have a typo where you write = instead, so you set the variable instead of compare it. Look for errors of this type.
Or always put the immutable value first, so in the code you have provided so far you would instead write
if (YES == self.dontSendDelegate)
That way, if you ever type one equal sign instead of two, the compiler will complain.
(from comments) When testing boolean variables, you don't need to use == at all. Just use if (self.dontSendDelegate) or if (!self.dontSendDelegate).
#GeorgFritzsche is correct. When this happens I override the setter and create a breakpoint. Then take a look at the stack on the left side of your screen and trace it down to the culprit. Most likely you are changing the value somewhere else in your program.
I want to use a selector on an NSObject instance without the need for an implemented protocol. For example, there's a category method that should set an error property if the NSObject instance it's called on supports it. This is the code, and the code works as intended:
if ([self respondsToSelector:#selector(setError:)])
{
[self performSelector:#selector(setError:) withObject:[NSError errorWithDomain:#"SomeDomain" code:1 userInfo:nil]];
}
However, the compiler doesn't see any method around with the setError: signature, so it gives me a warning, for each line that contains the #selector(setError:) snippet:
Undeclared selector 'setError:'
I don't want to have to declare a protocol to get rid of this warning, because I don't want all classes that may use this to implement anything special. Just by convention I want them to have a setError: method or property.
Is this doable? How?
Another option would be to disable the warning with:
#pragma GCC diagnostic ignored "-Wundeclared-selector"
You can place this line in the .m file where the warning occurs.
Update:
It works also with LLVM like this:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
... your code here ...
#pragma clang diagnostic pop
Have a look at NSSelectorFromString.
SEL selector = NSSelectorFromString(#"setError:");
if ([self respondsToSelector:selector])
It will allow you to create a selector at runtime, instead of at compile time through the #selector keyword, and the compiler will have no chance to complain.
I think this is because for some odd reason the selector isn't registered with the runtime.
Try registering the selector via sel_registerName():
SEL setErrorSelector = sel_registerName("setError:");
if([self respondsToSelector:setErrorSelector]) {
[self performSelector:setErrorSelector withObject:[NSError errorWithDomain:#"SomeDomain" code:1 userInfo:nil]];
}
I realise I'm a bit late to this thread but for completeness, you can globally turn off this warning using the target build settings.
In section, 'Apple LLVM warnings - Objective-C', change:
Undeclared Selector - NO
If your class implements the setError: method (even by declaring dynamic the setter of the eventual error property) you might want to declare it in your interface file ( .h), or if you don't like to show it that way you could try with the PrivateMethods tricky trick:
#interface Yourclass (PrivateMethods)
- (void) yourMethod1;
- (void) yourMethod2;
#end
just before your #implementation , this should hide the warnings ;).
I got that message to go away by #include'ing the file with the method. Nothing else was used from that file.
Another way to avoid this warning is to make sure your selector method looks like this:
-(void) myMethod :(id) sender{
}
Don't forget "(id) sender" if you want to accept any sender or specify a type of a sender object if you prefer.
A really comfortable macro to put in your .pch or Common.h or wherever you want:
#define SUPPRESS_UNDECLARED_SELECTOR_LEAK_WARNING(code) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wundeclared-selector"\"") \
code; \
_Pragma("clang diagnostic pop") \
It's an edit of this question for similar issue...
You can turn it off in Xcode like in the screenshot:
You can also cast the object in question to an id first to avoid the warning:
if ([object respondsToSelector:#selector(myMethod)]) {
[(id)object myMethod];
}
While the correct answer likely lies in informing Xcode through imports or registering the selector that such a selector exists, in my case I was missing a semi-colon. Make sure before you "fix" the error that perhaps, the error is correct and your code isn't. I found the error in Apple's MVCNetworking sample, for instance.
I was able to get the warning to go away by adding thenothing method (disclosure: I didn't think of this but found it by googling on scheduledtimerwithtimeinterval)
[NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
target:self
selector:#selector(donothingatall:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] run];
HTTPLogVerbose(#"%#: BonjourThread: Aborted", THIS_FILE);
}
}
+ (void) donothingatall:(NSTimer *)timer
{
}
While I appreciate knowing how to hide the warning, fixing it is better and neither Sergio's nor Relkin's techniques worked for me, for unknown reasons.
I'm getting some weird behavior in my CoreData wrapper class. Here's the function:
-(SystemCode*) getSystemCodeWithDescription:(NSString*)description andType:(Class)type {
NSString* entityName = [NSString stringWithFormat:#"%#", type];
// NSStringFromClass(type); was my first try, it also returned a nil string
SystemCode* result = [self getUniqueEntity:entityName predicate:#"Description == '%#'" predicateArg:description generateNew:NO];
return result;
}
If I put a breakpoint on the 2nd line (SystemCode* result = ...) and run GDB I get the following output:
(gdb) print-object entityName
Unable to access variable "entityName"
Can't print the description of a NIL object.
(gdb) print-object type
Result
How can 'type' be a valid object, but when I try to convert it to a string, it just turns in to a nil string? My project is using the XCode 4.0 and running in the iPad 4.3 simulator if that matters.
Are you sure type is a Class?
Also, you can use po instead of print-object.
I figured out the issue. The class was working correctly, someone checked in a build target for debug that was optimized. It was just that gdb couldn't see the values. My bug was actually in the predicate, that's why no entities were being selected.
Try using class_getName from <objc/runtime.h>.
I'm kind of new with objective c and I'm trying to pass an argument by reference but is behaving like it were a value. Do you know why this doesn't work?
This is the function:
- (void) checkRedColorText:(UILabel *)labelToChange {
NSComparisonResult startLaterThanEnd = [startDate compare:endDate];
if (startLaterThanEnd == NSOrderedDescending){
labelToChange.textColor = [UIColor redColor];
}
else{
labelToChange.textColor = [UIColor blackColor];
}
}
And this is the call:
UILabel *startHourLabel; // This is properly initialized in other part of the code
[self checkRedColorText:startHourLabel];
Thanks for your help
Objective-C only support passing parameters by value. The problem here has probably been fixed already (Since this question is more than a year old) but I need to clarify some things regarding arguments and Objective-C.
Objective-C is a strict superset of C which means that everything C does, Obj-C does it too.
By having a quick look at Wikipedia, you can see that Function parameters are always passed by value
Objective-C is no different. What's happening here is that whenever we are passing an object to a function (In this case a UILabel *), we pass the value contained at the pointer's address.
Whatever you do, it will always be the value of what you are passing. If you want to pass the value of the reference you would have to pass it a **object (Like often seen when passing NSError).
This is the same thing with scalars, they are passed by value, hence you can modify the value of the variable you received in your method and that won't change the value of the original variable that you passed to the function.
Here's an example to ease the understanding:
- (void)parentFunction {
int i = 0;
[self modifyValueOfPassedArgument:i];
//i == 0 still!
}
- (void)modifyValueOfPassedArgument:(NSInteger)j {
//j == 0! but j is a copied variable. It is _NOT_ i
j = 23;
//j now == 23, but this hasn't changed the value of i.
}
If you wanted to be able to modify i, you would have to pass the value of the reference by doing the following:
- (void)parentFunction {
int i = 0; //Stack allocated. Kept it that way for sake of simplicity
[self modifyValueOfPassedReference:&i];
//i == 23!
}
- (void)modifyValueOfPassedReference:(NSInteger *)j {
//j == 0, and this points to i! We can modify i from here.
*j = 23;
//j now == 23, and i also == 23!
}
Objective-C, like Java, only has pass-by-value. Like Java, objects are always accessed through pointers. "objects" are never values directly, hence you never assign or pass an object. You are passing an object pointer by value. But that does not seem to be the issue -- you are trying to modify the object pointed to by the pointer, which is perfectly allowed and has nothing to do with pass-by-value vs. pass-by-reference. I don't see any problem with your code.
In objective-c, there is no way to pass objects by value (unless you explicitly copy it, but that's another story). Poke around your code -- are you sure checkRedColorText: is called? What about [startDate compare:endDate], does it ever not equal NSOrderedDescending? Is labelToChange nil?
Did you edit out code between this line
UILabel *startHourLabel;
and this line?
[self checkRedColorText:startHourLabel];
If not, the problem is that you're re-declaring your startHourLabel variable, so you're losing any sort of initialization that was there previously. You should be getting a compiler error here.
Here are the possibilities for why this doesn't work:
the label you pass in to checkRedColorText is not the one you think it is.
the comparison result is always coming out the same way.
... actually, there is no 3.
You claim you initialised startHourLabel elsewhere, but, if it is a label from a nib file, you should not be initialising it at all. It should be declared as an IBOutlet and connected to the label in the nib with interface builder.
If it is not a label in the nib i.e. you are deliberately creating it programmatically, you need to check the address of the label you initialise and check the address of the label passed in to checkRedColorText. Either NSLog its address at initialisation and in checkRedColorText or inspect it with the debugger.