Swift 4 Cheat Sheet Advanced

weixin_33766168發表於2018-07-05

Swift 4 Cheat Sheet Advanced

@(程式設計筆記)[Swift]

Singleton

class MyManager {
    static let shared = MyManager()
    private init() {
    }
}

GCD

DispatchQueue.global().async {
  // Some async works
  DispatchQueue.main.async {
    // Update UI
  }
}

Selector

let timer = Timer(timeInterval: 1, target: object,
                  selector: #selector(MyClass.test),
                  userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
                 for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
             with: button, with: otherButton)

Cases where #selector doesn't work, and naming: Sometimes you don't have a function reference to make a selector with (for example, with methods dynamically registered in the ObjC runtime). In that case, you can construct a Selector from a string: e.g. Selector("dynamicMethod:") — though you lose the compiler's validity checking. When you do that, you need to follow ObjC naming rules, including colons (:) for each parameter.

Control Flow

Switch

let someCharacter: Character = "z"
switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
case "z":
    print("The last letter of the alphabet")
default:
    print("Some other character")
}
// Prints "The last letter of the alphabet"

https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html

Functions

Overloading

func output(x:Int) {
    print("The int value is \(x)")
}

func output(x:String) {
    print("The string value is \(x)")
}

Default value

@objc public class func checkNewAppVersionAvailable(
        userID: String = "",
        higherThan minBuildNo: Int = -1) -> Bool

let result = checker.checkNewAppVersionAvailable()

注意:
如果設定為nil,那麼會使用OC預設初始化值,以Bool為例就是false,並不會使用default value,也就是說,OC無法享受預設帶參的便利。

https://docs.swift.org/swift-book/LanguageGuide/Functions.html

Closures(block)

https://docs.swift.org/swift-book/LanguageGuide/Closures.html

// 宣告一個閉包(有兩個整形引數,且返回值為整形的閉包)
var sumClosure:((a: Int, b: Int) -> Int)

// 帶block方法宣告
public func checkNewAppVersionAvailable(
        success successCallback: @escaping ((_ hasNewVersion: Bool, _ info: [String: Any]?) -> Void),
        failure failureCallback: @escaping ((Error) -> Void))

// 方法呼叫
checker.checkNewAppVersionAvailable(success: { (_, _) in
            
}) { (_) in
            
}

Weak self

lazy var printName: ()->() = {
    [weak self] in
    if let strongSelf = self {
        print("The name is \(strongSelf.name)")
    }
}

Enumerations

enum Pet{
    case dog
    case cat
    case rabbit
}

var hisPet = Pet.dog
var herPet: Pet = .dat

RawValue

enum Pet: String {
    case dog = "?"
    case cat = "?"
    case rabbit = "?"
}

var myPet = Pet.rabbit
print("my pet is a \(myPet.rawValue)")    //output: my pet is a ?

var timsPet = Pet(rawValue: "?")
print("Tim's pet is a \(timsPet.rawValue)")//output: Tim's pet is a ?

Properties

https://docs.swift.org/swift-book/LanguageGuide/Properties.html

Read only

public private(set) var hours: Int = 0

Lazy Stored Properties

lazy var zyTableView: UITableView = {
    let tempTableView = UITableView (frame: self.view.bounds, style: UITableViewStyle.Plain)
    tempTableView.delegate = self
    tempTableView.dataSource = self
    return tempTableView
}()

Computed Properties(Setter and Getter)

Computed properties do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.

Setters and Getters apply to computed properties; such properties do not have storage in the instance - the value from the getter is meant to be computed from other instance properties. In your case, there is no x to be assigned.(劃重點:private var _x: Int = 0,計算屬性不會自動生成_成員變數,只是提供 set 和 get 兩種方法)

Explicitly: "How can I do this without explicit backing ivars". You can't - you'll need something to backup the computed property.

https://stackoverflow.com/questions/24025340/property-getters-and-setters

class Point {
  private var _x: Int = 0             // _x -> backingX, won't be created automatically like those in Objective-C
  var x: Int {
    set { _x = 2 * newValue } // newValue is the default name of new value
    get { return _x / 2 }
  }
}

Property Observers(willSet 和 didSet)

willSet didSet屬性觀測用來更新關聯屬性

struct Circle {
    var radius: Double = 0 {
        didSet {
            perimeter = 2 * Double.pi * radius
        }
    }
    private(set) var perimeter: Double = 0
}
var circle = Circle()
circle.radius = 2
print(circle.perimeter) //output: 12.5663706143592

想在一個屬性定義中同時出現 set 和 willSet 或 didSet 是一件辦不到的事情。計算屬性中我們可以通過改寫 set 中的內容來達到和 willSet 及 didSet 同樣的屬性觀察的目的。

