TOC
Must-know information about Swift
entry point
main?
print log
print("Hello swift")
let name = "swift"
print("Hello, \(name)")
print("Hello, \(name1 ?? name)")// If the optional value is missing, the default value is used instead.
Variable, Constant
Int:
- On a 32-bit platform, Int is the same size as Int32.
- On a 64-bit platform, Int is the same size as Int64.
UInt:
- On a 32-bit platform, UInt is the same size as UInt32.
- On a 64-bit platform, UInt is the same size as UInt64.
Double represents a 64-bit floating-point number.
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
Float represents a 32-bit floating-point number.
var myVariable = 42
myVariable = 50
let explicitDouble: Double = 70 // let explicitDouble: Double = 70
let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
// Prints "Hello, friend!"
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)
- Lazy Stored Properties
class DataImporter {
/*
DataImporter is a class to import data from an external file.
The class is assumed to take a nontrivial amount of time to initialize.
*/
var filename = "data.txt"
// the DataImporter class would provide data importing functionality here
}
class DataManager {
lazy var importer = DataImporter()
var data: [String] = []
// the DataManager class would provide data management functionality here
}
Properties
class DataImporter {
/*
DataImporter is a class to import data from an external file.
The class is assumed to take a nontrivial amount of time to initialize.
*/
var filename = "data.txt"
// the DataImporter class would provide data importing functionality here
}
class DataManager {
lazy var importer = DataImporter()
var data: [String] = []
// the DataManager class would provide data management functionality here
}
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// the DataImporter instance for the importer property hasn't yet been created
print(manager.importer.filename)
// the DataImporter instance for the importer property has now been created
// Prints "data.txt"
Computed Properties
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
// initialSquareCenter is at (5.0, 5.0)
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Prints "square.origin is now at (10.0, 10.0)"
struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
// 属性观察者
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
(StaticConfiguration)[https://developer.apple.com/documentation/widgetkit/staticconfiguration]
struct LeaderboardWidget: Widget {
public var body: some WidgetConfiguration {
StaticConfiguration(kind: EmojiRanger.LeaderboardWidgetKind, provider: LeaderboardProvider()) { entry in // I don't understand this line!
LeaderboardWidgetEntryView(entry: entry)
}
.configurationDisplayName("Ranger Leaderboard")
.description("See all the rangers.")
.supportedFamilies(LeaderboardWidget.supportedFamilies)
}
}
This Swift code defines a widget for the WidgetKit framework. Let’s break it down:
-
struct LeaderboardWidget: Widget
: Defines a structure namedLeaderboardWidget
that conforms to theWidget
protocol. This indicates that it is a widget that can be used with the WidgetKit framework. -
public var body: some WidgetConfiguration { ... }
: This declares the body of the widget, specifying the widget configuration. Thebody
property is a computed property of typesome WidgetConfiguration
, indicating that it returns a value conforming to theWidgetConfiguration
protocol. Thesome
keyword is used for opaque return types. -
StaticConfiguration(kind: EmojiRanger.LeaderboardWidgetKind, provider: LeaderboardProvider()) { entry in ... }
: This sets up a static configuration for the widget. It specifies the kind of widget usingEmojiRanger.LeaderboardWidgetKind
and the data provider for the widget usingLeaderboardProvider()
. The closure inside is executed to provide the entry view for the widget. -
LeaderboardWidgetEntryView(entry: entry)
: Creates an instance ofLeaderboardWidgetEntryView
using the providedentry
. This is the view that will be displayed in the widget. -
.configurationDisplayName("Ranger Leaderboard")
: Sets the display name for the widget configuration. In this case, it is “Ranger Leaderboard.” -
.description("See all the rangers.")
: Sets the description for the widget configuration. In this case, it is “See all the rangers.” -
.supportedFamilies(LeaderboardWidget.supportedFamilies)
: Specifies the widget families supported by this widget. ThesupportedFamilies
property is presumably defined in theLeaderboardWidget
struct.
In summary, this Swift code defines a widget named LeaderboardWidget
with a static configuration, specifying the display name, description, and supported families. The widget uses LeaderboardProvider
to provide data, and the entry view is defined in LeaderboardWidgetEntryView
.
Property Wrapper
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct SmallRectangle {
@TwelveOrLess var height: Int // 上面定义了TwelveOrLess
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"
rectangle.height = 10
print(rectangle.height)
// Prints "10"
rectangle.height = 24
print(rectangle.height)
// Prints "12"
Type Aliases
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
if let
if let actualNumber = Int(possibleNumber) {
print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
print("The string \"\(possibleNumber)\" couldn't be converted to an integer")
}
// If possibleNumber is the string "abc", the output of your Swift code snippet will be:
The string "abc" couldn't be converted to an integer
// If possibleNumber is the string "333", and the code is executed as follows:
The string "333" has an integer value of 333
//---------------if let---------start
let myNumber = Int(possibleNumber)
// Here, myNumber is an optional integer
if let myNumber = myNumber {
// Here, myNumber is a non-optional integer
print("My number is \(myNumber)")
}
// Prints "My number is 123"
// 以上代码等于:
if let myNumber {
print("My number is \(myNumber)")
}
// Prints "My number is 123"
//---------------if let---------end
//The following if statements are equivalent:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// Prints "4 < 42 < 100"
Control Flow
if score > 50 {//
teamScore += 3
} else {
teamScore += 1
}
var optionalName: String? = "John Appleseed" //
var greeting = "Hello!"
if let name = optionalName { // If the optional value is nil, the conditional is false
greeting = "Hello, \(name)"
}
var n = 2
while n < 100 {
n *= 2
}
var n = 2
while n < 100 {
n *= 2
}
print(n)
// Prints "128"
var m = 2
repeat {
m *= 2
} while m < 100
print(m)
// Prints "128"
- Branch Statements
guard-statement → guard condition-list else code-block
guard (settings.authorizationStatus == .authorized) ||
(settings.authorizationStatus == .provisional) else { return }
https://docs.swift.org/swift-book/ReferenceManual/Statements.html
String
Concatenate, split to array, start with, end with
"red pepper".hasSuffix("pepper"):
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"
//
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
Array(List), dictonary
create, foreach, append, delete, update,
- create
// create empty list
let emptyArray: [String] = []
let emptyDictionary: [String: Float] = [:]
fruits = []
occupations = [:]
var fruits = ["strawberries", "limes", "tangerines"]
let arr = 0..<4;
fruits.append("blueberries") // append
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
foreach list
let individualScores = [75, 43, 103, 87, 12]
for score in individualScores {
print(score)
}
var total = 0
for i in 0..<4 {
total += i
}
print(total)
// Prints "6"
foreach dictionary:
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
// Prints "25"
Function
Syntax:
func methodName(externalName internalName: Type) {
// method body
}
Using Function Types
var mathFunction: (Int, Int) -> Int = addTwoInts
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
var mathFunction: (Int, Int) -> Int = addTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"
//
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"
Function Types as Return Types
You can use a function type as the return type of another function. You do this by writing a complete function type immediately after the return arrow (->) of the returning function.
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
func chooseStepFunction(backward: Bool) -> (Int) -> Int {// (Int) -> Int 是返回函数
return backward ? stepBackward : stepForward
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function
//
//
print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!
Each function parameter has both an argument label and a parameter name. The argument label is used when calling the function; each argument is written in the function call with its argument label before it.
Each function parameter has both an argument label and a parameter name. The argument label is used when calling the function; each argument is written in the function call with its argument label before it. The parameter name is used in the implementation of the function. By default, parameters use their parameter name as their argument label
Functions With an Implicit Return
func greeting(for person: String) -> String {// for是函数外部参数名,person函数内部参数名
"Hello, " + person + "!" // 只有一句时隐含返回
}
print(greeting(for: "Dave"))
// Prints "Hello, Dave!"
// ----------
func someFunction(argumentLabel parameterName: Int) {
// In the function body, parameterName refers to the argument value
// for that parameter.
}
func greet(person: String, from hometown: String) -> String {
return "Hello \(person)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill! Glad you could visit from Cupertino."
//
//
// A variadic parameter accepts zero or more values of a specified type. 多参数
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
Omitting Argument Labelsin page link
If you don’t want an argument label for a parameter, write an underscore (_)
instead of an explicit argument label for that parameter.
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// In the function body, firstParameterName and secondParameterName
// refer to the argument values for the first and second parameters.
}
someFunction(1, secondParameterName: 2)
What does didReceive
means?
override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
if response.actionIdentifier == "FLUTTER_ACTION" {
makeFlutterMethodCall()
}
completionHandler()
}
didReceive is an argument label for method calling from outside.
import Flutter
import UIKit
public class HelloPlugin: NSObject, FlutterPlugin {
// The external parameter name `with` makes the method call more descriptive and easier to understand. It reads like "register with this FlutterPluginRegistrar instance".
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "hello_plugin", binaryMessenger: registrar.messenger())
let instance = HelloPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
}
// In the case of the register method, both of these calls are valid:
// Using the external parameter label 'with'
HelloPlugin.register(with: someFlutterPluginRegistrar) // Valid
// Using the internal parameter name 'registrar' directly
HelloPlugin.register(registrar: someFlutterPluginRegistrar) // Valid
Functions can be nested
// Use -> to separate the parameter names and types from the function’s return type.
// write _ to use no argument label.
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")
- Functions can be nested.
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
func add() {
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
}
add()
return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
// Prints "120"
print(statistics.2)
// Prints "120"
return a function
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
A function can take another function as one of its arguments.
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)
In-Out Parameters
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"
Closures
Closure Expression Syntax
{ (<#parameters#>) -> <#return type#> in
<#statements#>
}
//
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
// equal
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
// equal: Inferring Type From Context
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
// equal: Implicit Returns from Single-Expression Closures
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
// equal: Shorthand Argument Names
reversedNames = names.sorted(by: { $0 > $1 } )// Here, $0 and $1 refer to the closure’s first and second String arguments. Because $1 is the shorthand argument with highest number, the closure is understood to take two arguments. Because the sorted(by:) function here expects a closure whose arguments are both strings, the shorthand arguments $0 and $1 are both of type String.
// equal: Operator Methods
reversedNames = names.sorted(by: >)
// equal: Trailing Closures
reversedNames = names.sorted() { $0 > $1 }
// equal:
reversedNames = names.sorted { $0 > $1 }
// If a closure expression is provided as the function’s or method’s only argument and you provide that expression as a trailing closure
numbers.map({ (number: Int) -> Int in
let result = 3 * number
return result
})
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
}
// `settings` is the instance;
}
Trailing Closures
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. You write a trailing closure after the function call’s parentheses, even though the trailing closure is still an argument to the function.
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
}
// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
// closure's body goes here
})
// Here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
What does ‘didFinishLaunchingWithOptions launchOptions’ have a space between?
Following codes is the content of AppDelegate.swift
:
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
GPT: The space between didFinishLaunchingWithOptions
and launchOptions
is simply a naming convention used for method parameter labels in Swift.
In Swift, method parameters have an external parameter name and an internal parameter name. The external parameter name is used to call the method, while the internal parameter name is used within the method implementation itself.
By default, Swift uses the internal parameter name as the external parameter name. However, you can provide a custom external name for each parameter by specifying it in the method signature.
In the case of the didFinishLaunchingWithOptions
method, didFinishLaunchingWithOptions
is used as an internal parameter name, and launchOptions
is used as an external parameter name.
By separating the parameter names with a space, the method signature becomes easier to read and understand.
In other words, the space between didFinishLaunchingWithOptions
and launchOptions
in the method signature is just a naming convention used to make the method signature more readable and descriptive.
What does @UIApplicationMain
mean in following codes?
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// This method is invoked on the UI thread.
// Handle battery messages.
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
@UIApplicationMain
is an attribute used in Swift to indicate that a particular class is the entry point for the application.
In the given code snippet, @UIApplicationMain
is applied to the AppDelegate
class, which means that it is the starting point of the application.
When you mark a class with the @UIApplicationMain
attribute, you are telling the system that this is the main entry point of your application. The system will then create an instance of this class and use it to run your application.
In the case of the AppDelegate
class, it is responsible for handling application lifecycle events, such as launching, backgrounding, foregrounding, terminating, and more.
In summary, @UIApplicationMain
is a Swift attribute that indicates which class is the main entry point for the application. When applied to a class, it tells the system to create an instance of it and use it to drive your app’s lifecycle.
What does week self
means in following code?
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "vip.timefriend.www/flutterToNative",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
guard call.method == "getBatteryLevel" else {
result(FlutterMethodNotImplemented)
return
}
self?.receiveBatteryLevel(result: result)
})
) -> Void in
Explain the parameters on a func: willPresent notification: UNNotification
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .sound])
}
GPT3.5:
The code willPresent notification: UNNotification is part of a method declaration in Swift. Let’s break it down:
willPresent: This is the name of the parameter. It represents the argument that will be passed to the method.
notification: This is the parameter type annotation. It specifies the type of the notification parameter, which is UNNotification. UNNotification is a class defined in the UserNotifications framework and represents a local or remote notification.
userNotificationCenter(_:willPresent:withCompletionHandler:)
, what does ‘:’ mean?
GPT4: The userNotificationCenter(_:willPresent:withCompletionHandler:)
method is a delegate method in the UNUserNotificationCenterDelegate
protocol. It’s used to handle the display of notifications when your app is in the foreground.
The colons (:
) in the method signature represent argument labels and parameters in Swift methods. Here’s a breakdown of what they mean:
-
userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
userNotificationCenter
: This is the method name.(_ center: UNUserNotificationCenter)
: The_
means there’s no external parameter name. This is the first parameter to the method and it’s of typeUNUserNotificationCenter
.willPresent notification: UNNotification
: This is the second parameter, namednotification
, of typeUNNotification
.withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
: This is the third parameter, namedcompletionHandler
, which is a closure that takes aUNNotificationPresentationOptions
value and returnsVoid
. The@escaping
attribute means the closure is allowed to escape, meaning it can be stored and called later.
In Swift, colons are used to separate parameter names from their types in method signatures. They’re also used to denote dictionary key-value pairs, and in conditional ternary operators, among other things. In this context, they’re being used to denote argument types in a method signature.
Class, Override, Overload
Construct and Deconstruction function
Classes have additional capabilities that structures don’t have:
-
Inheritance enables one class to inherit the characteristics of another.
-
Type casting enables you to check and interpret the type of a class instance at runtime.
-
Deinitializers enable an instance of a class to free up any resources it has assigned.
-
Reference counting allows more than one reference to a class instance.
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
let someResolution = Resolution()
let someVideoMode = VideoMode()
print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"
//
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
// Unlike value types, reference types are not copied when they’re assigned to a variable or constant, or when they’re passed to a function. Rather than a copy, a reference to the same existing instance is used.
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"
// In Swift, initializers in classes are categorized into two types: designated initializers and convenience initializers. Understanding the differences between them is key to correctly implementing class initialization.
-
Designated Initializer:
- Primary Role: A designated initializer is the primary initializer for a class. It is responsible for ensuring that all of the properties introduced by the class are fully initialized.
- Full Initialization: It must fully initialize all properties introduced by its class before calling a superclass initializer.
- Superclass Initializer Calling: For classes with a superclass, a designated initializer must call a designated initializer from its immediate superclass.
- Number per Class: Typically, a class will have few designated initializers (often only one), and it’s common for subclasses to modify or override these initializers.
-
Convenience Initializer:# must call another initializer
- Supporting Role: A convenience initializer is a secondary initializer that must call another initializer of the same class. It is used to provide a shortcut for a common or simplified initialization pattern.
- Delegating Within the Class: It delegates across to another initializer of the class. This delegation must ultimately lead to a designated initializer.
- Less Initialization Responsibility: A convenience initializer does not need to fully initialize the class. It relies on the class’s designated initializer(s) to do that.
- Optional: Convenience initializers are optional; a class may not have any.
Key Differences:
- Responsibility: Designated initializers must ensure that all properties of the class (including those from its superclass) are fully initialized. Convenience initializers are for simplifying or providing alternative ways to initialize the class.
- Initialization Path: Designated initializers must call up to a superclass’s designated initializer. Convenience initializers must call another initializer within the same class.
- Frequency of Use: A class usually has fewer designated initializers (often only one), while it can have multiple convenience initializers.
// Designated initializer and Convenience initializer
class MyClass: MySuperclass {
var newProperty: Int
// Designated initializer
init(newProperty: Int, fromSuper: Int) {
self.newProperty = newProperty
super.init(fromSuper: fromSuper)
}
// Convenience initializer
convenience init(completeInitialization: Int) {
self.init(newProperty: completeInitialization, fromSuper: completeInitialization)
}
}
Initializer Parameters Without Argument Labels (https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization)
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
struct StaticConfiguration<Content> where Content : View // `where`: In Swift, the where keyword is used in the context of generics and type constraints. It allows you to specify additional requirements on the generic type parameters or associated types.
struct StaticConfiguration<Content> where Content : View{
init<Provider>(
kind: String,
provider: Provider,
@ViewBuilder content: @escaping (Provider.Entry) -> Content // `@escaping`,
) where Provider : TimelineProvider
}
// `@ViewBuilder`: Here, @ViewBuilder is applied to the content parameter, indicating that the closure can receive multiple view components and Swift will handle the composition of these views into a single view.
// The @escaping annotation indicates that this closure might be stored or called after the init function returns.
Enumerations
enum CompassPoint {
case north
case south
case east
case west
}
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
var directionToHead = CompassPoint.west
// The type of directionToHead is inferred when it’s initialized with one of the possible values of CompassPoint. Once directionToHead is declared as a CompassPoint, you can set it to a different CompassPoint value using a shorter dot syntax:
directionToHead = .east
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// Prints "Watch out for penguins"
//
//
// For some enumerations, it’s useful to have a collection of all of that enumeration’s cases. You enable this by writing : CaseIterable after the enumeration’s name. Swift exposes a collection of all the cases as an allCases property of the enumeration type. Here’s an example:
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
Error handling
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
Protocols
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.
Protocol properties
protocol SomeProtocol {
// protocol definition goes here
}
struct SomeStructure: FirstProtocol, AnotherProtocol { // 实现协议
// structure definition goes here
}
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
// class definition goes here
}
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
protocol AnotherProtocol {
static var someTypeProperty: Int { get set } // 静态变量;实现此协议的类其对应字段也需要是静态的;
}
struct ExampleStruct: AnotherProtocol {
static var someTypeProperty: Int = 0
}
// Accessing and modifying the property
ExampleStruct.someTypeProperty = 10
print(ExampleStruct.someTypeProperty) // Output: 10
protocol RandomNumberGenerator {
func random() -> Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c)
.truncatingRemainder(dividingBy:m))
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.3746499199817101"
print("And another one: \(generator.random())")
// Prints "And another one: 0.729023776863283"
//
//
protocol Togglable {
mutating func toggle()// If you mark a protocol instance method requirement as mutating, you don’t need to write the mutating keyword when writing an implementation of that method for a class. The mutating keyword is only used by structures and enumerations.
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {//
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch is now equal to .on
Subscripts
Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence.
Subscripts syntax
// read-write
subscript(index: Int) -> Int {
get {
// Return an appropriate subscript value here.
}
set(newValue) {
// Perform a suitable setting action here.
}
}
// read-only
subscript(index: Int) -> Int {
// Return an appropriate subscript value here.
}
example:
struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// Prints "six times three is 18"
REFERENCE: A Swift Tour
「点个赞」
点个赞
使用微信扫描二维码完成支付
