No warning when using nonnull? - objective-c

With code like this:
- (nonnull NSString *)testing {
return nil;
}
Shouldn't I get a compiler warning? I get no warning all, which seems to make the entire nullability stuff seem useless?

Well, in my opinion it should produce a warning, but I couldn't figure out to get one either.
What might be helpful though, is using Product > Analyze to run the CLANG Static Analyzer. This should give the following hint:
Null is returned from a method that is expected to return a non-null value
Another thing worth mentioning is the setting CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION which is named Incorrect Uses of Nullable values in the Apple LLVM 7.1 - Warnings - All languages section of the Build Settings.
This setting will not produce a warning for incorrect return values, but it shows a warning when using the method with incorrect parameters (e.g. nil for nonnull parameters).
This answer refers to Xcode Version 7.3.1 (7D1014)

"nonnull" is mostly there to combine Objective-C and Swift. Swift will translate the type of the method to "NSString" and not "NSString?".

Related

Strange behavior of -Wunused-value warning

I just spent some time finding out why a mutable set didn't correctly intersect itself
with another set, using
[someMutableSet intersectsSet:anotherSet]; // not the best idea
Of course, the correct syntax is [someMutableSet intersectSet:anotherSet] and the above line means something different – it's an method call with return value of BOOL type.
Since I have -Wall -Wextra options enabled this should have been caught as a warning. But it wasn't caught. I investigated further by trying, where types is an NSMutableSet:
(void)[types intersectsSet:types]; // -> no warning, this is expected
(BOOL)[types intersectsSet:types]; // (1) -> warning, this is expected
And, again, if I do this:
[types intersectsSet:types]; // (2) -> no warning, UNEXPECTED
there is no warning, even thought the method is defined as - (BOOL)intersectsSet:(NSSet *)otherSet; so one would expect (1) and (2) to be equivalent. Mayhaps the vile compiling tool considers (1) to be of a more dangerous nature compared to (2), but why does that affect warnings, I ask?
So, how to make compiler produce the same warning in (2) as in (1)?
This behavior in the compiler seems intentional (and reasonable.)
If the -Wunused-value warning were emitted for all ObjC message send expressions where the method has a return value that is implicitly discarded (i.e. there is no void cast,) it would be so “chatty” that it would render itself useless. In other words, people would get such large numbers of warnings for existing projects that they would simply turn the warning off, rather than annotate all such cases with (void) casts.
The fact that the warning is emitted in the case where the return value is cast to BOOL is a nice surprise, and makes sense: it's reasonable for the compiler to then assume that the programmer is indeed interested in the return value (because why otherwise include the cast?)
The Clang development community on the cfe-dev mailing list might be able to give you more information on the thinking behind this.
I don't know of any way to force the behavior you want in general, but for the interfaces in your own code you can force this warning by using the warn_unused_result attribute in the ObjC method (or C function) declaration:
#interface MyClass : NSObject
- (int) myReturnValueMustNotBeIgnored __attribute__((warn_unused_result));
#end

Why isn't there a 'nonnil' attribute for clang?

