1.元类型
元类型是指所有类型的类型,包括类、结构体、枚举和协议。
类、结构体或枚举类型的元类型是相应的类型名紧跟.Type。
协议类型的元类型——并不是运行时适配该协议的具体类型——是该协议名字紧跟.Protocol。
比如,类SomeClass的元类型就是SomeClass.Type,协议SomeProtocol的元类型就是SomeProtocal.Protocol。
你可以使用后缀self表达式来获取类型。比如,SomeClass.self返回SomeClass本身,而不是SomeClass的一个实例。同样,SomeProtocol.self返回SomeProtocol本身,而不是运行时适配SomeProtocol的某个类型的实例。还可以对类型的实例使用type(of: someInstance)表达式来获取该实例在运行阶段的类型
protocol TestProtocol {
func test()
}
class SomeBaseClass {
class func printClassName() {
print("SomeBaseClass")
}
}
class SomeSubClass: SomeBaseClass {
var title = "标题"
override class func printClassName() {
print("SomeSubClass")
}
}
let someInstance: SomeBaseClass = SomeSubClass()
print(type(of: someInstance))
print(type(of: SomeSubClass.self))
print(type(of: TestProtocol.self))
SomeSubClass
SomeSubClass.Type
TestProtocol.Protocol
2. .self和self
T.self:T是实例对象,当前T.self返回的就是实例对象本身;T是类,当前T.self返回的就是类型本身
let p = SomeSubClass()
print("\(p.self) === \(type(of: p.self))")
print("\(SomeSubClass.self)--------\(type(of: SomeSubClass.self))")
TestDemo1.ViewController.SomeSubClass === SomeSubClass
SomeSubClass--------SomeSubClass.Type
在实例方法中self是实例对象的本身;在类方法中self是类型本身。
class SomeSubClass: SomeBaseClass {
var title = "标题"
class func test(){
print("类方法:\(self)")
}
func test(){
print("对象方法:\(self)")
}
}
对象方法:TestDemo1.ViewController.SomeSubClass
类方法:SomeSubClass
3.Self
Self类型不是特定类型,而是为了方便引用当前类型,而无需重复或知道该类型的名称。在协议声明或者协议成员声明中,Self类型是指最终符合协议的类型。
func get() -> Self {
return self
}
static let age = 18
func test1(){
print("Self指代类型本身:\(Self.age)")
}
4.AnyObject、Any和AnyClass
AnyObject可以代表任意 class 类型(用来替代OC中的 id)
public typealias AnyObject
由定义就可以看出它就是一个接口,所有的 class 都隐式地实现了这个接口。所以 AnyObject 只适用于 class 类型。但是 swift 中的基本类型都是 struct 类型,并不能用 AnyObject 来表示。所以官方又提出了一个更特殊的 Any 类型,它除了 class 以外还可以表示其他类型,可以说是任意类型(包括 struct,enum,func等)。
例如:
let swiftArr = ["a", "b", "c"]
let swiftStr = "hello world"
var array = [AnyObject]()
array.append(swiftArr as AnyObject)
array.append(swiftStr as AnyObject)
这里我们显示的将 swift 中的 String 和 Array 转成了 AnyObject。实际上 array 里面的元素已经变成了 NSString 和 NSArray 了。
当然我们还有另外的方式解决此问题,用 Any。
let swiftArr1 = ["a", "b", "c"]
let swiftStr1 = "hello world"
var array1 = [Any]()
array1.append(swiftArr1)
array1.append(swiftStr1)
可以看到结果全部是 swift 中的原生类型
值得注意的是 Any 类型使用的时候需要使用 as 关键字做类型转换, 例如:
let string = mixed.first as? String {
print("The first element of mixed is \(string)")
}
AnyClass是AnyObject.Type的别名而已
public typealias AnyClass = AnyObject.Type
表示任意类的元类型,任意类的类型都隐式遵守这个协议,一般我们使用不多
5.可选类型(Optional)
您可以通过追加将数据类型简单地表示为 Optional。 方法是类型附加! 或 ?。 如果可选变量中包含一个值,则将其值返回为 Optional
var someValue:Int?
var someAnotherValue:Int!
print(someValue)
print(someAnotherValue)
注意:访问null的未包装可选对象时发生致命错误崩溃
var someValue:Int!
var unwrappedValue:Int = someValue
当您运行该程序时,您将得到致命错误的崩溃:解开Optional值时意外发现nil,因为代码unwrappedValue:Int = someValue试图将Optional someValue中的值分配给变量unwrappedValue。
但是,somevalue 是一个包含 nil 值的可选类型。试图将 nil 值分配给变量 unwrappedValue (这不是一个Optional)将导致崩溃。
可以用if判断nil+iflet+guard三种方式处理上面崩溃问题
6.类型别名(Typealias)
类型别名不会创建新类型。它们只是为现有类型提供一个新名称。
typealias name = existing type
在Swift中,大多数类型都可以使用typealias。它们可以是:
1.内置类型(例如:String, Int)
2.用户定义的类型(例如:类,结构,枚举)
3.复杂类型(例如:闭包)
func someMethod(oncomp:(Int)->(String)){
}
typealias CompletionHandler = (Int)->(String)
func someMethod(oncomp:CompletionHandler){
}
7.关联类(associatedtype)
associatedtype定义关联类型,相当于类型的占位符,让实现协议的类型来指定具体的类型
protocol Food {
}
protocol Animal {
associatedtype F: Food
func eat(_ food: F)
}
struct Meat: Food {
}
struct Tiger: Animal {
func eat(_ food: Meat) {
print("eat \(food)")
}
}
/*
具有关联类型的协议类型,只能当做泛型约束使用
错误代码:
func isTiger(animal: Animal) -> Bool {
}
*/
// 具有关联类型的协议类型,只能当做泛型约束使用
func isTiger(animal: A) -> Bool {
if animal is Tiger {
return true
} else {
return false
}
}
8.类型修饰词
1.available
可用来标识计算属性、函数、类、协议、结构体、枚举等类型的生命周期。(依赖于特定的平台版本 或 Swift 版本)。它的后面一般跟至少两个参数,参数之间以逗号隔开。
if #available(iOS 11.0, *) {
scrollView.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}
还有一种用法是放在函数、结构体、枚举、类或者协议的前面,表示当前类型仅适用于某一平台
@available(iOS 12.0, *)
func adjustDarkMode() {
/* code */
}
@available(iOS 12.0, *)
struct DarkModeConfig {
/* code */
}
@available(iOS 12.0, *)
protocol DarkModeTheme {
/* code */
}
2.@discardableResult
带返回的函数如果没有处理返回值会被编译器警告。但有时我们就是不需要返回值的,这个时候我们可以让编译器忽略警告,就是在方法名前用@discardableResult声明一下。可以参考Alamofire中request的写法:
@discardableResult
public func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
return SessionManager.default.request(
url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
3.@inlinable
这个关键词是可内联的声明,它来源于C语言中的inline。C中一般用于函数前,做内联函数,它的目的是防止当某一函数多次调用造成函数栈溢出的情况。因为声明为内联函数,会在编译时将该段函数调用用具体实现代替,这么做可以省去函数调用的时间。
内联函数常出现在系统库中,OC中的runtim中就有大量的inline使用
需要注意内联声明不能用于标记为private或者fileprivate的地方,这很好理解,对私有方法的内联是没有意义的。内联的好处是运行时更快。因为是编译时做替换,这增加了编译的开销,会相应的延长编译时间。
4.@warn_unqualified_access
通过命名我们可以推断出其大概含义:对“不合规”的访问进行警告。这是为了解决对于相同名称的函数,不同访问对象可能产生歧义的问题。
比如说,Swift 标准库中Array和Sequence均实现了min()方法,而系统库中也定义了min(::),对于可能存在的二义性问题,我们可以借助于@warn_unqualified_access。
extension Array where Self.Element : Comparable {
@warn_unqualified_access
@inlinable public func min() -> Element?
}
extension Sequence where Self.Element : Comparable {
@warn_unqualified_access
@inlinable public func min() -> Self.Element?
}
extension Array where Element: Comparable {
func minValue() -> Element? {
return min()
}
}
我们会收到编译器的警告:Use of 'min' treated as a reference to instance method in protocol 'Sequence', Use 'self.' to silence this warning。它告诉我们编译器推断我们当前使用的是Sequence中的min(),这与我们的想法是违背的。因为有这个@warn_unqualified_access限定,我们能及时的发现问题,并解决问题:self.min()。
5.@objc
把这个特性用到任何可以在 Objective-C 中表示的声明上——例如,非内嵌类,协议,非泛型枚举(原始值类型只能是整数),类和协议的属性、方法(包括 setter 和 getter ),初始化器,反初始化器,下标。 objc 特性告诉编译器,这个声明在 Objective-C 代码中是可用的。
@objc还有一个用处是当你想在OC的代码中暴露一个不同的名字时,可以用这个特性,它可以用于类,函数,枚举,枚举成员,协议,getter,setter等。
当在OC代码中访问enabled的getter方法时,是通过isEnabled
class ExampleClass: NSObject {
@objc var enabled: Bool {
@objc(isEnabled) get {
// Return the appropriate value
}
}
}
6.@objcMembers
因为Swift中定义的方法默认是不能被OC调用的,除非我们手动添加@objc标识。但如果一个类的方法属性较多,这样会很麻烦,于是有了这样一个标识符@objcMembers,它可以让整个类的属性方法都隐式添加@objc,不光如此对于类的子类、扩展、子类的扩展都也隐式的添加@objc,当然对于OC不支持的类型,仍然无法被OC调用
7.@frozen 和@unknown default
@frozen即冻结,保证之后该值类型不会再变。其实我们常用的类型像Int、Float、Array、Dictionary、Set等都已被“冻结”。需要说明的是冻结仅针对struct和enum这种值类型,因为他们在编译器就确定好了内存布局。
对于没有标记为frozen的枚举例如AVPlayerItem.Status,则认为该枚举值在之后的系统版本中可能变化。对于可能变化的枚举,我们在列出所有case的时候还需要加上对@unknown default的判断,这一步会有编译器检查。
switch currentItem.status {
case .readyToPlay:
/* code */
case .failed:
/* code */
case .unknown:
/* code */
@unknown default:
fatalError("not supported")
}
- lazy
lazy是懒加载的关键词,当我们仅需要在使用时进行初始化操作就可以选用该关键词。
lazy var dayLabel: UILabel = {
let label = UILabel()
label.text = self.todayText()
return label
}()
使用lazy你可能会发现它只能通过var初始而不能通过let,这是由lazy 的具体实现细节决定的:它在没有值的情况下以某种方式被初始化,然后在被访问时改变自己的值,这就要求该属性是可变的。
9.unowned weak
weak相当于oc里面的weak,弱引用,不会增加循环计数。主体对象释放时被weak修饰的属性也会被释放,所以weak修饰对象就是optional。
·unowned相当于oc里面的unsafe_unretained,它不会增加引用计数,即使它的引用对象释放了,它仍然会保持对被已经释放了的对象的一个 "无效的" 引用,它不能是 Optional 值,也不会被指向nil。如果此时为无效引用,再去尝试访问它就会crash。
lazy var someClosure: () -> Void = { [weak self] in
// 被weak修饰后self为optional,这里是判断self非空的操作
guard let self = self else { retrun }
self.doSomethings()
}
10.some
some是Swift5.1新增的特性。它的用法就是修饰在一个 protocol 前面,默认场景下 protocol 是没有具体类型信息的,但是用some 修饰后,编译器会让 protocol 的实例类型对外透明。
可以通过一个例子理解这段话的含义,当我们尝试定义一个遵循Equatable协议的value时:
// Protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements
var value: Equatable {
return 1
}
var value: Int {
return 1
}
编译器提示我们Equatable只能被用来做泛型的约束,它不是一个具体的类型,这里我们需要使用一个遵循Equatable的具体类型(Int)进行定义。但有时我们并不想指定具体的类型,这时就可以在协议名前加上some,让编译器自己去推断value的类型:
var value: some Equatable {
return 1
}
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
9.属性修饰词和权限控制词
swift提供了5个不同的访问级别,权限最高的是open,其次依次是public、internal、fileprivate,最低是private。默认使用的是internal。
open & public
使用open和public定义的实体,在它们定义的module的任意文件中皆可使用。其他module如果import了此实体,则其他module的源文件也可使用。一般在framework中指定的公开接口里,使用open和public级别。
open 和 public 的区别
public或更低权限的类,只能在其定义的module中子类化
public或更低权限的类的成员,只能在其定义的module中被重写或子类化
open权限的类,可在其定义的module,或者import了其的module中子类化
open权限的类的成员,可在其定义的module,或者import了其的module中重写或子类化
internal
intenal修饰的实体在其定义的module中皆可使用,但是在其他module中无法使用。一般定义APP或者framework内部结构的时候,会使用internal级别
fileprivate
fileprivate定义的实体,只能在其源文件中使用,当其只在整个源文件中使用的时候,使用fileprivate级别
private
private定义的实体,只能在定义的范围内,和其在同一文件的extension中使用,当其只在当前声明范围内使用的时候,使用private级别
使用原则
子类:子类的访问级别不可以高于父类,但子类重写的方法的访问级别可以高于父类
枚举:枚举的每个值都和它们所属枚举有相同的权限,且不能单独为某个值定义权限
协议:协议可以指定访问级别,并且对于协议的每一项来说,都和协议的访问级别相同,不能单独为协议的某个方法指定不同的访问级别