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
来指代二级指针,但是由于上述的转换,所以并不常用
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