Swift常用基础知识(一)

汇编相关

lldb常用指令

//读取寄存器的值
register read/格式
register read/

//修改寄存器的值
register write 寄存器名称 数值
register write rax 0

//读取内存中的值
x/数量-格式-字节大小 内存地址
x/3xw 0x0000010

//修改内存中的值
memory write 内存地址 数值
memory write 0x0000010 10

格式
x是16进制,f是浮点,d是十进制

字节大小
b-byte 1字节
h-half word 2字节
w-word 4字节
g-giant word 8字节

expreion 表达式
可以简写:expr 表达式
expreion $rax
expreion $rax = 1

po 表达式
print 表达式
po/x $rax
thread step-over、next、n
单步运行,把子函数当做整体一步执行(源码级别)

thread step-in、step、s
单步运行,遇到子函数会进入子函数(源码级别)

thread step-inst-over、nexti、ni
单步运行,把子函数当做整体一步执行(汇编级别)

thread step-inst、stepi、si
单步运行,遇到子函数会进入子函数(汇编级别)

thread step-out、finish
直接执行完当前函数的所有代码,返回到上一个函数(遇到断点会卡住)

规律

  • 内存地址格式为:0x4bdc(%rip), 一般是全局变量
  • 内存地址格式为:-0x78(%rbp), 一般是局部变量
  • 内存地址格式为:-0x10(%rax), 一般是堆空间

寄存器

有16个常用寄存器

  • rax、rbx、rcx、rdx、rsi、rdi、rbp、rsp
  • r8、r9、r10、r11、r12、r13、r14、r15

寄存器的具体用途

  • rax常作为函数返回值使用
  • rsi、rdi、rdx、rcx、r8、r9等寄存器常用于存放函数参数
  • rsp、rbp用于栈操作
  • rip作为指令指针
    • 存储着CPU下一条要执行的指令地址
    • 一旦CPU读取一条指令,rip会自动指向下一条指令地址(存储下一条指令地址)

Swift语法

  • 被class修饰的计算类型属性,可以被子类重写
  • 被static修饰的类型属性(存储、计算),不可以被子类重写
class Circle {
    static var radius: Int = 0
    class var diameter: Int {
        set {
            print("Circle setDiameter")
            radius = newValue / 2
        }
        get {
            print("Circle getDiameter")
            return radius * 2
        }
    }
}

class SubCircle: Circle {
    override static var diameter: Int {
        set {
            print("Circle setDiameter")
            super.diameter = newValue>0 ? newValue : 0
        }
        get {
            print("Circle getDiameter")
            return super.diameter
        }
    }
}


Circle.radius = 6;
    // Circle getDiameter
    // 12
    print(Circle.diameter)
    //Circle setDiameter
    Circle.diameter = 20
    // 10
    print(Circle.radius)
    
    SubCircle.radius = 6
    //SubCircle getDiameter
    // Circle getDiameter
    //12
    print(SubCircle.diameter)
    //SubCircle setDiameter
    //Circle setDiameter
    SubCircle.diameter = 20
    //10
    print(SubCircle.radius)
    
内存.png
//字符串长度<= 0xF,字符串内容直接存放在str1变量的存中
var str1 = "0123456789"
//字符串长度> 0xF, 字符串内容存放在_TEXT.cstring中 (常量区) 
//字符串的地址值信息存放在str2变量的后8个字节中
var str2 = "0123456789ABCDEF"
//由于字符串长度<= 0xF,所以字符串内容依然存放在str1变量的内存中
str1.append("ABCDE")
//开辟堆空间
str1.append("F")
//开辟堆空间
str2.append("G")

运算符重载

struct Point {
    var x: Double = 0
    var y: Double = 0
    
    static func + (p1: Point, p2: Point) -> Point {
        Point(x: p1.x + p2.x, y: p1.y + p2.y)
    }
    static func - (p1: Point, p2: Point) -> Point {
        Point(x: p1.x - p2.x, y: p1.y - p2.y)
    }
    static prefix func - (p: Point) -> Point {
        Point(x: -p.x, y: -p.y)
    }
    static func += (p1: inout Point, p2: Point) {
        p1 = p1 + p2
    }
    static prefix func ++(p: inout Point) -> Point {
        p += Point(x: 1, y: 1)
        return p
    }
    static postfix func ++ (p: inout Point) -> Point {
        let tmp = p
        p += Point(x: 1, y: 1)
        return tmp
    }
    static func == (p1: Point, p2: Point) -> Bool {
        (p1.x == p2.x) && (p1.y == p2.y)
    }
}

计算属性、下标方法、嵌套类型

extension Double {
    var km: Double { self * 1_000.0 }
    var m: Double { self }
    var dm: Double { self / 10.0 }
    var cm: Double { self / 100.0 }
    var mm: Double { self / 1_000.0 }
}

extension Array {
    subscript(nullable idx: Int) -> Element? {
        if (startIndex.. Void) -> Void {
        for _ in 0.. Int {
        self = self * self
        return self
    }
    enum Kind { case negative, zero, postive }
    var kind: Kind {
        switch self {
        case 0: return .zero
        case let x where x > 0: return .postive
        default: return .negative
        }
    }
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..< digitIndex { decimalBase *= 10 }
        return (self / decimalBase) % 10
    }
    
}

访问控制( Access Control )

■ 在访问权限控制这块, Swift提供了5个不同的访问级别(以下是从高到低排列,实体指被访问级别修饰的内容)

