Swift 4.1 Systematic Learning 13 Structures (2)

Keywords: Attribute Swift iOS

//
// main.swift
// swift14 (Structures)
//
// Created by iOS on 2018/10/15.
// Copyright 2018 weiman. All rights reserved.
//

import Foundation

// Structures (II)
/*
Continue with the previous section and learn about the structure.
In the previous section, we mainly studied the attributes of the structure, including:
1. Stored instance attributes
2. Lazy storage attributes
3. Computational attributes
4. Attribute observer
5. Type attributes

In this section, we will study the methods in the structure, including:
1. Example method
2. Type method
3. Initializer method
4. Membership-by-member initializer method
5. Initializer proxy for value type
6. Failable initializers
7. Subscript Grammar
*/

// 1. Example method
/*
Definition: When we define a function in an enumeration, class, or struct type, it is called a method.

Each instance method has an implicit attribute self, which points to the object instance that calls this method, so the type of self is the type of the current object.
*/

do {

struct Test {
    // Storage attribute
    var a = 10
    
    let s = "hello"
    
    func printS() {
        // Here, self can be omitted.
        print("a = \(a)")
        print("s = \(s)")
    }
    
    // Because of the modification of instance attributes in this method, mutating keyword is needed.
    mutating func method2(a: Int) {
        // The parameter a here is renamed with the instance attribute a, so it needs to be distinguished by adding self.
        self.a += a + 1
    }
    
    // Re-modify the associated object instance to the default state.
    mutating func method3() {
        self = Test()
    }
}

var test = Test()
test.method2(a: 10)
test.printS()
test.method3()
test.printS()

}

// Reference to Instance Method
/*
Since the instance method must be associated with an object instance, we use a function to refer to the method of pointing an object instance.
Object instances need to be brought along as well.
*/

do {

print("\n")

struct Test {
    var property = 100
    
    mutating func method(a: Int) {
        property += a
    }
    
    func foo(_ : Void = ()) {
        print("property = \(property)")
    }
    
    func foo(a: Int) {
        print("value = \(property + a)")
    }
}

var test = Test()
test.property += 10

// The method method method signature is used to invoke it here.
// Because mutating method does not allow it to be referenced by function reference objects,
// So we can only make direct calls through method signature.
// This is permissible.
test.method(a:)(5)

let ref = { test.method(a: 6) }
ref()

let ref1 = test.foo(_:)
ref1(())

let ref2 = test.foo(a: )
ref2(10)

/*
 Summary:
 A reference to an instance method must contain an object instance associated with it.
 */

}

// 2. Type method
/*
Type methods are similar to type attributes, and are methods associated with types, not object instances.
Defining a type method is also simple, just add static before func.
If the current type is a class type, we can also use the class keyword modifier to indicate that the current type method can be overridden by subclasses.
If the static keyword is used in the class type, then the type method is not allowed to be overridden by the subclass.
*/

do {

print("\n")

struct Test {
    static var a = 100
    
    /// Type method
    static func method() {
        // Type attributes can be modified
        self.a += 20
        print("method: a = \(a)")
    }
    
    static func getValue(a: Int) -> Int {
        return self.a + a
    }
    
    static func foo(_: Void) {
        print("This is a foo")
    }
    
    /// overloading method
    static func foo(a: Int) {
        print("a = \(a)")
    }
}

// Call type method
Test.method()

let a = Test.getValue(a: 5)
print("a = \(a)")

var ref = Test.foo(_:)
ref(())

let ref2 = Test.foo(a: )
ref2(1)

/*
 Print results:
 method: a = 120
 a = 125
 This is a foo.
 a = 1
 */

}

// 3. Initializer method
/*
The initializer method is used to initialize the instance attributes of an object of a class, structure, or enumeration type when it is created.
In swift, the init keyword is used to represent the initializer method of the current type, followed by a list of parameters.
Be careful:
Since an initializer method of a type certainly returns the object instance it created, its return type does not need to be written, that is, the current type itself.
*/

do {

struct Test {
    var a = 10
    let b: Float
    var c: String
    var d: Int?
    
    init() {
        b = 1.0
        self.c = "Hello"
    }
}
// Ellipsis init
var test = Test()
test = Test.init()

let ref = Test.init
test = ref()

}

