difference between `catMaybe as? Animal` and `catMaybe as Animal?` - swift

Lets say I have these:
class Animal {
}
class Cat: Animal {
var meow = "meow"
}
Now I have a variable:
var catMaybe: Cat? = /* ... */
and at some point later I do this:
var obj1 = catMaybe as Animal? // ①
and it compiles fine but I could have wrote this other code too:
var obj2 = catMaybe as? Animal // ②
only this time I get a warning. Which confuses me since I thought both were doing the same.
warning: cast from 'Cat?' to unrelated type 'Animal' always fails
var obj4 = catMaybe as? Animal
~~~~~~~~ ^ ~~~~~~
What is the real difference between ① and ②?
Edit1
I am interested in what is really happening in the background. I would go and read the assembly code to find why ② always fails but unfortunately I am not very good at it :(
In ②, since Animal is not an optional, I thought the compiler would know that catMaybe must be unwrapped first and if the operation succeed try to cast it to Animal. In any case of error it would return nil (since I am using as?) Why the compiler does not apply this logic? What stops him?
Edit2
I am using swift 1.2

With as? you cast an optional,let say you do a if letif let object = catMaby as? Animal{
/do some code with object
}
You are not sure if catMaby has a value.as? are for if let
Explanation
The optional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as, attempts the downcast and force-unwraps the result as a single compound action.So if you are not sure that catMaby is a Animal,put as?.There in the code checked if catMaby is Animal and checked if catMaby is an optional
So as? is for optional values,as! is the same but gives an error if you try to unwrap it

You get the warning since catMaybe is an optional. Optionals are of an enum type and only after you unwrap catMaybe (with either ? or !) you will get the correct object (of type Cat).
Since catMaybe is an enum (which does not inherit Animal) you get that warning.
So this should work :
var obj2 = catMaybe! as? Animal // upcast the real unwrapped value
The first version works since you keep catMaybe as an optional (an optional to an Animal object when unwrapped)

This is made complicated by mixing together two things – optionals, and casts. It might be clearer to ditch the optionals, look at that, and then reintroduce the optionals. Note this is all Xcode 6.3/Swift 1.2.
Here’s the simplest case:
class Animal { }
class Cat: Animal { }
let c = Cat()
let a = c as Animal // fine
The compiler knows that every Cat reference can be guaranteed convertible to Animal, so it lets you use as without complaint. If you do it the other way around though:
let a: Animal = Cat()
let c = a as Cat // compiler error
The compiler complains that as cannot be used – an Animal might not be a Cat. You must use as?, which returns an optional value – either a successfully converted Cat, or nil:
let a: Animal = Cat()
if let c = a as? Cat // c will be a Cat?
Swift also lets you use as? when the conversion is guaranteed to work:
let c = Cat()
let a = c as? Animal // a will be an Animal?
though it sometimes give you a warning that you’re using a conditional cast on something that will always succeed. But its only a warning.
That’s pretty much it for as vs as?. as for when the compiler can know for certain the cast will work, as? that returns an optional for when it might not. This doesn’t just apply to super/subclasses, it also applies to other conversions guaranteed to work, such as Swift.Array to NSArray, Sometype to Sometype?.
OK now to add in the other optional. Suppose instead you have:
let c: Cat? = Cat()
let a = c as Animal? // this is fine
So an optional Cat will convert to an optional Animal using as. This is because optionals have support for covariance – Swift looks at the contents of the optional being converted from, and the contents of the optional being converted to, and if the conversion works on the contents, it will allow the conversion.
By the way, this also works with arrays:
let c: [Cat] = [Cat()]
let a = c as [Animal]
Since Cat as Animal is allowed, so is [Cat] as [Animal].
However, casting away the optional is not allowed:
let c: Cat? = Cat()
let a = c as Animal
because you cannot discard optionality using as, just like this isn’t allowed:
let i: Int? = 1
let j = i as Int
And replacing as with as? here doesn’t help – you still can’t do the cast, so while it might compile, the result will always be nil (the compiler may warn you about this).
Finally, you can also do conditional casting inside the optional:
let c: Animal? = Cat()
let a = c as? Cat?
but beware! Now a will be of type Animal??, because the optional now contains an optional. So if you ever have to do this, you’re better off doing like so:
let c: Animal? = Cat()
let a = c.flatMap { $0 as? Cat? }
Or, you would be if this didn’t crash the compiler :-(. But the code ought to be correct.

Related

It seems Swfit.AnyHashable is not casing (as!, as?, as) properly with enums

Here is a simple case example of AnyHashable not supporting casting with enums.
enum testEnum: String {
case Test
}
let myObject: AnyHashable = testEnum.Test as AnyHashable
let newObject = myObject as? testEnum
In this case newObject will return nil. If I cast instead do
let newObject = (myObject as? AnyObject) as? testEnum
it will cast fine.
I have tried this with structs, custom classes and String and they all cast properly. For instance this works.
let myObject: AnyHashable = "Test" as AnyHashable
let newObject = myObject as? String
Is this a bug in swift or am I just not doing this correctly here.
I tried this in Swift 3.2 and Swift 4.
AnyHashable explicitly type-erases:
The AnyHashable type forwards equality comparisons and hashing operations to an underlying hashable value, hiding its specific underlying type.
The fact that this happens to work for some types is the surprising fact, not that it fails for enums. I would expect it's a performance optimization to help with dictionaries. But this isn't how you're expected to use AnyHashable. The expected usage is to initialize it with AnyHashable.init, not as AnyHashable.
What you meant is this:
enum TestEnum: String {
case test
}
let myObject = AnyHashable(TestEnum.test)
myObject.base // => test (as an Any)
myObject.base as? TestEnum // => Optional<TestEnum>(.test)
Note that AnyHashable is a completely different kind of thing than Any or AnyObject. The latter two are protocols. The former is a type-erasing struct. The prefix Any... in the stdlib means "type-eraser" except for the oddball special-cases (that you should avoid as much as humanly possible) Any and AnyObject.

swift 3.0 Problems about String initialization

I am quite new to Swift and when I learning about initializing a string, I find a wired syntax that I cannot understand.
For example
If I initialize a string using:
var str:String = "Hello, playground"
str.isEmpty
This works well
However, if I initialize a string with a constructor
var str = String("Hello, playground")
str.isEmpty
this does not work.
And the compiler fix it by changing the syntax to
str?.isEmpty
I have no idea about what is that “?” for.
Any suggestion is appreciated.
When you say:
let str = String("Hello, playground")
you're using String's init?(_ description: String) initialiser, which satisfies LosslessStringConvertible's initialiser requirement, as not all types that conform have a representation for an arbitrary string (for example, Double).
Because the initialiser is failable, it returns an optional string instance (i.e String?), hence why the compiler is prompting you to use optional chaining (if not already, I would highly recommend reading the optionals section of the language guide).
However it's worth noting there's absolutely no need for String's implementation of this initialiser to be failable – as a string can always represent a string! This will be fixed in Swift 4, it will be implemented as a non-failable initialiser satisfying the failable requirement. This is because it doesn't break the contract with the protocol of either returning a new instance or nil (it just never happens to do the latter).
In the mean time however, you could just force unwrap the result, as it will always succeed. But really the use of the initialiser here is completely redundant. You should use a string literal, such as in your first example:
let str = "Hello, playground"
str will simply be initialised to a non-optional String instance with the contents of the string literal. Note that Swift can infer str to be of type String – you don't need to explicitly annotate it as such.
var str = String("Hello, playground")
This produces what is called an "Optional". In Swift, if something can be null/nil, then it is wrapped in an Optional. The '?' tries to unwrap the optional and produce a String.
If you KNOW that the value can never be null, then you can force unwrap it like this:
var str = String("Hello, playground")
str!.isEmpty
or
var str2 = String("Hello, playground")!
str2.isEmpty
Generally though forced unwrapping is frowned upon, as it can lead to crashes. Better approaches:
struct MyError:Error {
var message:String
}
guard let str3 = String("Hello, playground") else {
throw MyError(message:"failed")
}
str3.isEmpty
if let str4 = String("Hello, playground") {
str4.isEmpty
}

swift use of AnyClass object with as?

I would like to do this:
let myClass: AnyClass = functionReturningClass() // ex. String.self
if let myObject = functionReturningObject() as? myClass { ...
The as? myClass doesn't compile ('myClass is not a type'). What can I do?
Unfortunately this is not possible--as must know the type it is casting to at compile-time. You cannot provide a variable type to as. If you must cast, then you are limited to features like generics and overloading that work at compile-time.
Think about it--what methods should the compiler allow you to call on the resulting myObject? It's impossible to say without knowing the runtime value of myClass. It makes good sense, therefore, that this doesn't work.
You can still return subclasses from functionReturningObject(). You can still cast them to different types. You just can't decide what type to cast them to dynamically at runtime.

Swift downcasting array of objects

import Foundation
class A: NSObject {
}
class B: A {
}
let array = [B(),B(),B(),B()]
array as? [A] //this does not works
B() as? A // this works
as? is a conditional downcasting operation (which can fail, and therefore returns an optional) – and you're trying to upcast (which can never fail, and therefore you can do freely).
You therefore can just use as in order to freely upcast the array – or rely on type inference, as Casey notes.
let arrayOfB = [B(),B(),B(),B()]
let arrayOfA = arrayOfB as [A] // cast of [B] to [A] via 'as'
let explicitArrayOfA : [A] = arrayOfB // cast of [B] to [A] via inference of explicit type
func somethingThatExpectsA(a:[A]) {...}
somethingThatExpectsA(arrayOfB) // implicit conversion of [B] to [A]
Although the compiler's error of A is not a subtype of B is certainly unhelpful in the context. A more suitable error (or maybe just a warning) could be 'as?' will always succeed in casting '[B]' to '[A]' – did you mean to use 'as'?.
You should also note that for your example:
B() as? A // this works
You'll get a compiler warning saying Conditional cast from 'B' to 'A' always succeeds. Although why you get a compiler warning, rather than error for this – I can't say. I suspect it's due to the special way in which array casting happens.
Because B is an A, you can drop the conditional cast and
let thing = array as [A]
Or declare the destination as the desired type and forget about casting
let thing : [A] = array

why there need add '!' in code suffix

enum Season: Int {
case Spring = 0
case Summer = 1
case Autumn = 2
case Winter = 3
}
var SeasonTime1 = Season.Spring
//why ? this code suffix need add !
SeasonTime1 = Season.init(rawValue: 2)!
List item
// why? there don't need add !
var SeasonTime2 = Season.init(rawValue: 2)
This is because Enum uses a failable initializer (see documentation here). As its title implies, a failable initilizer can fail and return nil. Objects that can contain nil are Optionals in Swift. Therefore you could have:
let seasonTime1 = Season.init(rawValue: 2)
In which case the type of seasonTime1 is Optional<Season>. When you want to use the value, you then have to unwrap the Optional, either using if let, guard or !. In this case:
let seasonTime1 = Season.init(rawValue: 2)!
You're unwrapping it immediately, so the type of seasonTime1 will be Season. Note the following code:
let seasonTime1 = Season.init(rawValue: 2222)!
This will run, but if you access seasonTime1 it will fail. This is because there is no enum with a raw value of 2222, and so seasonTime1 will contain nil. Since it is implicitly unwrapped, the fact that it contains nil is illegal, which will cause a crash if the unwrapping occurs.
Because the compiler cannot infer that the raw value type exists so you need the '!' too force unwrap it. If you wrap it in a guard statement you can ensure that it is of the type that "you" know it is. Then you don't need the '!'