Swift开发总结

1.单例的实现

class Singleton {
    // 静态属性
    static let shared = Singleton()
    // 私有构造器,防止外部创建新实例
    private init() {
    }

    var someProperty: Int = 0

    func someMethod() {
        
    }
}
  1. static let shared = Singleton(): 声明一个静态的常量属性 shared,它是 Singleton 类型的单一实例。

  2. private init(): 将构造器设为 private,防止外部直接创建 Singleton 类的新实例。

  3. 在需要使用单例实例的地方,直接通过 Singleton.shared 进行访问和操作。

// 访问单例属性和方法
Singleton.shared.someProperty = 42
Singleton.shared.someMethod()
  • 线程安全: 由于 shared 属性是静态的常量,在多线程环境下也能保证只有一个单例实例。

  • 延迟初始化Singleton 类的实例会在第一次访问 shared 属性时才被创建,节省资源。

  • 可扩展性: 单例类可以像普通类一样添加属性和方法,满足不同的需求。

  • 可测试性: 由于构造器是 private 的,可以通过依赖注入的方式来测试使用单例的类。

2.struct 与 class 的区别

  • 值类型 vs 引用类型:

    • Struct 是值类型,当它被赋值给一个变量或常量,或者被传递给函数时,会创建一个新的副本。修改副本不会影响原始的 Struct 实例。
    • Class 是引用类型,当它被赋值给一个变量或常量,或者被传递给函数时,只会创建一个对该实例的引用。修改引用会影响原始的 Class 实例。
// Struct 示例
struct Person {
    var name: String
    var age: Int
}

var person1 = Person(name: "Alice", age: 30)
var person2 = person1
person2.name = "Bob"
print(person1.name) // Output: "Alice"
print(person2.name) // Output: "Bob"

// Class 示例
class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let person1 = Person(name: "Alice", age: 30)
let person2 = person1
person2.name = "Bob"
print(person1.name) // Output: "Bob"
print(person2.name) // Output: "Bob"
  • 继承:

    • Class 支持继承,可以创建子类并继承父类的属性和方法。
    • Struct 不支持继承,但可以使用协议来实现类似的功能。
  • 初始化:

    • class 在初始化时不能直接把 property 放在默认的 constructor 的参数里,而是需要自己创建一个带参数的 constructor。struct 可以把属性放在默认的 constructor 的参数里。
class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let person = Person(name: "Alice", age: 30)


struct Person {
    let name: String
    let age: Int
}

let person = Person(name: "Alice", age: 30)
  • deinit:

    • Class 支持 deinit 方法,可以在实例被销毁时执行清理操作。
    • Struct 没有 deinit 方法,因为它们是值类型,在超出作用域时会自动销毁。
// Class deinit 示例
class Person {
    var name: String

    init(name: String) {
        self.name = name
        print("Person \(name) was initialized.")
    }

    deinit {
        print("Person \(name) was deinitialized.")
    }
}

var person: Person? = Person(name: "Alice")
person = nil // Output: "Person Alice was deinitialized."

// Struct 没有 deinit
struct Person {
    var name: String
}

var person = Person(name: "Alice")
// No deinit called
  • 类型转换:

    • Class 支持类型转换,可以在运行时检查和转换类实例的类型。
    • Struct 不支持类型转换,因为它们是值类型,没有运行时类型信息。
// Class 类型转换示例
class Animal {}
class Dog: Animal {}
class Cat: Animal {}

let animal: Animal = Dog()
if let dog = animal as? Dog {
    print("It's a dog!")
} else if let cat = animal as? Cat {
    print("It's a cat!")
} else {
    print("It's an unknown animal.")
}
  • 内存管理:

    • Class 实例需要手动管理内存,通常使用引用计数或自动引用计数(ARC)机制。
    • Struct 实例的内存管理是自动的,它们被分配在栈上,不需要手动管理。
  •  struct 的 function 要去改变 property 的值的时候要加上 mutating,而 class 不用。
struct Person {
    var name: String
    var age: Int

    mutating func updateName(to newName: String) {
        name = newName
    }
}

var person = Person(name: "Alice", age: 30)
person.updateName(to: "Bob")
print(person.name) // Output: "Bob"
  • struct 会自动生成需要的构造方法(constructor),哪个属性没有赋初始值就会生成以哪个属性为参数的构造方法。
