Swift 元类型、self、Self、AnyObject、Any和AnyClass

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 ,否则返回nil。

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")

}
  1. 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级别

使用原则
子类:子类的访问级别不可以高于父类,但子类重写的方法的访问级别可以高于父类
枚举:枚举的每个值都和它们所属枚举有相同的权限,且不能单独为某个值定义权限
协议:协议可以指定访问级别,并且对于协议的每一项来说,都和协议的访问级别相同,不能单独为协议的某个方法指定不同的访问级别

你可能感兴趣的:(Swift 元类型、self、Self、AnyObject、Any和AnyClass)