I'm new to Swift and trying to decode one of the Apple examples. Could someone please explain what this line is doing ?
#SceneStorage("selection") private var selectedProjectID: Project.ID?
Project is a struct conforming to Codable and Identifiable
Is selectedProjectID of the same type as Project.ID? ?
In general for some type, T, T? is syntactic sugar for Optional<T>.
Optional<T> is defined as an enum with special compiler support for comparisons and assignments to nil. It looks something like this.
enum Optional<Wrapped>
{
case none
case some(wrapped: Wrapped)
}
So when an optional is nil, it's actually .none
In comments you asked:
Does the Project.Id bit mean selectedProjectID will be of explicit type the same as the Project.ID member in the struct ?
The answer is no. selectedProjectID is an instance property (a variable) whose type is Optional<Project.ID>. Project.ID is a type. It does also have a type, a meta-type (the type of a type), but I don't think that's what you mean to ask. I think you're asking if selectedProjectID is same type as the id property that is defined in Project as part of its conformance to Identifiable. And the answer is no, not exactly.
If someProject is an instance of Project, then someProject.id is of type Project.ID, whereas selectedProjectID is of type Optional<Project.ID>.
There is a lot of compiler support extracting, comparing and assigning between optionals and their wrapped type, but strictly speaking they are different types, just as much as Int and Array<Int> are different types.
On the other hand, assuming someProjectID is not nil, then someProjectID! (ie, force-unwrapped) is the same type as someProject.id. Similarly in
if let pID = someProjectID {
...
}
within the if, pID is also the same type as someProject.id
Related
I am very confused around the concept of "metatype" in the Swift language.
Suppose I have:
class SomeClass {
class func callClassMethod() {
print("I'm a class method. I belong to my type.")
}
func callInstanceMethod() {
print("I'm an instance method. I belong to my type instance.")
}
}
According to the definition:
A metatype type refers to the type of any type, including class types,
structure types, enumeration types, and protocol types.
SomeClass is already a type called SomeClass, then what exactly is the type of SomeClass?
I can create a SomeClass.Type variable:
let var1 : SomeClass.Type = SomeClass.self
var1.doIt();//"I'm a class method. I belong to my type."
but I can also call the static/class function this way:
SomeClass.doIt();//"I'm a class method. I belong to my type."
Are they the same?
They are the same because the compiler guarantees that class names are unique (Swift is name spaced by module), so there is only one thing that is of SomeClass.Type and that is the class SomeClass. The meta type is often useful when you just want to pass the type of something to a function but you don't want to pass an instance. Codable does this for instance:
let decoded = try decoder.decode(SomeType.self, from: data)
If you could not pass the meta type here then the compiler could still infer the return type based on an annotation on the left, but it would be less readable:
let decoded: Sometype = try decoder.decode(data)
Some libraries do use the type inference style instead, although the Apple preference seems to be to use the meta type as its clearer what the right side of the assignment is on its own, without relying on type inference from the left side of the assignment.
The code below compiles and works in swift.
struct TestStruct {
let value: String = "asdf"
}
func iWantAReferenceType(object: AnyObject) {
print(String(describing: object))
}
let o: TestStruct = TestStruct()
iWantAReferenceType(object: o as AnyObject)
I expected this to be a compile error because a struct can never conform to AnyObject. As demonstrated below by code that fails to compile.
protocol Test: AnyObject {
}
//Compile error: because a struct cannot be AnyObject
struct TestStruct: Test {
let value: String = "asdf"
}
I am aware there is some bridging that can happen for certain types such as String. This would convert the value type of a reference type.
print(Mirror(reflecting: "asdf").subjectType) //print: String
print(Mirror(reflecting: "asdf" as AnyObject).subjectType) //print: NSTaggedPointerString
In writing this question I thought to see what the type of the cast object was and it seems it is also bridged in someway.
print(Mirror(reflecting: o).subjectType) //prints: TestStruct
print(Mirror(reflecting: o as AnyObject).subjectType) //prints: _SwiftValue
Why is this type of casting allowed? It seems to be breaking the contract for the function that is expecting a reference type.
I stumbled on this by accident when refactoring some code to support value types, to my surprise it had already been working for value types even though I thought it wouldn't. Is it safe to rely on this behaviour?
This is a feature to facilitate passing to Cocoa. Any struct can be wrapped into a SwiftValue reference type. If you print type(of: object) you'll see the wrapper.
I don't think there is any contract for "expecting a reference type." More importantly, while "value types" and "reference types" exist in Swift, what really matter is value and reference semantics, which are not expressible in the language. You can create value semantics in reference types and reference semantics in value types, so the Swift type system really isn't of any help in that regard.
The important point here is that you only get this unusual behavior if you explicitly request it by asking for as AnyObject. There are very few reason to write that, and if you are, you had better know exactly what you're doing.
As far as I understand, identity operator is used to determine if two objects have the same reference. It means, in practice, the both side of the operator shall be an object.
However, I've tried the following code and it confused me of the identity operator's function of what I understand
class Dog {}
let d: Dog = Dog()
if type(of: d) === Dog.self {
print("yep") //prints out "yep"
}
if type(of: d) == Dog.self {
print("yep") //prints out "yep"
}
The left and right side of the identity operator is not an object but a type and it seems, for this point, semantic equivalence operator and object identity operator (looks like) works in the same way.
Question:
Is this a bug or I didn't get the whole point correctly.
Thanks for your help and time
The left and right side of the identity operator is not an object but a type.
Actually, on Apple platforms, they are objects.
This is due to the fact that Swift classes are implemented as Objective-C classes under the hood, as Mike Ash goes into detail in this great blog post. This means that the metatype of a class is also an Objective-C class, and therefore conforms to AnyObject.
Because of this, you can compare class metatypes with the identity operator, as it's defined as:
public func ===(lhs: AnyObject?, rhs: AnyObject?) -> Bool
It'll compare whether the two objects are the same object, or specifically in this case, the same class metatype.
In contrast, under the hood, the metatype for a value type isn't an Objective-C object – it's just a pointer to some static metadata. If we rewrite your example to use a struct:
struct Dog {}
let d = Dog()
// Binary operator '===' cannot be applied to two 'Dog.Type' operands
if type(of: d) === Dog.self {
print("yep")
}
You'll see that we can no longer use === to compare metatypes, as they don't conform to AnyObject. So really, the ability to use the identity operator to compare class metatypes is just a side-effect of them being implemented as Objective-C objects.
The universal way of comparing metatypes is with the equality operator ==, as Swift provides an overload specifically for metatypes:
public func ==(t0: Any.Type?, t1: Any.Type?) -> Bool
This checks whether two metatypes are the same, however unlike ===, it works with both class metatypes and value-typed metatypes. Under the hood, it's implemented as a simple pointer comparison, so should always yield identical results as === with class metatypes.
I would therefore always recommend comparing metatypes with ==, as you're not relying on the conformance to AnyObject. For example, on a Linux platform, class metatypes don't conform to AnyObject and therefore cannot be compared with the identity operator (although interestingly, when Foundation is imported, it appears to add a === overload for AnyObject.Type operands – presumably to aid interoperability).
Not a bug, they are the same thing.
Dog, as a class (type) is a singular, there can only be one. There can be many Instances but only one of the Class.
type(of: d) return the owner Class of d, Dog.self returns the Class itself. They are exactly the same object, the singular Class of Dog.
I want to pass an array of types to a function - but I'm not sure what is the type of a Swift Type.
Let's say I have two classes:
MyClass and AnotherClass.
I want an array like this [MyClass, AnotherClass].
What will be the type of the array?
It would be of AnyClass type which is base for all object types.
func checkClassTypes(types: [AnyClass]) {
types.forEach { type in
// Your custom classes don't necessarily have to subclass from NSObject.
print(type is NSObject.Type)
}
}
checkClassTypes([MyClass.self, AnotherClass.self])
If you need to contain only class types, AnyClass will works:
class MyClass {
//...
}
class AnotherClass {
//...
}
let classTypes: [AnyClass] = [MyClass.self, AnotherClass.self]
If you want to include some value types, you may need to use Any or Any.Type:
let types: [Any] = [Int.self, String.self, MyClass.self, AnotherClass.self]
let anyTypes: [Any.Type] = [Int.self, String.self, MyClass.self, AnotherClass.self]
But all three types, AnyClass, Any or Any.Type, are hard to use. You will soon find it is very hard just to instantiate a class in the array.
I'm not sure I understand your question about Swift Types correctly, but Swift doesn't specify a Type as being a certain kind of variable. Swift defines that variables in code can have a certain type, which can be a float, a string, integer, etc. When it is said that Swift is type safe and infers a type, this means that Swift requires you to be clear on the type of values that you store in your variables (type safety) and that Swift will try to infer what type a variable is when you specify a value (type inference).
The type of your variable can also be more complex, like an enum, a struct or a class. Swift even sees an array or other collections as types. In this sense a Type could be defined as a piece of memory in which a constant or a variable (or a collection of these) is stored. A type can be different things. The great thing about having a Type as a constant/variable in which I store information, is that I can now defer the type of a variable until runtime, rather than that I have to specify it in my code. Or I can specify a structure of a generic type and later I can define different kinds of variables with the same structure, but of different types. Please, read for a more detailed understanding of dealing with types the Swift programming language from Apple. It is free and can be downloaded in the iBooks Store.
Now coming back to your second question of an array of [MyClass, AnotherClass] is kind of like deferring the types of your array until later. You can initially define your array as an array of AnyObjects or more swift-like AnyClass, like
myArray = [AnyClass]()
Later you can put different types of variables/objects in that array, like a Double, a String, or an object with a specific class.
You should be careful about building such an array, however. This array is not type safe and you haven't specified the type of object at a certain index. To prevent xcode having trouble with your code and your program from crashing, you should always type cast the individual objects in your array. For instance like
if let myObject = myArray[2] as? myClass { do something }
If you don't typecast your array both xcode and your program will do weird things.
I hope this answered your question.
Kind regards,
MacUserT
I'm guessing that you really want to create an array of instances of the two classes rather than an array of the class definitions. If so, you will be creating an array of Any or AnyObject, not AnyClass.
class MyClass {
}
class OtherClass {
}
let m = MyClass()
let o = OtherClass()
let array1 = [m, o] as [Any]
// or
let array2 = [m, o] as [AnyObject]
Given a protocol without any associated types:
protocol SomeProtocol
{
var someProperty: Int { get }
}
What is the difference between these two functions, in practice (meaning not "one is generic and the other is not")? Do they generate different code, do they have different runtime characteristics? Do these differences change when the protocol or functions become non-trivial? (since a compiler could probably inline something like this)
func generic<T: SomeProtocol>(some: T) -> Int
{
return some.someProperty
}
func nonGeneric(some: SomeProtocol) -> Int
{
return some.someProperty
}
I'm mostly asking about differences in what the compiler does, I understand the language-level implications of both. Basically, does nonGeneric imply a constant code size but slower dynamic dispatch, vs. generic using a growing code size per type passed, but with fast static dispatch?
(I realise that OP is asking less about the language implications and more about what the compiler does – but I feel it's also worthwhile also to list the general differences between generic and protocol-typed function parameters)
1. A generic placeholder constrained by a protocol must be satisfied with a concrete type
This is a consequence of protocols not conforming to themselves, therefore you cannot call generic(some:) with a SomeProtocol typed argument.
struct Foo : SomeProtocol {
var someProperty: Int
}
// of course the solution here is to remove the redundant 'SomeProtocol' type annotation
// and let foo be of type Foo, but this problem is applicable anywhere an
// 'anything that conforms to SomeProtocol' typed variable is required.
let foo : SomeProtocol = Foo(someProperty: 42)
generic(some: something) // compiler error: cannot invoke 'generic' with an argument list
// of type '(some: SomeProtocol)'
This is because the generic function expects an argument of some type T that conforms to SomeProtocol – but SomeProtocol is not a type that conforms to SomeProtocol.
A non-generic function however, with a parameter type of SomeProtocol, will accept foo as an argument:
nonGeneric(some: foo) // compiles fine
This is because it accepts 'anything that can be typed as a SomeProtocol', rather than 'a specific type that conforms to SomeProtocol'.
2. Specialisation
As covered in this fantastic WWDC talk, an 'existential container' is used in order to represent a protocol-typed value.
This container consists of:
A value buffer to store the value itself, which is 3 words in length. Values larger than this will be heap allocated, and a reference to the value will be stored in the value buffer (as a reference is just 1 word in size).
A pointer to the type's metadata. Included in the type's metadata is a pointer to its value witness table, which manages the lifetime of value in the existential container.
One or (in the case of protocol composition) multiple pointers to protocol witness tables for the given type. These tables keep track of the type's implementation of the protocol requirements available to call on the given protocol-typed instance.
By default, a similar structure is used in order to pass a value into a generic placeholder typed argument.
The argument is stored in a 3 word value buffer (which may heap allocate), which is then passed to the parameter.
For each generic placeholder, the function takes a metadata pointer parameter. The metatype of the type that's used to satisfy the placeholder is passed to this parameter when calling.
For each protocol constraint on a given placeholder, the function takes a protocol witness table pointer parameter.
However, in optimised builds, Swift is able to specialise the implementations of generic functions – allowing the compiler to generate a new function for each type of generic placeholder that it's applied with. This allows for arguments to always be simply passed by value, at the cost of increasing code size. However, as the talk then goes onto say, aggressive compiler optimisations, particularly inlining, can counteract this bloat.
3. Dispatch of protocol requirements
Because of the fact that generic functions are able to be specialised, method calls on generic arguments passed in are able to be statically dispatched (although obviously not for types that use dynamic polymorphism, such as non-final classes).
Protocol-typed functions however generally cannot benefit from this, as they don't benefit from specialisation. Therefore method calls on a protocol-typed argument will be dynamically dispatched via the protocol witness table for that given argument, which is more expensive.
Although that being said, simple protocol-typed functions may be able to benefit from inlining. In such cases, the compiler is able to eliminate the overhead of the value buffer and protocol and value witness tables (this can be seen by examining the SIL emitted in a -O build), allowing it to statically dispatch methods in the same way as generic functions. However, unlike generic specialisation, this optimisation is not guaranteed for a given function (unless you apply the #inline(__always) attribute – but usually it's best to let the compiler decide this).
Therefore in general, generic functions are favoured over protocol-typed functions in terms of performance, as they can achieve static dispatch of methods without having to be inlined.
4. Overload resolution
When performing overload resolution, the compiler will favour the protocol-typed function over the generic one.
struct Foo : SomeProtocol {
var someProperty: Int
}
func bar<T : SomeProtocol>(_ some: T) {
print("generic")
}
func bar(_ some: SomeProtocol) {
print("protocol-typed")
}
bar(Foo(someProperty: 5)) // protocol-typed
This is because Swift favours an explicitly typed parameter over a generic one (see this Q&A).
5. Generic placeholders enforce the same type
As already said, using a generic placeholder allows you to enforce that the same type is used for all parameters/returns that are typed with that particular placeholder.
The function:
func generic<T : SomeProtocol>(a: T, b: T) -> T {
return a.someProperty < b.someProperty ? b : a
}
takes two arguments and has a return of the same concrete type, where that type conforms to SomeProtocol.
However the function:
func nongeneric(a: SomeProtocol, b: SomeProtocol) -> SomeProtocol {
return a.someProperty < b.someProperty ? b : a
}
carries no promises other than the arguments and return must conform to SomeProtocol. The actual concrete types that are passed and returned do not necessarily have to be the same.
If your generic method had more than one parameter involving T, there would be a difference.
func generic<T: SomeProtocol>(some: T, someOther: T) -> Int
{
return some.someProperty
}
In the method above, some and someOther have to be the same type. They can be any type that conforms to SomeProtocol, but they have to be the same type.
However, without generics:
func nonGeneric(some: SomeProtocol, someOther: SomeProtocol) -> Int
{
return some.someProperty
}
some and someOther can be different types, as long as they conform to SomeProtocol.