Question #1 - Swift 1.0 or later
What is an optional and what problem do optionals solve?
Solution An optional is used to let a variable of any type represent the lack of value. In Objective-C, the lack of value is available in reference types only, and it uses the nil special value. Value types, such as int or float, do not have such ability. Swift extends the lack of value concept to both reference and value types with optionals. An optional variable can hold either a value or nil any time.
Question #2 - Swift 1.0 or later
When should you use a structure, and when should you use a class? Solution
There's an ongoing debate about whether using classes over structures is a good or bad practice. Functional programming tends to favor value types, whereas object-oriented programming prefers classes.
In Swift, classes and structures differ by a number of features. You can sum up the differences as follows:
Classes support inheritance, structures don't Classes are reference types, structures are value types There's no universal rule that determines what's best to use. The general recommendation is to use the most minimal tool required to accomplish your goal, but a good rule of thumb is to use structures unless you need inheritance or reference semantics.
For more details, check out this detailed post on the matter. Note on performance: At runtime, structure has better performance than classes because method invocations/calling are statically bound, whereas method invocation/calling for classes is dynamically resolved at runtime. That's another good reason to use structures instead of classes when possible.
Question #3 - Swift 1.0 or later
What are generics and what problem do they solve? Solution
Generics are used to make algorithms safely work with types. In Swift, generics can be used both in functions and data types, e.g. in classes, structures or enumerations.
Generics solve the problem of code duplication. A common case is when you have a method that takes a type of parameter and you have to duplicate it just to accommodate a parameter of another type. For example, in the following code the second function is a "clone" of the first one -- it just accepts strings instead of integers.
By adopting generics, you can combine the two functions into one and keep type safety at the same time. Here's the implementation:
func areTheyEqual<T: Equatable>(x: T, _ y: T) -> Bool {
return x == y
}
areTheyEqual("ray", "ray")
areTheyEqual(1, 1)
Since you're testing equality in this case, you restrict the parameters to any type that implements the Equatable protocol. This code achieves the intended result and prevents passing parameters of a different type.
Question #4 - Swift 1.0 or later
There are a few cases when you can't avoid using implicitly unwrapped optionals. When? Why?
Solution The most common reasons to use implicitly unwrapped optionals are:
When a property that is not nil by nature cannot be initialized at instantiation time. A typical example is an Interface Builder outlet, which always initializes after its owner has been initialized. In this specific case -- assuming it's properly configured in Interface Builder -- the outlet is guaranteed to be non-nil before it's used.
To solve the strong reference cycle problem, which is when two instances refer to each other and require a non-nil reference to the other instance. In such a case, one side of the reference can be marked unowned, while the other uses an implicitly unwrapped optional.
Pro tip: Don't use implicitly unwrapped optionals unless you must. Using them improperly increases the chance of runtime crashes. In some cases, a crash might be the intended behavior, but there are better ways to achieve the same result, for example, by using fatalError().
Question #5 - Swift 1.0 or later
What are the various ways to unwrap an optional? How do they rate in terms of safety? Hint: There are at least seven ways.
Solution forced unwrapping ! operator -- unsafe implicitly unwrapped variable declaration -- unsafe in many cases optional binding -- safe optional chaining -- safe nil coalescing operator -- safe new Swift 2.0 guard statement -- safe new Swift 2.0 optional pattern -- safe (suggested by @Kametrixom)
// url is optional here
switch (year, url) {
case (1990...2015, let unwrappedUrl?):
print("Current year is \(year), go to: \(unwrappedUrl)")
// x? is just a shortcut for .some(x), so this is equivalent to
switch(year, x) {
case (1990...2015,.Some):
print("Current year is \(year), go to: \(x!)")
case (1990...2015, let .some(unwrappedUrl)):
print("Current year is \(year), go to: \(unwrappedUrl)")
}
Intermediate
Question #1 - Swift 1.0 or later
Is Swift an object-oriented language or a functional language? Solution
Swift is a hybrid language that supports both paradigms.
It implements the three fundamental principles of OOP:
- Encapsulation
- Inheritance
- Polymorphism
As for Swift being a functional language, there are different but equivalent ways to define it. One of the more common is on Wikipedia: "…a programming paradigm [...] that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data." It's hard to argue that Swift is a full-fledged functional language, but it has the basics.
Question #2 - Swift 1.0 or later
Which of the following features are included in Swift? Generic classes Generic structs Generic protocols
Solution Inside: Answer Hide 1 and 2 are included. Generics can be used in classes, structs, enumerations, global functions and methods. 3 is partially implemented via typealias. It's not a generic type per se, it's a placeholder name. It's often referred to as an associated type and it's defined when a protocol is adopted by a type.
protocol Animal {
associatedtype F: Food
func eat(_ food: F)
}
struct Tiger: Animal {
func eat(_ food: Meat) {
print("eat \(meat)")
}
}
struct Sheep: Animal {
func eat(_ food: Grass) {
print("eat \(food)")
}
}
Question #3 - Swift 1.0 or later
In Objective-C, a constant can be declared like this: const int number = 0; Here is the Swift counterpart: let number = 0 Is there any difference between them? If yes, can you explain how they differ?
Solution A const is a variable initialized with a compile time value or an expression resolved at compilation time. An immutable created via let is a constant determined at runtime, and it can be initialized with the result being either a static or dynamic expression. Notice that its value can only be assigned once.
Question #4 - Swift 1.0 or later
To declare a static property or function you use the static modifier on value types. Here's an example for a structure: struct Sun { static func illuminate() {} } For classes, it's possible to use either the static or the class modifier. They achieve the same goal, but in reality they're different. Can you explain how they differ?
Solution
static makes a property or a function static and not overrideable. By using class, you can override the property or function.
When applied to classes, static becomes an alias for class final. For example, in this code the compiler will complain when you try to override illuminate():
class Star {
class func spin() {}
static func illuminate() {}
}
class Sun : Star {
override class func spin() {
super.spin()
}
override static func illuminate() { // error: class method overrides a 'final' class method
super.illuminate()
}
}
Question #5 - Swift 1.0 or later
Can you add a stored property by using an extension? Explain.
Solution No, it's not possible. An extension can be used to add new behavior to an existing type, but not to alter the type itself or its interface. If you add a stored property, you'd need extra memory to store the new value. An extension cannot manage such a task.
Despite of that, we can use a associate_object(runtime properties).
// MyClass.swift
class MyClass {
}
// MyClassExtension.swift
private var key: Void?
extension MyClass {
var title: String? {
get {
return objc_getAssociatedObject(self, &key) as? String
}
set {
objc_setAssociatedObject(self,
&key, newValue,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
// 测试
func printTitle(_ input: MyClass) {
if let title = input.title {
print("Title: \(title)")
} else {
print("没有设置")
}
}
let a = MyClass()
printTitle(a)
a.title = "Swifter.tips"
printTitle(a)
// 输出:
// 没有设置
// Title: Swifter.tips
key
Advanced
Question #1 - Swift 1.2
In Swift 1.2, can you explain the problem with declaring an enumeration with generic types? Take for example an Either enumeration with two generic types T and V, with T used as the associated value type for a Left case and V for a Right case:
enum Either
Pro tip: Inspect this case in an Xcode project, not in a Playground. Also notice that this question is related to Swift 1.2 so you'll need Xcode 6.4.
Solution Inside: Compilation fails with the following (cryptic) error message: unimplemented IR generation feature non-fixed multi-payload enum layout
The problem is that the memory size of T cannot be determined upfront because it depends on the T type itself, but enum cases require a fixed-sized payload. The most used workaround is to box the generic type into a reference type, conventionally called Box, as follows:
class Box<T> {
let value: T
init(_ value: T) {
self.value = value
}
}
enum Either<T, V> {
case Left(Box<T>)
case Right(Box<V>)
}
This problem affects only Swift 1.0 or later, but it has been solved in 2.0.
Question #2 - Swift 1.0 or later
Are closures value or reference types? Solution Closures are reference types. If a closure is assigned to a variable and the variable is copied into another variable, a reference to the same closure and its capture list is also copied.
Question #3 - Swift 1.0 or later
The UInt type is used to store unsigned integers. It implements the following initializer for converting from a signed integer: init(_ value: Int)
However, the following code generates a compile time error exception if you provide a negative value: let myNegative = UInt(-1)
Knowing that a negative number is internally represented, using two's complement as a positive number, how can you convert an Int negative number into an UInt while keeping its memory representation?
Solution Inside: Answer Hide There's an initializer for that: UInt(bitPattern: Int)
Question #4 - Swift 1.0 or later
Can you describe a situation where you might get a circular reference in Swift, and how you'd solve it?
A circular reference happens when two instances hold a strong reference to each other, causing a memory leak because none of two instances will ever be deallocated. The reason is that an instance cannot be deallocated if there's a strong reference to it, but each instance keeps the other alive because of its strong reference.
You'd solve the problem by breaking the strong circular reference by replacing one of the strong references with a weak or an unowned reference.
Question #5 - Swift 2.0 or later
Swift 2.0 features a new keyword to make recursive enumerations. Here is an example of such an enumeration with a Node case that takes two associated value types, T and List:
enum List<T> {
case Node(T, List<T>)
}
What's that keyword?
Solution Inside: It's the indirect keyword that allows for recursive enumeration cases like this:
enum List<T> {
indirect case Cons(T, List<T>)
}
在 enum 的定义中嵌套⾃⾝对于 class 这样的引⽤类型来说没有任何问题,但是对于像 struct 或者 enum 这样的值类型来说,普通的做法是不可⾏的。我们需要在定义前⾯加上 indirect 来提⽰编 译器不要直接在值类型中直接嵌套。⽤ enum 表达链表的好处在于,我们可以清晰地表⽰出空节 点这⼀定义,这在像 Swift 这样类型⼗分严格的语⾔中是很有帮助的。