  • open :允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写( open只能用在类、类成员上)
  • public :允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写
  • internal :只允许在定义实体的模块中访问,不允许在其他模块中访问
  • fileprivate :只允许在定义实体的源文件中访问
  • private :只允许在定义实体的封闭声明中访问

■ 绝大部分实体默认都是internal级别

访问级别的使用准则

■ 一个实体不可以被更低访问级别的实体定义,比如

  • 变量\常量类型 ≥ 变量\常量
  • 参数类型、返回值类型 ≥ 函数
  • 父类 ≥ 子类
  • 父协议 ≥子协议
  • 原类型 ≥ typealias
  • 原始值类型、关联值类型 ≥ 枚举类型
  • 定义类型A时用到的其他类型 ≥ 类型A

成员、嵌套类型

■ 类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的默认访问级别

  • 一般情况下,类型为private或fileprivate ,那么成员\嵌套类型默认也是private或fileprivate
  • 一般情况下,类型为internal或public ,那么成员\嵌套类型默认是internal
public class PublicClass {
    public var p1 = 0
    var p2 = 0 // internal
    fileprivate func f1() {} // fileprivate
    private func f2() {} // private
}
class InternalClass { // internal
    var p = 0 // internal
    fileprivate func f1() {} // fileprivate
    private func f2() {} // private
}

fileprivate class FilePrivateClass { // fileprivate
    func f1() {} // fileprivate
    private func f2() {} // private

}
private class PrivateClass { // private
    func f() {} // private
}

■ 子类重写的成员访问级别必须 ≥ 父类的成员访问级别

getter,setter

  • getter, setter默认自动接收他们所属环境的访问级别

■ 可以给setter单独设置一个比getter更低的访问级别 ,用以限制写的权限

fileprivate(set) public var num = 10
class Person {
    private(set) var age = 0
    fileprivate(set) public var weight: Int {
        set {}
        get { 10 }
    }
    internal(set) public subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}

初始化器

■ 如果一个public类想在另一个模块调用编译生成的默认无参初始化器,必须显式提供public的无参初始化器

  • 因为public类的默认初始化器是interna1级别

required初始化器必须跟它所属类拥有相同的访问级别
■如果结构体有private\ fileprivate的存储实例属性,那么它的成员初始化器也是private\ fileprivate

  • 否则默认就是internal

协议

■ 协议中定义的要求自动接收协议的访问级别,不能单独设置访问级别

  • public协议定义的要求也是public

■ 协议实现的访问级别必须 ≥ 类型的访问级别,或者 ≥ 协议的访问级别
■ 下面代码能编译通过么?

public protocol Runnable { 
    func run()
}
public class Person : Runnable {
    func run() {} 
}
//Method 'run()' must be declared public because it matches a requirement in public protocol 'Runnable'

扩展

■ 在同一文件中的扩展,可以写成类似多个部分的类型声明
口 在原本的声明中声明一个私有成员,可以在同一文件的扩展中访问它
口 在扩展中声明一个私有成员,可以在同一文件的其他扩展中、原本声明中访问它

public class Person {
    private func run0() {}
    private func eat0() {
        run0()
    }
}
extension Person {
    private func run1() {}
    private func eat1() {
        run1()
    }
}
extension Person {
    private func eat2() {
        run1()
    }
}

闭包的循环引用

■ 如果想在定义闭包属性的同时引用self,这个闭包必须是lazy的(因为在实例初始化完毕之后才能引用self)

■ 闭包fn内部如果用到了实例成员(属性、方法)
编译器会强制要求明确写出self

class Person {
    lazy var fn: (()->()) = {
        [weak self] in
        self?.run()
    }
    func run() { print("run") }
    deinit { print ("deinit") }
}

■ 如果lazy属性是闭包调用的结果,那么不用考虑循环引用的问题(因为闭包调用后,闭包的生命周期就结束了)

class Person {
    var age: Int = 0
    lazy var getAge: Int = {
        self.age
    }()
    deinit { print("deinit") }
}

@escaping

■ 非逃逸闭包、逃逸闭包, 一般都是当做参数传递给函数
■ 非逃逸闭包: 闭包调用发生在函数结束前,闭包调用在函数作用域内
■ 逃逸闭包: 闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域,需要通过@escaping声明

import Dispatch
typealias Fn = () -> ()
// fn是非逃逸闭包
func test1(_ fn: Fn) { fn() }
// fn是逃逸闭包
var gFn: Fn?
func test2(_ fn: @escaping Fn) { gFn = fn }

■左边的闭包fn内部如果用到了实例成员(属性、方法)

内存访问冲突 ( Conflicting Access to Memory )

■ 内存访问冲突会在两个访问满足下列条件时发生:

