Swift string vs. string! vs. string?
For your PancakeHouse
struct, name
is non-optional. That means that its name
property cannot be nil. The compiler will enforce a requirement that name
be initialized to a non-nil value anytime an instance of PancakeHouse
is initialized. It does this by requiring name
to be set in any and all initializers defined for PancakeHouse
.
For @IBOutlet
properties, this is not possible. When an Interface Builder file (XIB or Storyboard) is unarchived/loaded, the outlets defined in the IB file are set, but this always occurs after the objects therein have been initialized (e.g. during view loading). So there is necessarily a time after initialization but before the outlets have been set, and during this time, the outlets will be nil. (There's also the issue that the outlets may not be set in the IB file, and the compiler doesn't/can't check that.) This explains why @IBOutlet
properties must be optional.
The choice between an implicitly unwrapped optional (!
) and a regular optional (?
) for @IBOutlet
s is up to you. The arguments are essentially that using !
lets you treat the property as if it were non-optional and therefore never nil. If they are nil for some reason, that would generally be considered a programmer error (e.g. outlet is not connected, or you accessed it before view loading finished, etc.), and in those cases, failing by crashing during development will help you catch the bug faster. On the other hand, declaring them as regular optionals, requires any code that uses them to explicitly handle the case where they may not be set for some reason. Apple chose implicitly unwrapped as the default, but there are Swift programmers who for their own reasons, use regular optionals for @IBOutlet
properties.
What's the difference between [String] vs. [(String)]?
[String] is an array of strings, while [(String)] is an array of tuples which contain 1 string.
let arr1 = [("test")]
arr1[0].0 // contains "test"
let arr2 = ["test"]
arr2[0] // contains "test"
A more useful array of tuples might be something like:
let arr1: [(id: Int, name: String)] = [(12, "Fred"), (43, "Bob")]
arr1[0].name // contains "Fred"
What is the difference between String? and String! (two ways of creating an optional variable)?
The real benefit to using implicitly unwrapped optionals (declared with the !) is related to class initialisation when two classes point to each other and you need to avoid a strong-reference cycle. For example:
Class A <-> Class B
Class A's init routine needs to create (and own) class B, and B needs a weak reference back to A:
class A {
let instanceOfB: B!
init() {
self.instanceOfB = B(instanceOfA: self)
}
}
class B {
unowned let instanceOfA: A
init(instanceOfA: A) {
self.instanceOfA = instanceOfA
}
}
Now,
- Class B needs a reference to class A to be initialised.
- Class A can only pass
self
to class B's initialiser once it's fully initialised. - For Class A to be considered as initialised before Class B is created, the property
instanceOfB
must therefore be optional.
However, once A's been created it would be annoying to have to access instanceOfB using instanceOfB! since we know that there has to be a B
To avoid this, instanceOfB is declared as an implicity unwrapped optional (instanceOfB!), and we can access it using just instanceOfB. (Furthermore, I suspect that the compiler can optimise the access differently too).
An example of this is given on pages 464 to 466 of the book.
Summary:
- Use ? if the value can become nil in the future, so that you test for this.
- Use ! if it really shouldn't become nil in the future, but it needs to be nil initially.
Difference between [String] and Array String
From iOS Developer Library on Swift...
The type of a Swift array is written in full as Array< Element >, where
Element is the type of values the array is allowed to store. You can
also write the type of an array in shorthand form as [Element].
Although the two forms are functionally identical, the shorthand form
is preferred and is used throughout this guide when referring to the
type of an array.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html
What is the difference between [[String:String]]! and [[String:String]]() in Swift
[[String: String]]()
is an object of Array of type [[String: String]]
.
Where as [[String: String]]!
is an arrayType of type [[String: String]]
which can't be nil while executing because it is an implicitly unwrapped optional.
You are getting Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file
because items
was never initialised. But you are trying to add an item to items
which is actually a null value. You can solve this error by replacing items
declaration with the below code.
var items: [[String: String]] = [[String: String]]()
And the declaration of itemsNew is also wrong. You can declare it as below.
var itemsNew = [[String: String]]()
or
var itemsNew: [[String: String]] = [[String: String]]()
Swift: difference as String? vs. as? String
There's a subtle but important difference:
variable as? String
:variable
can be any type, such as an array, an integer, etc. Cast to string if it's a string, set tonil
otherwise.variable as String?
:variable
is aString?
, but stored in an opaque type, such asAnyObject?
, or it's a non optional string. If it's something different, a runtime exception is generated.
Some examples:
var x: AnyObject? = "Test"
x as String? // OK, the result is an optional string
x as? String // OK, evaluates to an optional string
"string" as String? // OK, evaluates to optional string
x as? Int // OK, evaluates to nil, it's not an Int
x as Int? // Runtime exception, it's not castable to optional Int
So:
as? Type
means: cast to this type, if possible, otherwise evaluate tonil
as Type?
means: cast to an optionalType
, because I know it's an optionalType
. I understand that if it's not that, a runtime exception is generated
However, the real difference is between as?
and as
: the former is an attempt to cast, the latter is a forced cast, resulting in runtime error if not possible.
Update Dec 14, 2015 Since Swift 1.2, there are 3 variations of the as
operator:
as?
is an attempt to cast, evaluating tonil
if cast failsas!
is a forced cast, resulting to an runtime exception if cast fails (this is whatas
previously did)as
is now a special type of cast to be used when casting to equivalent types, usually bridged types, such as Swift'sString
andNSString
.
Difference between String interpolation and String concatenation
From a speed point of view, to differentiate concatenation (value1 + "-" + value2
) and interpolation ("\(value1)-\(value2)"
), results may depend on the number of operations done to obtain the final string.
My results on an iPhone 8 show that:
- if there is roughly < 30 substrings to attach together, then concatenation is faster
- if there is roughly > 30 substrings to attach together, then interpolation is faster
Thank you Sirens for figuring out that one wasn't always faster than the other!
Try it yourself (and don't forget to adapt the tested character set and iterations for your needs):
import UIKit
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.global(qos: .default).async {
ViewController.buildDataAndTest()
}
}
private static func buildDataAndTest(times: Int = 1_000) {
let characterSet = CharacterSet.alphanumerics
characterSet.cacheAllCharacters()
let data: [(String, String)] = (0 ..< times).map { _ in
(characterSet.randomString(length: 50), characterSet.randomString(length: 20))
}
_ = testCIA(data)
_ = testInterpol(data)
print("concatenation: " + String(resultConcatenation))
print("interpolation: \(resultInterpolation)")
}
/// concatenation in array
static var resultConcatenation: CFTimeInterval = 0
private static func testCIA(_ array: [(String, String)]) -> String {
var foo = ""
let start = CACurrentMediaTime()
for (a, b) in array {
foo = foo + " " + a + "+" + b
}
resultConcatenation = CACurrentMediaTime() - start
return foo
}
/// interpolation
static var resultInterpolation: CFTimeInterval = 0
private static func testInterpol(_ array: [(String, String)]) -> String {
var foo = ""
let start = CACurrentMediaTime()
for (a, b) in array {
foo = "\(foo) \(a)+\(b)"
}
resultInterpolation = CACurrentMediaTime() - start
return foo
}
}
extension CharacterSet {
static var cachedCharacters: [Character] = []
public func cacheAllCharacters() {
CharacterSet.cachedCharacters = characters()
}
/// extracting characters
/// https://stackoverflow.com/a/52133647/1033581
public func characters() -> [Character] {
return codePoints().compactMap { UnicodeScalar($0) }.map { Character($0) }
}
public func codePoints() -> [Int] {
var result: [Int] = []
var plane = 0
for (i, w) in bitmapRepresentation.enumerated() {
let k = i % 8193
if k == 8192 {
plane = Int(w) << 13
continue
}
let base = (plane + k) << 3
for j in 0 ..< 8 where w & 1 << j != 0 {
result.append(base + j)
}
}
return result
}
// http://stackoverflow.com/a/42895178/1033581
public func randomString(length: Int) -> String {
let charArray = CharacterSet.cachedCharacters
let charArrayCount = UInt32(charArray.count)
var randomString = ""
for _ in 0 ..< length {
randomString += String(charArray[Int(arc4random_uniform(charArrayCount))])
}
return randomString
}
}
Related Topics
Convert Firdatasnapshot to Custom Type
Cannot Use Mutating Member on Immutable Value of Type
!? Strange Double Unwrapped Optional Syntax in For_In []
How to Apply Borders and Corner Radius to Uibarbuttonitem
How to Install the Alamofire 4.0 in Xcode 8.0
Access Properties via Subscripting in Swift
Swift Difference Between Final Var and Non-Final Var | Final Let and Non-Final Let
How to Properly Implement the Equatable Protocol in a Class Hierarchy
How to Restrict the Type That a Function Throws in Swift
Result Values in '? :' Expression Have Mismatching Types 'Some View' and '...'
Creating a Custom Scngeometry Polygon Plane with Scngeometryprimitivetype Polygon Crash/Error
Adding a Search Bar to Navigationview in Swiftui
Dyld: Library Not Loaded: @Rpath/Libswiftcore.Dylib Problem with New Xcode (10.2)
Difference Between Println and Print in Swift
Difference Between? and ! in Swift Language
"Ambiguous Use of 'Propertyname'" Error Given Overridden Property with Didset Observer