当前位置:网站首页>Swift - 标红的修饰词

Swift - 标红的修饰词

2022-07-22 22:54:00 荒唐的天梯

访问控制 Access Control

open
public
internal
fileprivate
private
  • open/public 可以在任何模块中访问使用,即可以使用 import 在其他模块中访问,其中 open 只能修饰 class 类型,public 既可以修饰 class 类型,也可以修饰 struct/enum 类型
  • internal 默认访问权限,可以在当前模块中访问,不能在其他模块中使用
  • fileprivate 可以在当前文件中被访问,在其他文件中不能被访问
  • private 可以在当前 class 以及 struct/enum 中被访问,其他类中不能被访问

import

关于引入框架,我觉得很多人都在找类似 OCprefix 的文件,但是没找到,其实 Swift 中是存在这种方法的

@_exported import SDWebimage

众所周知,带有_ 的语句为未确定语句

将上面的代码放在 @main 函数执行的文件中进行引入,那么在当前模块中都无线再进行引入,可以直接使用

重命名与关联对象

typealias

typealias 用来对已经存在的类型重新定义新名字,通过命名,可以是代码变得更加清楚简洁。

typealias success = (_ data: String) -> Void

typealias ViewType = UIView

associatedType

associatedType 用来定义一个名字,也可以确定类型,具体使用的值需要用 typealias 来确定,一般在 protocol 中使用。

protocol Network {
    
	associatedType DataType // 没确定类型
	associatedType ViewType: UIView // 确定类型,实现时可以使用其子类
}

class Model: Network {
    
	typealias DataType = String
	typealias ViewType = UILabel
}

方法返回值

@discardableResult 一般用在有返回值的方法前,含义是,调用当前方法时,可以不使用对象去接收

func getName() -> String {
    
}
// 当调用该方法时需要使用对象承接
let name = getName()
// 或者
let _ = getName()

而如果不使用对象去接收时,编译器则会报告警告️,因此使用 @discardableResult 将警告消除

@discardableResult func getAge() -> Int {
    
}

getAge()

关于继承

只有 class 类型可以被继承这个概念不用我多说吧,那么就来说说继承之后的事

  • override 覆盖父类的方法或属性,如覆盖父类的 init 方法
override init() {
    
	// 必须要执行父类的init方法
	// 其中子类自定义的属性要放在 super.init() 方法前执行
	super.init()
	// 子类修改父类的属性要放在 super.init() 方法后执行
}

// 覆盖属性父类属性要使用计算属性覆盖
override var name: String {
    
	get {
    
		return ""
	}
	set {
    
	
	}
}
  • convenience 用于修饰 init 方法,为便利构造器,可以 override convenience 覆盖父类的便利构造器,方法中必须执行当前子类的构造器
override init() {
    
}

override convenience init(name: String) {
    
	self.init()
	self.name = name
}
  • required 子类必须重写的 init 方法
required init(_ sex: String) {
    
	fatalError("init(_:) has not been implemented")
}

动态成员查找

@dynamicMemberLookup 是在Swift 4.2 中增加的新特性,即动态成员查找。在使用了 @dynamicMemberLookup 修饰对象后(classstructenumprotocol),并且实现了 subscript(dynamicMember member: String) 方法后就可以方为不存在的属性,而属性就会作为 member 传入这个方法。

@dynamicMemberLookup
struct Person {
    
	subscript(dynamicMember member: String) -> String {
    
		let properties = ["nickname": "Zhang", "city": "BeiJing"]
		return properties[member, default: "nil"]
	}
}
// 执行
let p = Person()
print(p.city)
print(p.nickname)
print(p.name)

如果没有声明 @dynamicMemberLookup ,执行代码会报错
如果实现了多个 subscript(dynamicMember member: String) 方法时,需要在执行时执行返回类型

@dynamicMemberLookup
struct Person {
    
	subscript(dynamicMember member: String) -> String {
    
		let properties = ["nickname": "Zhang", "city": "BeiJing"]
		return properties[member, default: "nil"]
	}

	subscript(dynamicMember member: String) -> Int {
    
		return 18
	}
}

let p = Person()
let age: Int = p.age
print(age) // 18

@dynamicMemberLookup 内部处理

