[关闭]
@Xiaojun-Jin 2015-03-27 09:31 字数 8241 阅读 1340

Swift Notes

Swift


Generics (type)

  1. struct Pair<T: Equatable> {
  2. let a: T!
  3. let b: T!
  4. init(a: T, b: T) {
  5. self.a = a
  6. self.b = b
  7. }
  8. func equal() -> Bool {
  9. return a == b
  10. }
  11. }
  12. let pair = Pair(a: 5, b: 10)
  13. pair.a // 5
  14. pair.b // 10
  15. pair.equal() // false
  16. let floatPair = Pair(a: 3.14159, b: 2.0)
  17. floatPair.a // 3.14159
  18. floatPair.b // 2.0
  19. floatPair.equal() // false

This struct now can take parameters of any types (int, float or else) by using Generics.

Containers

  1. // automatically inferred
  2. let array = [1, 2, 3, 4]
  3. let dictionary = ["dog": 1, "elephant": 2]
  1. // fully defining
  2. let array: Array<Int> = [1, 2, 3, 4]
  3. let dictionary: Dictionary<String, Int> = ["dog": 1, "elephant": 2]
  1. // short form
  2. let array: [Int] = [1, 2, 3, 4]
  3. let dictionary: [String: Int] = ["dog": 1, "elephant": 2]

Switch Statements

Instead of using the isEqualToString: method, in Swift you are able to directly use == to compare strings.

  1. switch person.name {
  2. case "Matt Galloway":
  3. println("Author of an interesting Swift article")
  4. case "Ray Wenderlich":
  5. println("Has a great website")
  6. case "Tim Cook":
  7. println("CEO of Apple Inc.")
  8. default:
  9. println("Someone else")
  10. }

There are no breaks in sight. That's because cases in switches no longer fall through to the next one!

  1. switch i {
  2. case 0, 1, 2:
  3. println("Small")
  4. case 3...7:
  5. println("Medium")
  6. case 8..<10:
  7. println("Large")
  8. case _ where i % 2 == 0:
  9. println("Even")
  10. case _ where i % 2 == 1:
  11. println("Odd")
  12. default:
  13. break
  14. }

Here break is added to declare that default will do noting. Note that switch also has the ability to define a case as a calculation of the input.

Closure

Closure expression syntax has the following general form:

{ (parameters) -> return type in statements }

  1. { (s1: String, s2: String) -> Bool in return s1 > s2 }

If the return types can be inferred (which is often the case), the return arrow (->), the parentheses, and keyword return can also be omitted.

{ parameters in statements}

  1. { s1, s2 in s1 > s2 }
  2. // Shorthand Argument Names
  3. { $0 > $1 }

Struct or Class?

An array that contains [a, b, c] is really the same as another array that contains [a, b, c] and they are completely interchangeable. It doesn't matter whether you use the first array or the second, because they represent the exact same thing. That's why arrays are structs.

You would model a person as a class because two person objects are two different things. Just because two people have the same name and birthdate, doesn't mean they are the same person.


Protocol Conformance

Perhaps you've seen a class declaration like this before:

  1. class MyViewController: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate,
  2. UINavigationControllerDelegate,
  3. UIImagePickerControllerDelegate, UITextFieldDelegate

We won't judge you for having such a busy view controller, but we do suggest having each protocol conformance declared in its own extension:

  1. class MyViewController {
  2. // Standard view controller stuff here
  3. }
  4. extension MyViewController: UITableViewDelegate {
  5. // Table view delegate methods here
  6. }
  7. extension MyViewController: UITextFieldDelegate {
  8. // Text field delegate methods here
  9. }
  10. // etc.

This keeps things organized and helps keep related methods together. It also means you can add a protocol and related methods in one step in one place, rather than editing the class declaration and then adding methods somewhere to a potentially crowded list of methods.

Init

  1. let tap: UITapGestureRecognizer
  2. // error: Property 'tap' not initialized at super.init call
  3. let tap: UITapGestureRecognizer?
  4. // defined as optional value to avoid force initializing in init function

Function without return value

  1. // no return value, thus no return arrow (->) is included
  2. func sayGoodbye(personName: String) {
  3. println("Goodbye, \(personName)!")
  4. }

Functions without a defined return type return a special value of type Void. This is simply an empty tuple, in effect a tuple with zero elements, which can be written as ().

Functional Programming

  1. func isEven(number: Int) -> Bool {
  2. return number % 2 == 0
  3. }
  4. evens = Array(1...10).filter(isEven)
  5. println(evens)

Functional filtering: Array(1...10) creates an Array 1...10 creates a Range. The filter statement creates and returns a new array that contains only the items for which the given function returns true.

  1. evens = Array(1...10).filter { (number) in number % 2 == 0 }
  2. println(evens)

Functions are just named closures. Compiler infers the type of the parameter number and return types of the closure from its usage context.

  1. evens = Array(1...10).filter { $0 % 2 == 0 }
  2. println(evens)

The above uses argument shorthand notation, implicit returns, type inference!

Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure's arguments by the names $0, $1, $2, and so on.

Generic Function:

  1. func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] {
  2. var result = [T]()
  3. for i in source {
  4. if predicate(i) {
  5. result.append(i)
  6. }
  7. }
  8. return result
  9. }
  10. evens = myFilter(Array(1...10)) { $0 % 2 == 0 }
  11. println(evens)
  12. // or calling like this:
  13. evens = myFilter(Array(1...10)) { number in number % 2 == 0 }
  14. println(evens)