nonnull works for C functions but not obj-c methods. To be clear, I am suggesting this
- (void)doSomethingWithRequiredString:(NSString * __attribute((nonnil)))requiredString
bar:(NSString *)optionalString);
or (more like nonnull)
- (void)doSomethingWithRequiredString:(NSString *)requiredString
bar:(NSString *)optionalString)
__attribute((nonnil(0)));
I have puzzled over whether or not there is a good technical reason. I understand that clang could only really use the attribute for a compile time check or static analysis, but that seems orthogonal. Is there some strong reason not to have this?
You totally can. The only thing you're doing wrong is thinking that method parameters are 0-indexed, when in fact they're 1-indexed (oh, and it's nonnull, not nonnil):
- (void)doSomethingWithRequiredString:(NSString *)requiredString
bar:(NSString *)optionalString
__attribute((nonnull(1)));
Now when you try to use that:
id thing = ...;
[thing doSomethingWithRequiredString:nil bar:#"42"];
Xcode will warn you with a by saying "Null passed to a callee which requires a non-null argument".
Also, if you leave out the "(1)" portion of the __attribute, it's assumed that the non-nil requirement applies to all parameters.
Clang recognizes the GCC attributes, and GCC's definition of the nonnull attribute is here: http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html#index-g_t_0040code_007bnonnull_007d-function-attribute-1733
Update: As of Xcode 6.3 a cleaner syntax is supported.
In properties and methods the keywords are nullable, nonnull and null_unspecified.
So your method signature would become this:
- (void)doSomethingWithRequiredString:(nonnull NSString *)requiredString
bar:(nullable NSString *)optionalString;
In the Xcode 6.3 beta new Objective-C features have been added to express (non)nullability in headers: https://developer.apple.com/swift/blog/?id=22
Yes there is.
You may code like this:
- (nullable AAPLListItem *)itemWithName:(nonnull NSString *)name;
- (NSInteger)indexOfItem:(nonnull AAPLListItem *)item;
https://developer.apple.com/swift/blog/?id=25

Fixing "direct comparison of a string literal has undefined behavior" automatically

In Xcode, I'm getting the error "direct comparison of a string literal has undefined behavior," and I know why I'm getting it, but is there some way for me to click a button and have Xcode remove it? I'm saying this because in 370 places in my app I've gotten it.
The clang option to disable this warning is -Wno-objc-literal-compare.
However, warnings are there for a reason; this one is because comparing against NSString literals using == is not guaranteed to behave as you might expect. Use isEqual: or isEqualToString: instead and you can both get rid of this warning and avoid having this turn into a bug for you later.
You can avoid the warning using `isEqualToString` instead of `==`.
`==` simply compares the pointers, which will usually be different even
if their contents are the same. The`isEqualToString` method compares
their contents.

What would setting a getter do?

First, some context: while answering questions on SO, I came across a post wherein the author had been trying to set a getter with syntax similar to [self.propertyGetter:newValue];. For some reason, this compiles, and I thought to myself, "this would constitute a call to nil, wouldn't it?". So, my question is, why in the heck does this 'work'? (to be perfectly clear, the poster was complaining that this had no effect, so by 'work', I mean compile).
The code you quoted is [self.propertyGetter:newValue]. Here's how the Objective-C compiler parses this.
The first thing after the left bracket has to be the receiver of the message. In that code, the receiver is the value of self.propertyGetter. The compiler transforms self.propertyGetter into [self propertyGetter]. So we can rewrite the code you quoted as [[self propertyGetter]:newValue].
Now the compiler needs to figure out the selector of the message. You usually see a keyword selector like setStatusBarHidden:animated:, which is a series of keywords followed by colons. But it turns out a keyword can be zero-length. So a colon by itself is a valid keyword selector. So the compiler sees it like this: [[self propertyGetter] :newValue]. (Using a zero-length keyword is almost always very bad style.)
You can declare an instance method using that selector. For example:
- (void):(NSObject *)newValue;
If [self propertyGetter] returns an object of a class that has that method, the code will compile and run.
If the compiler has seen any class with a method named like that, and [self propertyGetter] returns type id, then the code will compile (because id is a wildcard type and the compiler will not complain if you try to send it any known message selector). However, the app will crash at runtime if [self propertyGetter] doesn't actually understand the : message.
This compiles because all objective-C objects are dynamic entities by default. It would compile, but would crash at runtime.
Source.

Why do I need to cast before a method of an item of a NSArray can be called?

I am fairly new to Objective-C. Currently porting my own library from C#/Java to objective C.
I now run into a very strange problem for me.
I have a NSArray with several Note objects. I want to transpose on of these notes:
//Note.h
- (Note *) transpose: (int) semitones;
//Main
NSArray *notes = [get it from somewhere];
Note *transposedNote = [[notes objectAtIndex:0]transpose:1]; //Doesn't compile
Note *transposedNote = [(Note*)[notes objectAtIndex:0]transpose:1]//Does compile
Is this happening because there is already a transpose method available in the general libraries?
I thought due to the dynamic nature of objective-C at runtime it would be checked which class objectAtIndex returns and then sends the message to it?
It is my understanding that there is no runtime type checking for the assignment operator in Objective C. Since an array can contain a mixture of types, there is no way for the system to know what objectAtIndex returns.
How about
Note *transposedNote = [notes objectAtIndex:0]; // first line
[transposedNote transpose:1]; // second line
? Notice in the reference that objectAtIndex: returns an id, you will see it is pretty obvious:
In the code above, because id can fit into any object, the first line doesn't need to cast it into Note. In the second line I'm just calling a method on a Note so the compiler is happy.
In your code you are calling methods on the returned id object, so the compiler doesn't understand what you are trying to do. Just assign it to a Note reference and it will be fine.
Yes, the error is because there's already a transpose: method in AppKit. And you're also right that it normally doesn't cause an error when you have two unrelated classes implementing methods with the same name. The reason you get an error is because the two methods either return incompatible types or take incompatible types as arguments. In your particular case, you're seeing both problems:
-[NSResponder transpose:] takes an id and returns void
-[Note transpose:] takes an int and returns an id
These are totally incompatible types, and the compiler does need to know the types involved even if it doesn't know what exact method is going to be called.
It does compile unless you have -Werror set to treat warnings as errors.
It might produce a warning if the compiler doesn't already know about the selector or if the selector is declared in more than one class. In the former case, it should be necessary only to import the interface containing the selector. In the latter case, you'll need to do the cast to suppress the error.