struct Person {
    let name: String
    let age: Int = 30
}

let person = Person(name: "Alice") // Can call the constructor with name parameter
  • Struct 不能被序列化成 NSData 对象,原因是无法归解档。归解档的类必须遵守 NSCoding 协议,struct 不能遵守 NSCoding 协议。
  • 当项目的代码是 Swift 和 Objective-C 混合开发时,会发现在 Objective-C 的代码里无法调用 Swift 的 Struct。因为要在 Objective-C 里调用 Swift 代码的话,对象需要继承于 NSObject

总的来说,如果数据是简单的、不需要共享引用的,且不需要复杂的继承结构,使用 Struct 通常是更好的选择。而对于需要共享引用、继承、复杂内存管理的场景,使用 Class 会更合适。

3.如何使用map、filter和reduce

map:

  • 用于对集合中的每一个元素执行一个指定的操作,并返回一个新的集合。
  • 示例:将一个数组中的所有数字乘以2
let numbers = [1, 2, 3, 4, 5]
let doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers) // [2, 4, 6, 8, 10]

filter:

  • 用于从集合中过滤出符合指定条件的元素,并返回一个新的集合。
  • 示例:从数组中过滤出所有偶数
let numbers = [1, 2, 3, 4, 5]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // [2, 4]

reduce:

  • 用于将集合中的所有元素组合成一个单一的值。
  • 它接受一个初始值和一个闭包函数,该函数将前一次的结果和当前元素合并成一个新的值。
  • 示例:计算数组元素的和
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { $0 + $1 }
print(sum) // 15

4.计算属性和存储属性

存储属性:

  • 存储属性是与特定类、结构体或枚举相关联的常量或变量。
  • 它们直接存储值,可以是任意类型,包括基本数据类型、自定义类型等。

计算属性:

  • 计算属性不直接存储值,而是提供一个 getter 和可选的 setter 来间接地访问和修改其他属性或变量的值。
  • 计算属性的声明使用 var 关键字,即使属性本身是只读的。
struct Circle {
    var radius: Double
    var area: Double {
        get {
            return Double.pi * radius * radius
        }
        set {
            radius = sqrt(newValue / Double.pi)
        }
    }
}

let myCircle = Circle(radius: 5.0)
print(myCircle.area) // 输出: 78.53981633974483
myCircle.area = 100.0
print(myCircle.radius) // 输出: 5.641895835477563

在这个例子中,area是一个计算属性,它根据radius属性计算圆的面积。当设置area时,它会自动更新radius的值。

计算属性的优势在于:

  • 可以根据其他属性或变量的值动态计算属性值
  • 可以提供自定义的 getter 和 setter 方法,控制属性的读写行为
  • 可以实现只读或只写属性

5.枚举的其它用法

关联值 (Associated Values):

  • 枚举成员可以关联不同类型的值。
  • 这些值在每个枚举成员中都可以是不同的。
  • 示例:
enum CompassDirection {
    case north(direction: String, degrees: Int)
    case south(direction: String, degrees: Int)
    case east(direction: String, degrees: Int)
    case west(direction: String, degrees: Int)
}

let northDirection = CompassDirection.north(direction: "North", degrees: 0)

原始值 (Raw Values):

  • 枚举成员可以有预定义的原始值,通常是 IntStringCharacter 或 Bool 类型。
  • 原始值在每个枚举成员中必须是唯一的。
  • 示例:
enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

let earth = Planet(rawValue: 3)
print(earth?.rawValue) // 输出: 3

递归枚举 (Recursive Enumerations):

  • 枚举成员可以包含另一个同类型的枚举作为关联值。
  • 这种情况下,枚举需要使用 indirect 关键字修饰。
  • 示例:
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

枚举的计算属性和方法:

  • 枚举可以定义计算属性和方法,就像结构体和类一样。
  • 这些属性和方法可以访问枚举成员的关联值或原始值。
  • 示例:
enum TrafficLight {
    case red, yellow, green

    var description: String {
        switch self {
        case .red:
            return "Stop"
        case .yellow:
            return "Slow down"
        case .green:
            return "Go"
        }
    }