http://swifter.tips/property-observer/

Optional Chaining

https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html

OC介面橋接

OC 的介面橋接到 Swift 後預設都是nonnull,這點和OC不一樣,OC的所有指標都是預設可空的。
為了橋接適配 Swift,可空需要加nullable識別符號
eg:

@interface Action : UIButton
+ (instancetype)actionWithTitle:(nullable NSString *)title handler:(void (^ __nullable)(Action *action))handler;
@property (nullable, nonatomic, readonly) NSString *title;
@property (nonatomic, getter=isEnabled) BOOL enabled;
@end

生成的介面檔案如下:

open class Action : UIButton {
    public convenience init!(title: String?, handler: ((Action?) -> Swift.Void)? = nil)
    open var title: String? { get }
    open var isEnabled: Bool
}

Unwrapping

使用?來訪問一個Optional值,let rooms = person.residence?.numberOfRooms

注意:當未對可選值做處理時,Xcode會在可選值上建議你用!強制解包,不要聽它的
注意:當未對可選值做處理時,Xcode會在可選值上建議你用!強制解包,不要聽它的
注意:當未對可選值做處理時,Xcode會在可選值上建議你用!強制解包,不要聽它的

let john = Person()
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error

正確姿勢:
宣告

class Person {
    var residence: Residence?
}
class Residence {
    var numberOfRooms = 1
}
let person = Person()

使用 guard let

let john = Person()
guard let roomCount = john.residence?.numberOfRooms else {
    print("Unable to retrieve the number of rooms.")
}
print("John's residence has \(roomCount) room(s).")
// Prints "Unable to retrieve the number of rooms."

或者if let

let john = Person()
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."

建議用guard,在流程上更能表現出解包失敗是非預期行為,而且減少了程式碼層級。

Default Value

let rooms = person.residence?.numberOfRooms ?? 5 // output: 5

Error Handling

https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html

二級指標

@interface PDModuleManager (Handle)
- (id)invokeAction:(NSString *)action forModule:(NSString *)module withArguments:(NSArray *)arguments error:(NSError **)error;
@end

OC的(NSError **)會被自動轉換為throws -> Any

open func invokeAction(_ action: String!, forModule module: String!, withArguments arguments: [Any]!) throws -> Any

雖然 Foundation 裡有定義 public typealias NSErrorPointer = AutoreleasingUnsafeMutablePointer<NSError?>?來指代二級指標,但是由於上述的轉換,所以並不常用

Enum - the modern way

public enum MoyaError: Swift.Error {
    /// Indicates a response failed to map to an image.
    case imageMapping(Response)
    /// Indicates a response failed to map to a JSON structure.
    case jsonMapping(Response)
    /// Indicates a response failed to map to a String.
    case stringMapping(Response)
    /// Indicates a response failed to map to a Decodable object.
    case objectMapping(Swift.Error, Response)
}

func canThrowErrors() throws -> String
func cannotThrowErrors() -> String

do {
    let urlRequest = try endpoint.urlRequest()
    closure(.success(urlRequest))
} catch MoyaError.requestMapping(let url) {
    closure(.failure(MoyaError.requestMapping(url)))
} catch MoyaError.parameterEncoding(let error) {
    closure(.failure(MoyaError.parameterEncoding(error)))
} catch {
    closure(.failure(MoyaError.underlying(error, nil)))
}

Type Casting

https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html

Downcasting

for item in library {
    if let movie = item as? Movie {
        print("Movie: \(movie.name), dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: \(song.name), by \(song.artist)")
    }
}

// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley

Type Casting for Any and AnyObject

var things = [Any]()

things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })

for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}

// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael

Extensions

https://docs.swift.org/swift-book/LanguageGuide/Extensions.html

extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements goes here
}

Protocols

protocol Vehicle {
    var numberOfWheels: Int { get }     //readonly
    var color: UIColor { get set }      //read-write
}

Optional

https://stackoverflow.com/questions/48837915/optional-protocol-methods-in-swift-without-using-objc

OP1

You can define default func implementation by:

protocol Opt {
    func requiredFunc()
    func optionalFunc()
}

extension Opt {
    func optionalFunc() {}
}

With this you don't have to implement optionalFunc() in classes conforming to Opt, because they already have their default implementation.

OP2

Using @objc in swift, we can create an optional methods inside protocol like

@objc protocol MyProtocol {
  @objc optional func anOptionalMethod()
}

Declaring Protocol Adoption with an Extension

struct Hamster {
    var name: String
    var textualDescription: String {
        return "A hamster named \(name)"
    }
}
extension Hamster: TextRepresentable {}

Access Control

  • Open
  • Public
  • Internal
  • File-private
  • Private

相關文章