  • 至少一个是写入操作
  • 它们访问的是同一块内存
  • 它们的访问时间重叠(比如在同一个函数内)
 //不存在内存访问冲突
    func plus(_ num: inout Int) -> Int { num + 1 }
    var number = 1
    number = plus (&number)
    //存在内存访问冲突
    // Simultaneous accees to Øx0, but modificat ion requires exclusive acce
    var step = 1
    func increment(_ num:inout Int) { num += step }
    increment (&step)

    //解决内存访问冲突
    var copyOfStep = step
    increment(©OfStep)
    step = copyOfStep
func balance(_ x: inout Int,_ y: inout Int) {
        let sum=x+y
        x=sum/2
        y=sum-x
    }
    var num1 = 42
    var num2 = 30

    balance(&num1, &num2) // 0K
    balance(&num1, &num1) // Error
    
    
    struct Player {
        var name: String
        var health: Int
        var energy: Int
        mutating func shareHealth(with teammate: inout Player) {
            balance(&teammate.health, &health)
        }
    }
    var oscar = Player(name: "Oscar", health: 10, energy: 10)
    var maria = Player(name: "Maria", health: 5, energy: 10)
    
    oscar.shareHealth(with: &maria) // 0K
    oscar.shareHealth(with: &oscar) // Error
    
    
    var tulpe = (health: 10, energy: 20)
    // Error
    balance(&tulpe.health, &tulpe.energy)
    var holly = Player(name: "Holly", health: 10, energy: 10)
    // Error
    balance(&holly.health, &holly.energy)

■ 如果下面的条件可以满足,就说明重叠访问结构体的属性是安全的

  • 你只访问实例存储属性,不是计算属性或者类属性
  • 结构体是局部变量而非全局变量
  • 结构体要么没有被闭包捕获要么只被非逃逸闭包捕获
// OK
    func test() {
        var tulpe = (health: 10, energy: 20)
        balance (&tulpe.health, &tulpe.energy)
        var holly = Player(name: "Holly", health: 10, energy: 10)
        balance (&holly.health, &holly.energy)
    }
    test()

指针

■ Swift中也有专门的指针类型,这些都被定性为"Unsafe" (不安全的) , 常见的有以下4种类型

  • UnsafePointer 类似于 const Pointee *
  • UnsafeMutablePointer 类似于 Pointee *
  • UnsafeRawPointer 类似于 const void *
  • UnsafeMutableRawPointer 类似于 void *
    var age = 10
    func test1(_ ptr: UnsafeMutablePointer) {
        ptr.pointee += 10
    }
    func test2(_ ptr: UnsafePointer) {
        print(ptr.pointee)
    }
    
    test1(&age)
    test2(&age) // 20
    print(age) // 20
    var age = 10
    func test3(_ ptr: UnsafeMutableRawPointer) {
        ptr.storeBytes(of: 20, as: Int.self)
    }
    func test4(_ ptr: UnsafeRawPointer) {
        print(ptr.load(as: Int.self))
    }
    test3(&age)
    test4(&age) // 20
    print(age) // 20
    var age = 10
    var ptr = withUnsafePointer(to: &age) { (p) -> Int in
        return 20
    }

获得某个变量的指针

    var age = 11
    var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
    var ptr2 = withUnsafePointer(to: &age) { $0 }
    ptr1.pointee = 22
    print(ptr2.pointee) // 22
    print(age) // 22
    var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
    var ptr4 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
    ptr3.storeBytes(of: 33, as: Int.self)
    print(ptr4.load(as: Int.self)) // 33
    print(age) // 33

创建指针

    var ptr1 = UnsafeRawPointer( bitPattern: 0x100001234)
    // 创建
    var ptr = malloc(16)
    // 存
    ptr?.storeBytes(of: 11, as: Int.self)
    ptr?.storeBytes(of: 22, toByteOffset: 8, as: Int.self)
    // 取
    print((ptr?.load(as: Int.self))!) // 11
    print((ptr?.load(fromByteOffset: 8, as: Int.self))!) // 22
    free(ptr)
    var ptr = UnsafeMutableRawPointer . allocate (byteCount: 16, alignment: 1)
    ptr.storeBytes(of: 11, as: Int.self)
    ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)
    print(ptr.load(as: Int.self)) // 11
    print(ptr.advanced(by: 8).load(as: Int.self)) // 22
    ptr.deallocate()

指针间的转换

    var ptr = UnsafeMutableRawPointer . allocate(byteCount: 16, alignment: 1)
    ptr.aumingMemoryBound(to: Int.self).pointee = 11
    (ptr + 8).aumingMemoryBound(to: Double.self). pointee = 22.0
    print(unsafeBitCast(ptr, to: UnsafePointer.self).pointee) // 11
    print(unsafeBitCast(ptr + 8, to: UnsafePointer.self).pointee) // 22.0
    ptr.deallocate()
  • unsafeBitCast是忽略数据类型的强制转换,不会因为数据类型的变化而改变原来的内存数据
  • 类似于C++ 中的reinterpret_cast
    class Person {}
    var person = Person()
    // personObjectAddre存储的就是堆空间地址
    var personObjectAddre = unsafeBitCast(person, to: UInt.self)
    print(UnsafeRawPointer(bitPattern: personObjectAddre))
    var p = unsafeBitCast(person, to: UnsafeRawPointer.self)
    print(p)

