Swift String VS. String! VS. String

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 @IBOutlets 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 to nil otherwise.

  • variable as String?: variable is a String?, but stored in an opaque type, such as AnyObject?, 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 to nil
  • as Type? means: cast to an optional Type, because I know it's an optional Type. 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 to nil if cast fails
  • as! is a forced cast, resulting to an runtime exception if cast fails (this is what as previously did)
  • as is now a special type of cast to be used when casting to equivalent types, usually bridged types, such as Swift's String and NSString.

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



Leave a reply



Submit