If generic function takes two function parameters:

  1. func myFilter<T>(source: [T], predicate:(T) -> Bool, tFun:() -> ()) -> [T] {
  2. var result = [T]()
  3. for i in source {
  4. if predicate(i) {
  5. result.append(i)
  6. }
  7. }
  8. tFun()
  9. return result
  10. }
  11. func isEven(number: Int) -> Bool {
  12. return number % 2 == 0
  13. }
  14. func tFun() -> () {
  15. println("I am Rylan.")
  16. }
  1. let evens = myFilter(Array(1...10), isEven, tFun)
  2. // alternatively
  3. let evens = myFilter(Array(1...10), { $0 % 2 == 0 }, {println("I am Rylan.")})

If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a Trailing Closure instead.

  1. // trailing closures
  2. let evens1 = myFilter(Array(1...10),{ nuber in nuber % 2 == 0 }) { println("dd") }

Functional Reduce

  1. // func reduce<U>(initial: U, combine: (U, T) -> U) -> U
  2. // reduce(0) {...} trailing closures style
  3. // 'typealias Element = T', so 'T' refers to array element?
  4. evenSum = Array(1...10)
  5. .filter { (number) in number % 2 == 0 }
  6. .reduce(0) { (total, number) in total + number }
  7. // concise version
  8. evenSum = Array(1...10)
  9. .filter { $0 % 2 == 0 }
  10. .reduce(0) { $0 + $1 }
  11. println(evenSum)
  1. let numbers = Array(1...10)
  2. .reduce("numbers: ") {(total, number) in total + "\(number) "} /*{ $0 + "\($1) " }*/
  3. println(numbers)

Building an Index

Imperative method:

  1. let words = ["Cat", "Chicken", "fish", "Dog",
  2. "Mouse", "Guinea Pig", "monkey"]
  3. // Using a typealias makes the code more readable
  4. typealias Entry = (Character, [String])
  5. func buildIndex(words: [String]) -> [Entry] {
  6. var result = [Entry]()
  7. var letters = [Character]()
  8. for word in words {
  9. let firstLetter = Character(word.substringToIndex(
  10. advance(word.startIndex, 1)).uppercaseString)
  11. if !contains(letters, firstLetter) {
  12. letters.append(firstLetter)
  13. }
  14. }
  15. for letter in letters {
  16. var wordsForLetter = [String]()
  17. for word in words {
  18. let firstLetter = Character(word.substringToIndex(
  19. advance(word.startIndex, 1)).uppercaseString)
  20. if firstLetter == letter {
  21. wordsForLetter.append(word)
  22. }
  23. }
  24. result.append((letter, wordsForLetter))
  25. }
  26. return result
  27. }
  28. println(buildIndex(words))

Functional Way:

  1. func buildIndex(words: [String]) -> [Entry] {
  2. let letters = words.map {
  3. (word) -> Character in
  4. Character(word.substringToIndex(advance(word.startIndex, 1)
  5. ).uppercaseString)
  6. }
  7. // omit return type (->)
  8. /*
  9. let letters = words.map {
  10. (word) in
  11. Character(word.substringToIndex(advance(word.startIndex, 1)
  12. ).uppercaseString)
  13. }
  14. */
  15. // concise version
  16. /*
  17. let letters = words.map {
  18. Character($0.substringToIndex(advance($0.startIndex, 1)
  19. ).uppercaseString)
  20. }
  21. */
  22. println(letters) // [C, C, F, D, M, G, M]
  23. return [Entry]()
  24. }

map creates a new array with the results of calls to the supplied closure for each element in the supplied array. You use map to perform transformations; in this case, map transforms an array of type [String] into an array of type [Character].

  1. func distinct<T: Equatable>(source: [T]) -> [T] {
  2. var unique = [T]()
  3. for item in source {
  4. if !contains(unique, item) {
  5. unique.append(item)
  6. }
  7. }
  8. return unique
  9. }

distinct iterates over all the items in an array, building a new array that contains only the unique items.

Update buildIndex as follows:

  1. func buildIndex(words: [String]) -> [Entry] {
  2. let letters = words.map {
  3. (word) -> Character in
  4. Character(word.substringToIndex(advance(word.startIndex, 1)
  5. ).uppercaseString)
  6. }
  7. let distinctLetters = distinct(letters)
  8. return distinctLetters.map {
  9. (letter) -> Entry in
  10. return (letter, words.filter {
  11. (word) -> Bool in
  12. Character(word.substringToIndex(advance(word.startIndex, 1)
  13. ).uppercaseString) == letter
  14. })
  15. }
  16. }

Concise version (create func firstLetter to remove duplicate code):

  1. func buildIndex(words: [String]) -> [Entry] {
  2. func firstLetter(str: String) -> Character {
  3. return Character(str.substringToIndex(
  4. advance(str.startIndex, 1)).uppercaseString)
  5. }
  6. let letters = words.map {
  7. (word) -> Character in
  8. firstLetter(word)
  9. }
  10. let distinctLetters = distinct(letters)
  11. return distinctLetters.map {
  12. (letter) -> Entry in
  13. return (letter, words.filter {
  14. (word) -> Bool in
  15. firstLetter(word) == letter
  16. })
  17. }
  18. }

func filter(includeElement: (T) -> Bool) -> [T] // Return an Array containing the elements x of self for which includeElement(x) is true

Even more concise version:

  1. func buildIndex(words: [String]) -> [Entry] {
  2. func firstLetter(str: String) -> Character {
  3. return Character(str.substringToIndex(
  4. advance(str.startIndex, 1)).uppercaseString)
  5. }
  6. return distinct(words.map(firstLetter))
  7. .map {
  8. (letter) -> Entry in
  9. return (letter, words.filter {
  10. (word) -> Bool in
  11. firstLetter(word) == letter
  12. })
  13. }
  14. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注