字面量(Literal)

var age = 10
var isRed = false
var name = "Jack"

上面代码中的10、false, "Jack"就是字面量
■ 常见字面量的默认类型

public typea lias IntegerLiteralType = Int
public typealias FloatLiteralType = Double
public typealias BooleanL iteralType = Bool
public typealias StringLiteralType = String
//可以通过typealias修改字 面量的默认类型
typealias FloatLiteralType = Float
typealias IntegerLiteralType = UInt8
var age = 10 // UInt8
var height = 1.68 // Float

■ Swift自带的绝大部分类型,都支持直接通过字面量进行初始化
Bool、Int、Float、Double、String、Array、 Dictionary、 Set、Optional

字面量协议应用

extension Int : ExpressibleByBooleanLiteral {
    public init( booleanLiteral value: Bool) { self = value ? 1 : 0 }
}
    var num: Int = true
    print(num) // 1
    //■有点类似于C+ +中的转换构造函数
    class Student : ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral,
                    CustomStringConvertible {
    var name: String = ""
    var score: Double = 0
        required init(floatLiteral value: Double) { self.score = value }
        required init( integerLiteral value: Int) { self.score = Double(value) }
        required init(stringLiteral value: String) { self.name = value }
        required init ( unicodeScalarLiteral value: String) { self.name = value }
        required init (extendedGraphemeClusterLiteral value: String) { self.name = value }
        var description: String { "name=\(name) , score=\(score)"}
    }
    var stu: Student = 90
    print(stu) // name=, score=90.0
    stu = 98.5
    print(stu) // name=, score=98.5
    stu = "Jack"
    print(stu) // name=Jack, score=0.0
    
struct Point {
    var x = 0.0, y = 0.0
}
extension Point : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
    init(arrayLiteral elements: Double...) {
        guard elements.count > 0 else { return }
        self.x = elements [0]
        guard elements.count > 1 else { return }
        self.y = elements [1]
    }
    init(dictionaryLiteral elements: (String, Double)...) {
        for (k, v) in elements {
            if k == "x" { self.x = v }
            else if k == "y" { self.y = v}
        }
    }
}
    var p: Point = [10.5, 20.5]
    print(p) // Point(x: 10.5, y: 20.5)
    p = ["x":11,"y":22]
    print(p) // Point(x: 11.0, y: 22.0)

通配符模式 ( Wildcard Pattern )

■ _ 匹配任何值
■ _?匹配非nil值

    enum Life {
        case human(name: String, age:Int?)
        case animal(name: String, age:Int?)
    }
    func check(_ life: Life) {
        switch life {
        case . human(let name, _) :
            print ("human", name)
        case . animal(let name,_?):
            print("animal", name)
        default:
            print("other")
        }
    }
    check( . human( name: "Rose", age: 20)) // human Rose
    check( . human(name: "Jack", age: nil)) // human Jack
    check( . animal(name: "Dog", age: 5)) // animal Dog
    check( . animal( name: "Cat", age: nil)) // other

枚举Caset模式 ( Enumeration Case Pattern )

  • if case语句等价于只有1个case的switch语句
    let age=2
    //原来的写法
    if age >= 0 && age <= 9 {
        print("[0, 9]")
    }
    //枚举Case模式
    if case 0...9 = age {
        print("[0, 9]")
    }
    guard case 0...9 = age else { return }
    print("[0, 9]")
    
    switch age {
    case 0...9: print("[0, 9]")
    default: break
    }
    
    
    let ages: [Int?] = [2, 3, nil, 5]
    
    for case nil in ages {
        print("有nil")
        break
    } //有nil值
    
    let points = [(1, 0), (2, 1), (3, 0)]
    for case let(x, 0) in points {
        print(x)
    } // 1 3

可选模式 ( Optional Pattern )

    let age: Int? = 42
    if case .some(let x) = age { print(x) }
    if case let x? = age { print(x) }

    let ages: [Int?] = [nil, 2, 3, nil, 5]
    for case let age? in ages {
        print (age)
    }// 2 3 5

    for item in ages {
        if let age = item {
            print(age)
        } //跟上面的for,效果是等价的
    }
    
    func check(_ num: Int?) {
        switch num {
        case 2?: print("2")
        case 4?: print("4")
        case 6?: print("6")
        case _?: print("other")
        case _: print("nil")
        }
    }
    check(4) // 4
    check(8) // other
    check(nil) // nil

类型转换模式 ( Type-Casting Pattern )

    let num: Any = 6
    switch num {
    case is Int:
        //编译器依然认为num是Any类型
        print("is Int", num)
//    case let n as Int:
//        print("as Int", n + 1)
    default:
        break
    }
    
    class Animal { func eat() { print(type(of: self), "eat") } }
    
    class Dog: Animal { func run() { print(type(of: self), "run") } }
    
    class Cat: Animal { func jump() { print(type(of: self), "jump") } }
    
    func check(_ animal: Animal) {
        
        switch animal {
        case let dog as Dog:
            dog.eat()
            dog.run()
        case is Cat:
            animal.eat()
        default: break
        }
    }
    
    // Dog eat
    // Dog run
    check(Dog())
    // Cat eat
    check(Cat())