    func next() -> TrafficLight {
        switch self {
        case .red:
            return .green
        case .yellow:
            return .red
        case .green:
            return .yellow
        }
    }
}

let light = TrafficLight.red
print(light.description) // 输出: "Stop"
let nextLight = light.next()
print(nextLight) // 输出: green

枚举遵循协议:

  • 在 Swift 中,枚举可以像类和结构体一样实现协议。
  • 这允许我们为枚举添加协议中定义的属性、方法和下标。
  • 示例:
protocol Named {
    var name: String { get }
}

enum WeekDay: Named {
    case monday, tuesday, wednesday, thursday, friday, saturday, sunday
    
    var name: String {
        switch self {
        case .monday: return "Monday"
        case .tuesday: return "Tuesday"
        // 其他情况...
        }
    }
}

在这个例子中,我们定义了一个 Named 协议,然后让 WeekDay 枚举遵循这个协议。这允许我们为枚举成员添加 name 属性的实现。

通过扩展增加功能:

  • 我们还可以通过扩展来为枚举增加额外的功能。
  • 扩展允许我们在不修改枚举本身的情况下,为其添加新的计算属性、方法和下标。
  • 示例:
extension WeekDay {
    var isWeekend: Bool {
        return self == .saturday || self == .sunday
    }
    
    func next() -> WeekDay {
        switch self {
        case .sunday: return .monday
        case .monday: return .tuesday
        // 其他情况...
        }
    }
}

let today = WeekDay.monday
print(today.name) // 输出: "Monday"
print(today.isWeekend) // 输出: false
print(today.next().name) // 输出: "Tuesday"

在这个例子中,我们通过扩展为 WeekDay 枚举添加了 isWeekend 计算属性和 next() 方法,这些功能并没有在枚举的原始定义中出现。

6.?解包

可选绑定 (Optional Binding):

  • 可选绑定用于安全地访问可选类型的值。
  • 它允许我们在使用可选值之前先检查它是否为 nil
  • 可选绑定使用 if let 或 guard let 语句来实现。
  • 示例:
let myString: String? = "Hello, world!"
if let unwrappedString = myString {
    print(unwrappedString) // 输出: "Hello, world!"
} else {
    print("myString is nil")
}

在这个例子中,我们使用 if let 语句来检查 myString 是否为 nil。如果不为 nil,则将其解包并赋值给 unwrappedString 常量,然后在 if 块内使用它。否则,我们会进入 else 块并输出 "myString is nil"。

可选链接 (Optional Chaining):

  • 可选链接用于安全地访问可选类型中的属性、方法或下标。
  • 它允许我们在可选值可能为 nil 的情况下进行访问,而不会引发运行时错误。
  • 可选链接使用 ? 运算符来实现。
  • 示例:
class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms: Int = 1
}

let john = Person()
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("John doesn't have a residence.")
}

在这个例子中,我们使用 john.residence?.numberOfRooms 来访问 Person 实例的 residence 属性和 Residence 实例的 numberOfRooms 属性。如果 residence 属性为 nil,则整个表达式的值也为 nil,从而避免了运行时错误。

nil 合并运算符 (??):

  • nil 合并运算符 ?? 用于安全地获取可选类型的值。
  • 它提供了一种简单的方式来指定,如果可选值为 nil,应该返回一个备用值。
  • 示例:
let userName: String? = nil
let greeting = "Hello, \(userName ?? "Guest")"
print(greeting) // 输出: "Hello, Guest"

在这个例子中,如果 userName 为 nil,则 userName ?? "Guest" 会返回字符串 "Guest"

强制解包运算符 (!):

  • 强制解包运算符 ! 用于将可选类型强制转换为非可选类型。
  • 它会直接访问可选类型中的值,而不进行任何安全检查。
  • 如果可选值为 nil,使用 ! 会导致运行时错误。
  • 示例:
let myString: String? = "Hello, world!"
let unwrappedString = myString! // 如果 myString 为 nil,这会导致运行时错误
print(unwrappedString) // 输出: "Hello, world!"

在这个例子中,我们使用 ! 运算符将 myString 强制转换为非可选类型 String。如果 myString 恰好为 nil,这种强制转换会导致运行时错误。因此,强制解包运算符 ! 应该谨慎使用,最好先确保可选值不为 nil

未完待续。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值