a = someValue.someMember
==>
a = someValue[dynamicMember: "someMember"]

动态传参

@dynamicCallable ,当对象被 @dynamicCallable 标记后,需要实现两个必要方法中的某一个或者两个来进行动态传参。

// 其中数组中的数据类型及返回类型可替换成其他类型
func dynamicallCall(withArguments args: [String]) -> Double

// 其中KeyValuePairs中的数据类型及返回类型可替换成其他类型
func dynamicallCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double

使用方法

@dynamicCallable
struct Random {
    
// func generate(numberOfZeros: Int) -> Double {
    
// let max = pow(10, Double(numberOfZeros))
// return Double.random(in: 0...max)
// }

	func dynamicallyCall(withArguments: [Int]) -> Double {
    
        return 12.0
    }
    
    func dynamicallyCall(withKeywordArguments args: [String: Int]) -> Double {
    
        let numberOfZeros = Double(args.first?.value ?? 0)
        let max = pow(10, Double(numberOfZeros))
        return Double.random(in: 0...max)
    }
}

// 调用
let random = Random()
// let result = random.dynamicallyCall(withKeywordArguments: ["numberOfZeros": 3])
let result = random(number: 3) // 调用 withKeywordArguments 方法
let result = random(3) // 12.0 调用 withArguments 方法
print(result)

@dynamicCallable 使用时需要注意一些重要规则

  1. 可以将其应用于 classstructenumprotocol
  2. 如果使用 withKeywordArguments: 并且不使用 withArguments: 你的类型仍然可以在没有参数标签的情况下调用 - 只不过会获得空字符串的键(key)
  3. 如果 withKeywordArguments: / withArguments: 被标记为 throwing ,则调用类型也将 throwing
  4. 不能将@dynamicCallable添加到扩展,只能添加到类型本身
  5. 你仍然可以向类型添加其他方法和属性,并正常使用。

Swift 的野心

Swift 之所以做出 @dynamicMemberLookup@dynamicCallable ,其根本原因在于 Swift 不仅仅将视线放在了 COC 的互通上,也将目光放在了将来与 PythonJavaScript 等动态语言一起工作。

属性包装器

@propertyWrapper 属性包装器的目的就是为了能够给属性一个统一的入口和出口,方便我们进行操作。比如当我们想监听属性时,需要在每个属性中做监听,而属性包装器可以将监听做成统一入口。

对于 @propertyWrapper,有两个要求:

  1. 必须使用 @propertyWrapper 进行定义
  2. 必须具有 wrappedValue 属性
@propertyWrapper
struct Random<T> {
    
    var wrappedValue: T
}

// 使用
@Random var r: Int = 0
// 或者
@Random(wrappedValue: 0) var r
// 或者
var r: Randow = Random(wrappedValue: 0)
/// 三种方法均可,通常使用第一种

系统定义好的属性包装器有 @State, @Binding, @Published, @ObservedObject, @StateObject, @EnvironmentObject, @Environment等。

// 关于在SwiftUI中有重大问题,将单列一篇文章来讲
@State private var height: CGFloat? // 在实际使用中有重大问题

使用限制

  • 具有包装器的属性不能再子类中覆盖
  • 具有包装器的属性不能有 lazy, @NSCoping, @NSManaged, weakunowned 修饰
  • wrappedValue, init(wrappedValue:)projectedValue 必须具有与包装类型本身相同或高级(并不生效)的访问控制级别
  • 不能再协议或扩展中声明带有包装器的属性

Where

where 在定义泛型的类中会被经常使用,

// 泛型
struct Stack<Element> {
    
}
// 如果要求泛型是某一特殊类型时
extension Stack where Element == String {
    
}
// 如果要求泛型遵循某一协议时
extension Stack where Element: Equatable {
    
}

// 定义非默认泛型
struct Sildeshow<Data, ID, Content>: View where Data: RandomAccessCollection, ID: Hashable, Content: View {
    
}
// 上面的定义可以写成
struct Sildeshow<Data: RandomAccessCollection, ID: Hashable, Content: View>: View {
    
}
原网站

版权声明
本文为[荒唐的天梯]所创,转载请带上原文链接,感谢
https://blog.csdn.net/LiqunZhang/article/details/125926955