表达式模式 ( Expression Pattern )

  • 表达式模式在case中
let point = (1, 2)
    switch point {
    case (0, 0):
        print("(0, 0) is at the origin.")
    case (-2...2, -2...2):
        print("(\(point.0), \(point.1)) is near the origin.")
    default:
        print("The point is at (\(point.0), \(point.1)).")
        
    }// (1, 2) is near the origin.

自定义表达式模式

■ 可以通过重载运算符,自定义表达式模式的匹配规则

    struct Student {
        var score = 0, name = ""
        static func ~=(pattern: Int, value: Student) -> Bool { value.score >= pattern }
        static func ~=(pattern: ClosedRange, value: Student) -> Bool { pattern.contains(value.score) }
        static func ~=(pattern: Range, value: Student) -> Bool { pattern.contains (value.score) }
    }
    
    var stu = Student(score: 75, name: "Jack" )
    switch stu {
    case 100: print(">= 100")
    case 90: print(">= 90")
    case 80..<90: print("[80, 90)")
    case 60..79: print("[60, 79]")
    case 0: print(">= 0")
    default: break
    }// [60, 79]
    
    if case 60 = stu {
        print(">= 60")
    } //>= 60
    var info = (Student(score: 70, name: "Jack"), "及格")
    switch info {
    case let (60, text): print(text)
    default: break
    }//及格
extension String {
    static func ~= (pattern: (String) -> Bool, value: String) -> Bool {
        pattern(value)
    }
}
func hasPrefix(_ s: String) -> ((String) -> Bool) { { $0.hasPrefix(s) } }
func hasSuffix(_ s: String) -> ((String) -> Bool) { { $0.hasSuffix(s) } }


                                                      

    var str = "jack"
    switch str {
    case hasPrefix("j"), hasSuffix("k"):
        print("以j开头, 或以k结尾")
    default: break
    } // 以j开头, 或以k结尾
func isEven(_ i: Int) -> Bool { i%2 == 0 }
func is0dd(_ i: Int) -> Bool { i%2 != 0 }
extension Int {
    static func ~= (pattern: (Int) -> Bool, value: Int) -> Bool {
        pattern( value )
    }
}
    var age = 10
    switch age {
    case isEven:
    print(age, "是个偶数" )
    case isOdd:
    print(age, "是个奇数")
    default: break
    }
prefix operator ~>
prefix operator ~>=
prefix operator ~<
prefix operator ~<=
prefix func ~> (_ i: Int) -> ((Int) -> Bool) { {$0 > i} }
prefix func ~>= (_ i: Int) -> ((Int) -> Bool) { {$0 >= i} }
prefix func ~< (_ i: Int) -> ((Int) -> Bool) { {$0 < i} }
prefix func ~<= (_ i: Int) -> ((Int) -> Bool) { {$0 <= i} }
    var age = 9
    switch age {
    case ~>=0, ~<=10:
    print("1")
    case ~>10, ~<20:
    print("2")
    default: break
    } // [0, 10]

Where

// 可以使用where为模式匹配增加匹配条件
    var data = (10, "Jack")
    switch data {
    case let (age, _) where age > 10:
        print(data.1, "age>10" )
    case let (age, _) where age > 0:
        print(data.1, "age>0")
    default: break
    }
    
    var ages = [10, 20, 44, 23, 55]
    for age in ages where age > 30 {
        print (age)
    }//44 55
    
    
protocol Stackable { associatedtype Element }
protocol Container {
    associatedtype Stack : Stackable where Stack.Element: Equatable
}

    func equal(_ s1: S1,_ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element: Hashable {
        return false
    }
    
extension Container where Self.Stack.Element: Hashable {}

条件编译

// 操作系统: macOS\i0S\tvOS\watchOS\Linux\Android\Windows\FreeBSD
#if os(macOS) || os(iOS)
// CPU#LMJ: i386x&6_ _64(a rma rm64
#elseif arch(x86_64) || arch(arm64 )
// swift版本
#elseif swift(<5) && swift(>=3)
// 模拟器
#elseif targetEnvironment(simulator)
// 可以导入某模块
#elseif canImport(Foundation)
#else
#endif

// debug模式
#if DEBUG
// release模式
#else
    
#endif

#if TEST
    print("test")
#endif

#if OTHER
    print("other")
#endif
16602884931580.jpg

打印

func log(_ msg: T ,
            file: NSString = #file,
            line: Int = #line,
            fn: String = #function) {
#if DEBUG
    let prefix = "\(file. lastPathComponent)_\(line)_\(fn):"
    print(prefix, msg)
#endif
}

系统版本检测

if #available(iOS 10, macOS 10.12, *) {
    //对于i0S平台,只在i0S10及以,上版本执行
    //对于mac0S平台,只在mac0S 10. 12及以上版本执行
    //最后的*表示在其他所有平台都执行
    }

API可用性说明

@available(iOS 10, macOS 10.15, *)
class PersonX {}
struct StudentX {
    @available(*, unavailable, renamed: "study" )
    func study_() {}
    func study() {}
    
    @available( iOS, deprecated: 11)
    @available (macOS, deprecated: 10.12)
    func run() {}
    
}