// 4. Membership-by-member initializer method
/*
For structure types, there is a default form of initialization called the Member-by-Member initializer method.
When we define some storage attributes in the structure, and do not initialize them, nor do we display the methods that provide initializers, then
When we use this structure to create an object instance, we can use the Member-by-Member initialization method to initialize each stored instance attribute in the structure object.
Specify a specific value.
*/

do {

print("\n")

struct Test: CustomStringConvertible {
    var a = 10
    let b: Float
    var c: String
    var d: Int?
    
    var description: String {
        return "a = \(a), b = \(b), c = \(c), d = \(d)"
    }
}
// Member-by-member initializer method
let test = Test.init(a: 10, b: 1.9, c: "Living at the bottom of society, we should also strive to live.", d: 8)
print("test : \(test)")

}

// 5. Initializer proxy for value type
/*
When we call another initializer method in an initializer method to perform partial initialization of an object instance, the process is
It's called the initializer proxy.
*/

do {

print("\n")

struct Test {
    var a = 10
    let b: Float
    var c: String
    var d: Int?
    
    init(b: Float) {
        self.b = b
        c = ""
    }
    
    init(b: Float, c: String) {
        self.init(b: b)
        self.c = c
    }
    
    init(b: Float, c: String, d: Int) {
        self.init(b: b, c: c)
        self.d = d
    }
}

var test = Test.init(b: 1.0)
test = Test.init(b: 2.0, c: "OK")
print("test: \(test)")
test = Test.init(b: 3.4, c: "Ha ha ha ha", d: 8)
print("test: \(test)")

}

// 6. Failable initializers
/*
Sometimes it is necessary to define certain types that may fail to create object instances based on user input or current execution environment.
We can use "failable initializers".
Failable initializers can return null values based on current conditions. Therefore, when we create an object using a failable initializer, the object's
The type is Optional.
*/

do {

print("\n")

struct Test {
    var a: Int
    
    init? (value: Int) {
        if value == 0 {
            return nil
        }
        
        a = 100 / value
    }
}

let test = Test(value: 0)
if test == nil {
    print("failed")
} else {
    print("test: \(test)")
}

}

// 7. Subscript Grammar
/*
In swift language, we are allowed to use subscripts in custom types.
*/

do {

print("\n")

struct Test {
    // Stored instance attribute a
    var a = 10
    
    // Define subscription method
    subscript(index: Int) -> Int {
        
        get {
            return a + index
        }
        
        set(value) {
            a = value + index
        }
    }
    
    subscript(str: String) -> Int {
        return str.count
    }
    
    subscript(value: Int, str: String) -> Int? {
        get {
            guard let count = Int(str) else {
                return nil
            }
            
            return value + count
        }
        
        set {
            if let data = newValue, let strValue = Int(str) {
                a = value + data + strValue
            }
        }
    }
    
    subscript(_: Void) -> Int {
        
        get {
            return a
        }
        
        set {
            a = newValue
        }
    }
}

var test = Test()
/// Here we call the setter method of subscript(index: Int), the small label method of the test object.
test[1] = 10
print("test[5] = \(test[5])")
//Print: test[5] = 16

print("count = \(test["abc"])")

test[2, "123"] = 100

print("test: \(test)")

}

// 8. key path
/*
Sometimes, the type of an attribute in a structure, enumeration, or class type is more complex and the type nesting is deeper. In swift4, we introduced
Smart KeyPaths is a concept that simplifies access to deeply nested attributes.

*/

do {

print("\n")

struct MyRect {
    
    struct Point {
        var x: Float
        var y: Float
    }
    
    struct Size {
        var width: Float
        var height: Float
    }
    
    var position: Point
    
    var size: Size
}

struct MyStruct {
    var property: Int
    var rect: MyRect
}

/// Here we use Smart KeyPath literal quantities
/// Define a width Key Path key path.
/// It's for MyStruct.rect.size.width
/// Access path for this instance attribute
let widthKeyPath = \MyStruct.rect.size.width

var obj = MyStruct(property: 10, rect: MyRect(position: MyRect.Point(x: 0.0, y: 1.0), size: MyRect.Size(width: 10.0, height: 20.0)))
let width = obj[keyPath: widthKeyPath]
print("width: \(width)")

obj[keyPath: \MyStruct.rect.position.x] += obj.rect.position.y * 8.0
print("x = \(obj.rect.position.x)")

}

Posted by doctor_james on Fri, 01 Feb 2019 16:21:15 -0800