访问级别 | 描述 | 同一模块内可见 | 不同模块内可见 | 示例 |
---|---|---|---|---|
open |
最高访问级别,允许实体被定义模块外的其他模块访问,也可以被继承和重写。 | ✅ | ✅ | 用于公共框架和库的公共接口,如一个开源框架的公共API。 |
public |
允许实体被定义模块外的其他模块访问,但不能被继承和重写。 | ✅ | ❌ | 用于公共接口的内部实现,如一个库的具体实现细节。 |
internal |
默认访问级别,允许实体在定义模块内部任何地方访问,但不能被定义模块外的其他模块访问。 | ✅ | ❌ | 用于应用程序或框架的内部实现,如应用程序的各个组件之间的交互。 |
fileprivate |
限制实体只能在其定义的文件内部访问。 | ✅ | ❌ | 用于实现细节和私有功能,如一个类的内部细节只对该类的其他成员可见。 |
private |
限制实体只能在其定义的作用域(类、结构体、枚举或扩展)内部访问。 | ✅ | ❌ | 用于实现细节和私有功能,如一个结构体内部的数据成员只能在该结构体的方法中使用。 |
示例:
// ModuleA.swift
open class MyOpenClass {}
public class MyPublicClass {}
internal class MyInternalClass {}
fileprivate class MyFilePrivateClass {}
private class MyPrivateClass {}
// ModuleB.swift
import ModuleA
// 在 ModuleB.swift 中可以访问 MyOpenClass、MyPublicClass 和 MyInternalClass
// 但不能访问 MyFilePrivateClass 和 MyPrivateClass
Any
、AnyObject
和Generics
区别这里是关于 Any
、AnyObject
和泛型的一些区别:
Any
:
Any
是 Swift
中的⼀种类型擦除(type erasure)的概念,是⼀个协议(protocol)。Any
声明的变量可以存储任何类型的值,包括值类型和引⽤类型。Any
是 Swift
中的一个特殊类型,用于表示任意类型的实例。Any
时, Swift
编译器会放弃类型检查,因此需要⼩⼼使⽤,避免类型错误。var anyValue: Any
anyValue = 42
print(anyValue) // 输出: 42
anyValue = "Hello, Swift"
print(anyValue) // 输出: Hello, Swift
anyValue = [1, 2, 3]
print(anyValue) // 输出: [1, 2, 3]
AnyObject
:
AnyObject
是 Swift
中的⼀个协议( protocol
),表示任何类(引⽤类型)类型的实例。AnyObject
声明的变量可以存储任何类类型的实例,但不能存储结构体、枚举或其他类型的实例。AnyObject
是一个协议类型,所有类都隐式地遵循了 AnyObject
协议。var anyObjectValue: AnyObject
class MyClass {}
anyObjectValue = MyClass()
print(anyObjectValue) // 输出:
class YourClass {}
anyObjectValue = YourClass()
print(anyObjectValue) // 输出:
Generics
:
Swift
中的⼀种编程技术,⽤于编写可以处理任意类型的代码。Any
时的类型不确定性问题。func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var intValue1 = 42
var intValue2 = 10
swapTwoValues(&intValue1, &intValue2)
print("intValue1: \(intValue1), intValue2: \(intValue2)") // 输出: intValue1: 10, intValue2: 42
var stringValue1 = "Hello"
var stringValue2 = "World"
swapTwoValues(&stringValue1, &stringValue2)
print("stringValue1: \(stringValue1), stringValue2: \(stringValue2)") // 输出: stringValue1: World, stringValue2: Hello
简而言之,Any
和 AnyObject
是用于处理不同类型的实例的机制,而泛型则是一种编写灵活、可重用代码的机制。Any
可以表示任何类型的实例,而 AnyObject
只能表示类类型的实例。Generics 允许在编译时编写灵活的代码,以处理不同类型的数据。
final
关键字 ⽤过没 主要⽤来⼲嘛final
关键字在 Swift 中用于限制类、属性和方法的继承和重写。具体来说,final
有以下用途:
限制类的继承:通过在类的定义前加上 final
关键字,可以防止其他类继承该类。这样做可以确保类的实现不会被修改或扩展,从而提高代码的安全性和稳定性。
final class FinalClass {}
// OtherClass: FinalClass // 编译错误,FinalClass 不能被继承
限制属性的重写:通过在属性的定义前加上 final
关键字,可以防止子类重写该属性。这样做可以确保父类的属性在子类中保持不变。
class ParentClass {
final var finalProperty: Int = 42
}
class ChildClass: ParentClass {
// override var finalProperty: Int // 编译错误,finalProperty 不能被重写
}
限制方法的重写:通过在方法的定义前加上 final
关键字,可以防止子类重写该方法。这样做可以确保父类的方法在子类中保持不变。
class ParentClass {
final func finalMethod() {
print("ParentClass final method")
}
}
class ChildClass: ParentClass {
// override func finalMethod() { // 编译错误,finalMethod 不能被重写
// print("ChildClass override method")
// }
}
总之,final
关键字用于限制类、属性和方法的继承和重写,从而提高代码的安全性和稳定性。特别是在框架或库中希望确保类的⾏为不被修改时⾮常有⽤。
static
和class
区别
static
和class
关键字都可以用来声明类型级别的属性和方法
static
:可以用在类、结构体和枚举中,用来声明类型级别的属性和方法。在类中,static
修饰的属性和方法可以被子类继承和重写。class
:只能用在类中,用来声明类型级别的属性和方法。在类中,class
修饰的属性和方法可以被子类继承,但是只有类方法可以被子类重写。static
:无法在子类中重写。class
:只能用于类方法,可以在子类中进行重写。static
:可以被继承。class
:可以被继承static
:可以直接通过类型名称来访问,例如 ClassName.staticMember。class
:只能通过实例或子类来访问,例如 instanceOfClassOrSubclass.classMember。class Message {
// 使用 static 关键字声明静态属性
static var staticProperty: Int = 0
// 使用 class 关键字声明类属性
class var classProperty: Int {
get {
return 10
}
set {
print("Setting class property to \(newValue)")
}
}
// 使用 static 关键字声明静态方法
static func staticMethod() {
print("This is a static method")
}
// 使用 class 关键字声明类方法
class func classMethod() {
print("This is a class method")
}
}
class Chat: Message {
// 重写类属性
override class var classProperty: Int {
get {
return 100
}
set {
print("Setting class property to \(newValue)")
}
}
// 重写类方法
override class func classMethod() {
print("This is a subclass's class method")
}
}
// 调用静态属性和方法
Message.staticProperty = 5
print("Static Property: \(Message.staticProperty)")
Message.staticMethod()
// 调用类属性和方法
print("Class Property: \(Message.classProperty)")
Message.classMethod()
// 调用子类的类方法
Chat.classMethod()
注:如果你需要在子类中重写方法或属性,应该使用
class
关键字。如果不需要重写,并且希望在类、结构体或枚举中统一处理类型级别的属性和方法,那么可以使用static
关键字。
extension
⽤过没,都能⼲嘛是的,extension
在 Swift 中经常被使用。Swift 的 extension
(扩展)允许你在不修改原始代码的情况下,扩展现有类型的功能,包括类、结构体、枚举和协议。使用 extension
可以实现以下功能:
extension Int {
func squared() -> Int {
return self * self
}
}
let number = 5
let squaredNumber = number.squared() // 25
extension Double {
var squared: Double {
return self * self
}
}
let doubleNumber = 3.0
let squaredDouble = doubleNumber.squared // 9.0
extension String {
init(repeating character: Character, count: Int) {
var string = ""
for _ in 0..<count {
string.append(character)
}
self = string
}
}
let repeatedString = String(repeating: "A", count: 5) // "AAAAA"
protocol CustomProtocol {
func customMethod()
}
extension Int: CustomProtocol {
func customMethod() {
print("Custom method called on Int")
}
}
let number = 10
number.customMethod() // 输出: Custom method called on Int
protocol SomeProtocol {
func requiredMethod()
}
extension SomeProtocol {
func requiredMethod() {
print("Default implementation of required method")
}
}
struct SomeStruct: SomeProtocol {}
let someStruct = SomeStruct()
someStruct.requiredMethod() // 输出: Default implementation of required method
总之,extension
是一种强大的工具,用于扩展已有类型的功能,使代码更加模块化、易于维护和扩展。
if let
:适合在需要处理 nil
的情况下解包。guard let
:适合在函数或方法中提前返回。??
:适合提供默认值。注:切记使用**
!
**强制解包,需谨慎使用
class MyClass {
var myProperty: Int = 0 {
willSet {
print("New value will be set: \(newValue)")
}
didSet {
print("Old value was: \(oldValue)")
}
}
}
let instance = MyClass()
instance.myProperty = 5
// 输出:
// New value will be set: 5
// Old value was: 0
class MyClass {
var myProperty: Int = 0 {
didSet {
propertyDidChange?(myProperty)
}
}
var propertyDidChange: ((Int) -> Void)?
}
let instance = MyClass()
instance.propertyDidChange = { newValue in
print("Property did change: \(newValue)")
}
instance.myProperty = 5
// 输出: Property did change: 5
import Combine
class MyClass {
@Published var myProperty: Int = 0
}
let instance = MyClass()
let cancellable = instance.$myProperty.sink { value in
print("New value: \(value)")
}
instance.myProperty = 5
// 输出: New value: 5
@State
、@Binding
、@ObservedObject
和 @EnvironmentObject
。struct ContentView: View {
@State private var count: Int = 0
var body: some View {
VStack {
Text("Count: \(count)")
Button("Increment") {
count += 1
}
}
}
}
class MyModel: ObservableObject {
@Published var count: Int = 0
}
struct ContentView: View {
@ObservedObject var model: MyModel
var body: some View {
VStack {
Text("Count: \(model.count)")
Button("Increment") {
model.count += 1
}
}
}
}
struct ParentView: View {
@State private var count: Int = 0
var body: some View {
VStack {
ChildView(count: $count)
Button("Increment") {
count += 1
}
}
}
}
struct ChildView: View {
@Binding var count: Int
var body: some View {
Text("Count: \(count)")
}
}
class UserData: ObservableObject {
@Published var username = "John"
}
struct ContentView: View {
@EnvironmentObject var userData: UserData
var body: some View {
VStack {
Text("Hello, \(userData.username)!")
}
}
}
struct MainView: View {
var body: some View {
ContentView()
.environmentObject(UserData())
}
}
以上是 Swift 和 SwiftUI 中监听属性值变化的几种方法,开发者可以根据具体需求选择适合的方法。
Swift 中的高阶函数是指那些以其他函数作为参数或者返回值的函数。这些函数通常用于简化代码、提高可读性和功能性。常见的高阶函数包括 map
、filter
、reduce
、flatMap
、compactMap
、allSatisfy
等。
map
map
函数用于对集合中的每个元素应用一个指定的转换闭包,然后返回一个包含转换结果的新集合。
函数定义:
func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
示例:
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled) // 输出 [2, 4, 6, 8, 10]
filter
filter
函数用于从集合中选择满足指定条件的元素,并返回一个包含满足条件的元素的新集合。
函数定义:
func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]
示例:
let numbers = [1, 2, 3, 4, 5]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // 输出 [2, 4]
reduce
reduce
函数用于将集合中的所有元素组合成单个值,并返回该值。
函数定义:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
示例:
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0, { $0 + $1 })
print(sum) // 输出 15
flatMap
flatMap
函数用于对集合中的每个元素应用一个转换闭包,并将结果拼接成一个新的集合。
函数定义:
func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
示例:
let nestedArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flatArray = nestedArray.flatMap { $0 }
print(flatArray) // 输出 [1, 2, 3, 4, 5, 6, 7, 8, 9]
compactMap
compactMap
函数用于对集合中的每个元素应用一个转换闭包,并过滤掉结果中的 nil 值。
函数定义:
func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
示例:
let strings = ["1", "2", "3", "hello", "5"]
let numbers = strings.compactMap { Int($0) }
print(numbers) // 输出 [1, 2, 3, 5]
allSatisfy
allSatisfy
函数用于检查序列中的所有元素是否都满足指定条件。
函数定义:
func allSatisfy(_ predicate: (Element) throws -> Bool) rethrows -> Bool
示例:
let numbers = [2, 4, 6, 8, 10]
let allEven = numbers.allSatisfy { $0 % 2 == 0 }
print(allEven) // 输出 true
以上就是 Swift 中常用的高阶函数,它们可以帮助你更加高效地处理集合中的元素。
zip
函数吗,用来干嘛zip
函数是一种合并两个序列的函数,它接受两个序列作为输入,并将它们的元素一一对应地组合成元组,然后返回一个包含这些元组的序列。在 Swift 中,zip 函数通常被归类为集合操作函数或序列操作函数,因为它主要用于处理集合类型(如数组、字典等)或序列类型(如数组、字符串等)的数据。
在刷Leetcode
3114. 替换字符可以得到的最晚时间,这个题的一个题解就可以使用zip函数和高阶函数allSatisfy来实现,代码里面有两种写法其实都是一样的,注释掉的那种是隐藏了参数类型
当我们调用 zip(s, t)
时,它会将两个序列 s
和 t
中的元素一一配对,生成一个包含元组的序列。在这里,s
是输入字符串,t
是从最晚时间开始逆序生成的时间字符串。例如,如果 s = "1?:?4"
,而 t = "11:54"
,那么 zip(s, t)
将生成一个包含元组 ("1", "1")
、("?", "1")
、("?", ":")
、("?", "5")
、("4", "4")
的序列。
接下来,我们调用 allSatisfy
函数来检查这个序列中的所有元素是否满足某个条件。在这里,我们使用一个闭包作为参数传递给 allSatisfy
函数。闭包接受一个元组作为输入参数,其中的第一个元素是来自输入字符串 s
,第二个元素是来自时间字符串 t
。闭包中的条件表达式 { $0 == "?" || $0 == $1 }
检查每个元组中的第一个元素是否是问号,或者是否与第二个元素相等。
这样,如果序列中的所有元素都满足这个条件,即如果输入字符串中的每个字符要么是问号,要么与对应的时间字符串中的字符相等,那么 allSatisfy
函数就会返回 true
。这表示时间字符串 t
符合要求,我们就返回它。
给定一个由 ?
和数字组成的字符串 time
,请你将其中的 ?
替换为可以构成的最晚有效时间。其中字符串表示的时间需符合 24 小时制的格式,最晚有效时间指的是 23:59。
如果无法构成最晚有效时间,请返回一个空字符串。字符串中每个 ?
都可以被任何一位数字替换。
输入:time = "2?:?0"
输出:"23:50"
输入:time = "0?:3?"
输出:"09:39"
输入:time = "1?:22"
输出:"19:22"
输入:time = "?4:03"
输出:"14:03"
func findLatestTimee(_ s: String) -> String {
// 从最晚时间开始逆序遍历小时和分钟
for h in (0...11).reversed() {
for m in (0...59).reversed() {
// 将小时和分钟格式化为两位数的字符串
let hour = String(format: "%02d", h)
let minute = String(format: "%02d", m)
// 组合小时和分钟,形成时间字符串 t
let t = hour + ":" + minute
// 检查时间字符串 t 是否与输入字符串 s 相匹配
// if zip(s, t).allSatisfy({ $0 == "?" || $0 == $1 }) {
// // 如果匹配,则返回时间字符串 t
// return t
// }
if zip(s, t).allSatisfy({ (element:(Character, Character)) in
print("dddd")
return element.0 == "?" || element.0 == element.1
}) {
return t
}
}
}
// 如果找不到匹配的时间字符串,则返回空字符串
return ""
}
// 测试样例
print(findLatestTimee("2?:?0")) // 输出 "23:50"
print(findLatestTimee("0?:3?")) // 输出 "09:39"
print(findLatestTimee("1?:22")) // 输出 "19:22"
print(findLatestTimee("?4:03")) // 输出 "14:03"
以上是替换字符可以得到的最晚时间的实现代码。