■ 更多用法参考

iOS程序的入口

//在AppDelegate上面默认有个 @UIApplicationMain标记 , 这表示
//编译器自动生成入口代码(main函数代码),自动设置AppDelegate为APP的代理
//也可以删掉 @UIApplicationMain , 自定义入口代码 : 新建一个main.swift文件

import UIKit
class HYApplication : UIApplication {}
UIApplicationMain(CommandLine.argc,
                  CommandLine.unsafeArgv,
                  NSStringFromClass(HYApplication.self),
                  NSStringFromClass(AppDelegate.self))

Swift调用OC

新建1个桥接头文件,文件名格式默认为:{targetName}-Bridging-Header.h


16602009689445.jpg

在{targetName}-Bridging-Header.h文件中#import "AEPerson.h" OC要暴露给Swift的内容
注:Swift 项目创建OC文件时会提示你自动创建桥接头文件

// OC中
int sum(int a, int b);
@interface AEPerson : NSObject


@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *name;

- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name;

- (void)run;
+ (void)run;

- (void)eat:(NSString *)food other:(NSString *)other;
+ (void)eat:(NSString *)food other:(NSString *)other;
@end

@implementation AEPerson
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name {
    if (self = [super init]) {
        self.age = age;
        self.name = name;
    }
    return self;
}
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name {
    return [[self alloc] initWithAge:age name:name];
}

+ (void)run { NSLog(@"Person +run"); }
- (void)run { NSLog(@"Person -run"); }

