"Direct comparision of a string literal" warnings since last Xcode update - ios

Since the last update of Xcode (to v4.6) I've got a bunch of the following warnings:
"Direct comparison of string a literal has undefined behavior"
This is when a NSString property is compared with another string by this way:
if ([self.myString isEqualToString:#"Compare Me"]) { ...
Originally I compared with self.myString == #"Compare Me" but there are the same warnings. Xcode suggests to use isEqual: instead.
I wonder why I should do this. Other comparisons still work, e.g.:
if ([segue.identifier isEqualToString:#"nextScreen"]) { // => NO WARNING HERE
Edit: OK, for anyone who doesn't believe/downvoters, see this screenshot:

Sometimes Xcode gets confused and shows old warnings. I suspect it is showing an old warning for use of ==. Clean the build, go to the organiser window and delete the derived data for this project, then restart Xcode.

Basically (according to this other stackoverflow answer, you should switch any of your == string comparisons to use isEqual or isEqualToString.
To make it easier, I found this post on bignerdranch's blog that said there is no difference (in the observable human world) between isEqual and isEqualToString, so I personally use isEqual.

When you use "==", it should, in theory (like in C/C++) compare the pointers, not the value of the objects.
Fact is obj-c (and compiler I guess) will understand what you really want and transform this in isEqualToString. But to be "safe", it's better to use the good method to compare two strings, two numbers etc.

Related

How "id" type understands the receiver of method without casting?

After merging master to my working branch I got compiler error on the line, which wasn't be changed. The error looks like
id test;
[test count];
Multiple methods named 'count' found with mismatched result.
At first it looks clear, because compiler doesn't know which concrete type the "test" variable is. But I don't understand why it worked before.
If I create a new file this line works, assuming that is a NSArray's method. Why compiler doesn't show error in this case?
While showing error message, there is several possible receivers of count method are shown. (NSArray, NSDictionary, NSSet) Does it search all classes that can receive that message and show error if there are multiple?
I noticed that error occurs when I import "-Swift.h" file. How it depends?
Compiler doesn't cast or check your id type. It just provides you all possible selectors. You said that this issue connected with importing "-Swift.h" file. In this case check you Swift code, probably you have count function visible for Objective C which returns something else than Int.
Also, you can check the issue in Issue navigator, select it and it will show all count calls visible in Objective C. Check them all, most of them will return NSUInteger, but there should be one that returns something else, for example:
SWIFT_CLASS("_TtC3dev19YourClass")
#interface YourClass : NSObject
- (int32_t)count SWIFT_WARN_UNUSED_RESULT;
#end
Objective-C doesn't need to know the type of the receiver. At run-time, all objects are just id, and everything is dynamically dispatched. So any message can be sent to any object, no matter its type. (At run-time, objects are free to decide what to do with messages they don't understand. The most common thing to do is raise an exception and crash, but there are many kinds of objects that can handle arbitrary messages that don't map directly to method calls.)
There is a couple of technical details, however, that complicate this.
The ABI (application binary interface) defines different mechanisms for returning certain primitive types. As long as the value is "a word-sized integer," then it doesn't matter (this includes things like NSInteger and all pointers, which means by extension all objects). But on some processors, floats are returned in different registers than integers, and structs (like CGRect) might be returned in a variety of ways depending on their size. In order to write the necessary assembly language, the compiler has to know what kind of return value it will be.
ARC has added additional wrinkles that require that the compiler know a more about the type of the parameters (specifically whether they're objects or primitives), and whether there are any memory-management attributes that have to be considered.
The compiler doesn't really care what "real" type test is, as long as it can figure out the types and attributes of -count. So when dealing with an id value, it looks through every known selector it can see (i.e. every one defined in an included header or the current .m). It's fine if there are many of them on different classes, as long as they all agree. But if it can't find the selector at all, or if some of the interfaces disagree, then it can't compile the line of code.
As lobstah notes, you likely have a type somewhere in your Swift code that has an #objc method called count() or an #objc property named count that returns something other than Int (which maps to NSInteger, and so match the usual signature of -count). You'll need to fix that method, or you'll need to hide it from ObjC (for example, by adding #nonobjc).
Or much better: get rid of the id, and use its actual type. id is generally a bad idea in Cocoa, and is especially a bad idea if you're calling methods on it, since the compiler can't check that the object will respond and you may crash.

How to represent Core Data optional Scalars (Bool/Int/Double/Float) in Swift?

(first noticed on: Xcode 8.2.1, iOS 10, Swift 3)
(still present as of: Xcode 9 beta 3, iOS11, Swift 4)
We all know that the Core Data concept of optionals precedes and is not strictly tied to the Swift concept of optionals.
And we have accepted that even if a Core Data attribute is marked as Non-optional, the auto-generated NSManagedObject subclass has an optional type:
(some people manually remove the ? with no adverse effects, some don't, but that's beside the point)
(From here on the example and screenshots are for Bool properties, but same goes for Int16/32/64, Double, Float)
Now I noticed the reverse - when a Core Data attribute of type Bool is marked as Optional (and Use Scalar Type is chosen, which Xcode does by default), the auto-generated class has a variable of a non-optional type.
Does this make sense? Is it a bug? Is the behaviour documented anywhere?
And most importantly - how do I actually represent an optional Bool?
I can think of some work-arounds, but they don't seem ideal (e.g. not using scalars, but going back to NSNumber representation of the Bool. Or (even worse) having a separate Bool called something like isVerified_isSet)
Note: I did a couple more tests and if the Default Value is set to None or to NO, then the variable gets saved as false (even if I never actually assign it in my code). If the Default Value is set to YES, then the variable gets saved as true. Still, this means that (apparently) there is no way to logically represent this variable as not having been set yet.
I see the same thing, and I consider it to be a bug. It's not documented anywhere that I can find. Apparently Core Data is applying Objective-C style assumptions here, where a boolean defaults to NO, and an integer defaults to 0. The Core Data/Swift interface has some rough edges, and this is one I hadn't considered before.
It's a good find but I think you're stuck with it until Apple addresses it. You already know the best workarounds, which I agree aren't great. I recommend filing a bug.
This happens because Objective-C scalar types do not have a notion of nil value. Source: handling-core-data-optional-scalar-attributes
I would rather use Objective-C types to manage these cases than Swift types.
In these cases, for scalars types, you can use NSNumber.
#NSManaged public var myDouble: NSNumber?
In the model myDouble is an optional double with nil value by default.
To get the real value you only need to use:
myEntity.myDouble?.doubleValue
If you end up here with this:
#NSManaged var boolAttribute: Bool
and it is not being seen in Objective-C, and you have already disabled "Optional" and enabled "Use Scalar Type" on those attributes, then do yourself a favour.
Double check you have imported your Swift bridging header into that Objective-C file.
I did not and, well, I was most of the way to changing my Bools to NSNumbers before smacking my head and realising how foolish I had been.

Typedef NSArray of type in Objective C

It has been a long time since I have worked in Objective C but now I am using it because I need to write something that will remain mostly source compatible for future versions. I want to create an init method that allows me to init my viewController with an array of my custom model object. In Swift I would do it like this:
typealias Stack = [StackBarTabItem]
…
func init(stacks:[Stack])
But how would I typedef an NSArray like that? I am pretty sure I can't do something like typedef NSArray<StackBarTabItem> Stack; so what is the syntax in objective c?
Until iOS 9 and Xcode 7, this isn't officially supported. One way to do this is to subclass NSArray or NSMutableArray and enforce typing in your subclass, but this isn't really recommended. One way to deal with the fact that NSArray can only hold ids is to use respondsToSelector before calling a method on any of the objects in the array.
This solution isn't really a substitute for a good typing system, but it's a common practice to get around this limitation. Thankfully, generic support is getting added soon!
Objective-C is dynamically typed. You simply do not check for it.
Asking the audience on talks and in internet fora, the real danger that code will be shipped with a typing bug is minimal and by far lower than other sources of errors. Simply do not care about this.
Ask yourself: How could that happen without getting a runtime error at the very beginning of your next program run?

IOS: performSelector:withObject: acts weird

my app turns out to be unable to load some content. I was surprised, and I found out that the bug was that
[contentItem performSelector:#selector(setIsContainer:) withObject:[NSNumber numberWithBool:true]];
was passing "False" to the method instead of "True".
Is there any explanation to this? Or I should, as a good practice, avoid using performSelector?
(I asked coworkers for their phones, and I took same iPhone5s with same iOS versions, in all 3 of them everything worked fine, except the boss' phone)
You are passing an NSNumber object to a method which expects a BOOL primitive. It should fail on every version of iOS.
Change the implementation to:
- (void)setIsContainer:(NSNumber *)isContainer {
_isContainer = [isContainer boolValue];
}
(if you require the original semantics then you'll have to provide an alternative version; one for NSNumber and one for BOOL).

Message passing - compiler won't check whether method is existing?

In Objective-C's Wiki page, there is a section named Messages. It says when compiling, Objective-C doesn't care whether an object has a given method, because anyone can send a message to another. This is dynamic binding.
in C++, obj->method(argument); if no method, wrong.
in Objective-C, [obj method:argument]; if no method, can be fine.
But in my daily coding, with XCode, if compiler cannot find a public method of an object, it always prompt error even before build. like this,
no visible #interface for 'ClassName' declares the selector 'methodName'
I am a little confused about this 'contradiction'. Please forgive me if the question is silly. thanks in advance.
I think the compiler is just protecting you from yourself. In the case you note, the compiler knows that the method you're calling doesn't exist so it reports it as an error.
However, if you tell the compiler that you don't care or don't give it enough information, then it's perfectly valid.
Example:
NSString* var = #"Hello";
[(id)var thisDoesNotExist];
id var2 = #"Hello";
[var2 neitherDoesThis:var];
These (should) both compile.
Chances are you use ARC. For compiling ARC-enabled code, the compiler needs to know what type of objects a method expects as arguments and returns as its return value in order to be able to emit the necessary calls to memory management methods. So, when you are compiling ARC code, the compiler will check if a method signature exists.
If you, however, use manual reference counting (MRC), then the compiler doesn't need this information for this purpose (some of it is still necessary for generating code comformant to the ABI), and it doesn't issue an error if it can't find a certain message/method/selector. It does, however, emit a warning for safety.

Resources