+ (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Person +eat %@ %@", food, other); }
- (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Person %zd %@ -eat %@ %@", (long)_age, _name, food, other); }

@end
int sum(int a, int b) { return a + b; }

// swift调OC

    var p = AEPerson(age: 10, name: "Jack" )
    p.age = 18
    p.name = "Rose"
    p.run() // 18 Rose -run
    p.eat("Apple", other: "Water") // 18 Rose -eat Apple Water
    AEPerson.run() // Person +run
    AEPerson.eat("Pizza", other: "Banana") // Person +eat Pizza Banana
    print(sum(10, 20)) // 30

Swift调用OC - @_silgen_name

  • 如果C語言暴露給Swift的凾数名跟Swift中的其他凾数名冲突了
  • 可以在Swift中使用 @_silgen_name 修改C函数名
// C语言
int sum(int a, int b) {}
    return a + b;
}


// Swift
@_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32
func oc1() {
    print(swift_sum(10, 20)) // 30
    print(sum(10, 20)) // 30
}

OC调用Swift

  • Xcode已经默认生成一 个用于OC调用Swift的头文件,文件名格式是: {targetName}-Swift.h
16602021727539.jpg

■ Swift暴露给OC的类最终继承自NSObject
■ 使用@objc修饰需要暴露给OC的成员
■ 使用@objcMembers修饰类
口 代表默认所有成员都会暴露给OC(包括扩展中定义的成员)
口 最终是否成功暴露,还需要考虑成员自身的访问级别

@objcMembers class Car: NSObject {
    var price: Double
    var band: String
    init(price: Double, band: String) {
        self.price = price
        self.band = band
    }
    func run() { print(price, band, "run") }
    static func run() { print("Car run") }
}
extension Car {
    func test() { print(price, band, "test") }
}

■可以通过@objc重命名Swift暴露给OC的符号名 (类名、属性名、函数名等)

@objc(AECar)
@objcMembers class Car: NSObject {
    var price: Double
    @objc(name)
    var band: String
    init(price: Double, band: String) {
        self.price = price
        self.band = band
    }
    @objc(drive)
    func run() { print(price, band, "run") }
    static func run() { print("Car run") }
}
extension Car {
    @objc(exec:v2:)
    func test(name: String, price: Int) { print(price, band, "test") }
}

注:在OC中引入头文件#import "{targetName}-Swift.h"

    AECar *car = [[AECar alloc] initWithPrice:10.5 band:@"Bently"];
    car.name = @"BMW";
    car.price = 109;
    [car drive];
    
    [AECar run];

选择器 ( Selector )

  • Swift中依然可以使用选择器,使用#selector(name)定义一个选择器
  • 必須是被@objcMembers或@objc修怖的方法オ可以定义选择器
@objcMembers class Person2: NSObject {
    func test1(v1: Int) { print("test1") }
    func test2(v1: Int, v2: Int) { print("test2(v1:v2:)") }
    func test2(_ v1: Double,_ v2: Double) { print("test2(_ :_ _:)") }
    
    func run() {
        perform( #selector(test1))
        perform(#selector(test1(v1:)))
        perform(#selector(test2(v1:v2:)))
        perform(#selector(test2(_:_:)))
        perform(#selector(test2 as (Double, Double) -> Void))
    }
}

String

■Swift的字符串类型String ,跟OC的NSString ,在API设计上还是有较大差异

    //空字符串
    var emptyStr1 = ""
    var emptyStr2 = String()
    
    var str1 = "123456"
    print(str1.hasPrefix("123")) // true
    print(str1.hasSuffix("456")) // true
    
    var str: String = "1"
    // 拼接, jack_rose
    str.append("_2")
    //重载运算符+
    str = str + "_3"
    //重载运算符+=
    str += "_4"
    // \()插值
    str = "\(str)_5"
    //长度,9,1_2_3_4_5
    print(str.count)

String的插入和删除

    var str = "1_2"
    // 1_2_
    str.insert("_", at: str.endIndex)
    //1_2_3_4
    str.insert(contentsOf: "3_4", at: str.endIndex)
    // 1666_2_3_4
    str.insert(contentsOf: "666", at: str.index(after: str.startIndex))
    // 1666_2_3_8884
    str.insert(contentsOf: "888", at: str.index(before: str.endIndex) )
    // 1666hello_ 2_ _3_ 8884
    str.insert( contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))

    var str = "1_2"
    //1_2_
    str.insert("_", at: str.endIndex)
    //1_2_3_4
    str.insert(contentsOf: "3_4", at: str.endIndex)
    // 1666_2_3_ 4
    str.insert(contentsOf: "666", at: str.index(after: str.startIndex) )
    // 1666_2_3_8884
    str.insert(contentsOf: "888", at: str.index(before: str.endIndex))
    // 1666hello_2_3_8884
    str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))
    
    // 666hello_2_3_8884
    str.remove(at: str.firstIndex(of: "1")!)
    // hello_2_3_8884
    str.removeAll { $0 == "6" }
    var range = str.index(str.endIndex, offsetBy: -4)..

Substring

Substring.jpg

// String可以通过下标, prefix、suffix等截取子串,子串类型不是String ,而是Substring

    var str="1_2_3_4_5"
    // 1_2
    var substr1 = str.prefix(3)
    //4_5
    var substr2 = str.suffix(3)
    // 1_2
    var range = str.startIndex.. String
    var str2 = String(substr3)
    // ■Substring和它的base ,共享字符串数据
    // Substring转为String时, 会重新分配新的内存存储字符串数据

String与Character

for c in "jack" {// c是Character类型
        print(c)
    }
    var str = "jack"
    // c是Character类型
    var c = str[str.startIndex]

多行String

let str = """
1
    "2"
3
    '4'
"""
//缩进以结尾的3引号为对齐线
let str9 = """
        1
            "2"
        3
            '4'
        """

#warning("要显示3引号有问题啊")
// 如果要显示3引号, 至少转义1个引号
let str0 = """
Escaping the first quote \"""
Escaping two quotes \"\""
Escaping all three quotes \"\"\"
"""

//以下2个字符串是等价的
let str1 = "These are the same."
let str2 = """
These are the same.
"""

String 与 NSString

  • String 与 NSString之间可以随时随地桥接转换
    -如果你觉得String的API过于复杂难用,可以考虑将String转为NSString
var str10: String = "jack"
var str20: NSString = "rose"
var str3 = str1 as NSString
var str4 = str2 as String
// ja
var str5 = str3.substring (with: NSRange(location: 0, length: 2))
print(str5)
  • 比较字符串内容是否等价
  • String使用 == 运算符
  • NSString使用isEqual方法,也可以使用==返算符(本貭还是调用了isEqual方法)

Swift、 OC桥接转换表

Swift OC
String ↔️ NSString
String ⬅️ NSMutableString
Array ↔️ NSArray
Array ⬅️ NSMutableArray
Dictionary ↔️ NSDictionary
Dictionary ⬅️ NSMutableDictionary
Set ↔️ NSSet
Set ⬅️ NSMutableSet

只能被class继承的协议

protocol Runnable1: AnyObject {}
protocol Runnable2: class {}
@objc protocol Runnable3 {}
  • 被@objc修饰的协议,还可以暴露给OC取遵守实现

可选协议

■可以通过 @objc定义可选协议,这种协议只能被class遵守

@objc protocol Runnable {
    func run1()
    @objc optional func run2()
    func run3()
}
class Dog: Runnable {
    func run3() { print ("Dog run3") }
    func run1() { print("Dog run1") }
}
var d = Dog()
d.run1() // Dog run1
d.run3() // Dog run3

dynamic

■被 @objc dynamic 修怖的内容会具有劫恣性,比如凋用方法会走runtime那一套流程

class Dog: NSObject {
    @objc dynamic func test1() {}
    func test2() {}
}
var d = Dog()
d.test1()
d.test2()

KVC\KVO

■Swift支持KVC\KVO的条件

  • 属性所在的类、监听器最终继承自NSObject
  • 用 @objc dynamic 修饰对应的属性
class Observer: NSObject {
    
    override func observeValue(forKeyPath keyPath: String?,
                               of object: Any?,
                               change: [NSKeyValueChangeKey : Any]?,
                               context: UnsafeMutableRawPointer?) {
        print ("observeValue", change?[.newKey] as Any)
    }
}
class Person: NSObject {
    @objc dynamic var age: Int = 0
    var observer: Observer = Observer()
    override init() {
        super.init( )
        self.addObserver(observer,
                          forKeyPath: "age",
                          options: .new,
                          context: nil)
    }
    deinit {
        self.removeObserver(observer,
                            forKeyPath: "age")
    }
}
var p = Person( )
// observeValue Optional(20)
p.age = 20
// observeValue Optional(25)
p.setValue(25, forKey: "age")

block方式的KVO

class Person: NSObject {
    @objc dynamic var age: Int = 0
    var observation: NSKeyValueObservation?
    override init() {
        super.init( )
        observation = observe(\Person.age, options: .new, changeHandler: { (person, change) in
            print(change.newValue as Any)
        })
    }
}
var p = Person( )
// Optional(20)
p.age = 20
//  Optional(25)
p.setValue(25, forKey: "age")

关联对象( Associated Object )

//在Swift中, class依然可以使用关联对象

  • 默认情况, extension不可以增加存储属性
  • 借助关联对象,可以实现类似extension为class增加存储属性的效果
class Person {}
extension Person {
    private static var AGE_KEY: Void?
    var age: Int {
        get {
            (objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0
        }
        set {
            objc_setAssociatedObject(self, &Self.AGE_KEY, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
}

资源名管理

enum R {
    enum string: String {
        case add = "添加"
    }
    enum image: String {
        case logo
    }
    enum segue: String {
        case login_main
    }
}
func testR() {
    let img = UIImage(named: "logo")
    let btn = UIButton(type: .custom)
    btn.setTitle("添加", for: .normal)
    performSegue(withIdentifier: "login_main", sender: self)
    
    let img = UIImage (R.image.logo)
    let btn = UIButton(type: .custom)
    btn.setTitle(R.string.add, for: .normal)
    performSegue ( withIdentifier: R.segue. login_main, sender: self)
}

//or
extension UIImage {
    convenience init?(_ name :R.image) {
        self.init(named: name.rawValue)
    }
}
extension UIViewController {
    func performSegue(withIdentifier identifier: R.segue, sender: Any?) {
        performSegue(withIdentifier: identifier .rawValue, sender: sender)
    }
}
    
extension UIButton {
    func setTitle(_ title: R.string, for state: UIControl.State) {
        setTitle(title.rawValue, for: state)
    }
}

■这种做法实际上是参考了Android的资源名管理方式

资源名管理的其他思路

let img = UIImage(named: "logo")
let font = UIFont(name: "Arial", size: 14)
let img = R.image.logo
let font = R.font.arial(14)
enum R {
    enum image {
        static var logo = UIImage (named: "logo")
    }
    enum font {
        
        static func arial(_ size: CGFloat) -> UIFont? {
            UIFont(name: "Arial", size: size)
        }
    }
}

更多优秀思路 参考

多线程开发

异步

public typealias Task = () -> Void
struct Queue {
    public static func async(_ task: @escaping Task) {
        _async(task)
    }
    public static func async(_ task: @escaping Task,
                             mainTask: @escaping Task) {
        _async(task, mainTask)
    }
    
    private static func _async(_ task: @escaping Task,
                               _ mainTask: Task? = nil) {
        let item = DispatchWorkItem( block: task)
        DispatchQueue.global().async(execute: item)
        if let main = mainTask {
            item.notify(queue: DispatchQueue.main, execute: main)
        }
    }
}

延迟

    @discardableResult
    public static func delay(_ seconds: Double, _ block: @escaping Task) -> DispatchWorkItem {
        let item = DispatchWorkItem(block: block)
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
        return item
    }

异步延迟

    @discardableResult
    public static func asyncDelay(_ seconds: Double ,
                                  _ task: @escaping Task) -> DispatchWorkItem {
        return _asyncDelay(seconds, task)
    }
    @discardableResult
    public static func asyncDelay(_ seconds: Double,
                                  _ task: @escaping Task,
                                  _ mainTask: @escaping Task) -> DispatchWorkItem {
        return _asyncDelay(seconds, task, mainTask)
    }
    
    private static func _asyncDelay(_ seconds: Double,
                                    _ task: @escaping Task,
                                    _ mainTask: Task? = nil) -> DispatchWorkItem {
    let item = DispatchWorkItem(block: task)
        DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds,
                                          execute: item)
        if let main = mainTask {
            item.notify(queue: DispatchQueue.main, execute: main)
        }
        return item
    }

once

■ dispatch_once在Swift中已被废弃,取而代之

  • 可以用类型属性或者全局变量\常量
  • 默认自带lazy + dispatch_once效果
let age1: Int = {
print( 666)
return 10
}()
class ViewController: UIViewController {
    static let age2: Int = {
        print (888)
        return 20
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print (age1)
        print(age1)
        // 666 10 10
        print (ViewController.age2)
        print (ViewController.age2)
        // 888 20 20
    }
}

加锁

//gcd信号量

class Cache {
    private static var data = [String: Any]()
    private static var lock = DispatchSemaphore(value: 1)
    
    static func set(_ key: String, value: Any) {
        lock.wait()
        defer { lock.signal() }
        data [key] = value
    }
}
//Foundation
    private static var lock = NSLock()
    static func set(_ key: String,_ value: Any) {
        lock.lock()
        defer { lock.unlock() }
    }
private static var lock = NSRecursiveLock()
    static func set(_ key: String,_ value: Any) {
        lock.lock()
        defer { lock.unlock() }
    }

Swift常用基础知识(二)

你可能感兴趣的:(Swift常用基础知识(一))