swift基础_关键字

2022-03-10  本文已影响0人  李永开

一.inout

func inoutTest(_ r: Double) {
    print("test")
    r = 100
}
func inoutTest(_ r: inout Double) {
    print("test")
    r = 100
}
// inoutTest(_:)
sil hidden @main.inoutTest(Swift.Double) -> () : $@convention(thin) (Double) -> () {
// %0 "r"                                         // user: %1
bb0(%0 : $Double):
  debug_value %0 : $Double, let, name "r", argno 1 // id: %1
  %2 = tuple ()                                   // user: %3
  return %2 : $()                                 // id: %3
} // end sil function 'main.inoutTest(Swift.Double) -> ()'

加完inout,我们看到$@convention(thin) (Double) -> ()变成了$@convention(thin) (@inout Double)bb0(%0 : $Double):变成了bb0(%0 : $*Double):

// inoutTest(_:)
sil hidden @main.inoutTest(inout Swift.Double) -> () : $@convention(thin) (@inout Double) -> () {
// %0 "r"                                         // user: %1
bb0(%0 : $*Double):
  debug_value_addr %0 : $*Double, var, name "r", argno 1 // id: %1
  %2 = tuple ()                                   // user: %3
  return %2 : $()                                 // id: %3
} // end sil function 'main.inoutTest(inout Swift.Double) -> ()'

二.let & var

let 代表的是Constants,它不是啥单词的缩写官方文档
variable 美 [ˈveriəblˌˈværiəbl] adj. 易变的,多变的;可变的,可调节的;(数)(数字)变量的;

class YKPoint {
    var x = 0.0
}
let p = YKPoint()   //使用let修饰p,打印100
p.x = 100
print(p.x)

class YKPoint {
    var x = 0.0
}
var p = YKPoint()   //使用var修饰p,打印还是100
p.x = 100
print(p.x)

我们不管用let还是var修饰了p,都能正常输出是因为p的属性x是可变的
如果用let修饰了p,那么给p这个指针赋新值就会报错,用var则没有问题

看看struct

struct YKPoint {
    var x = 0.0
}
var p = YKPoint()   //使用var修饰p,打印100
p.x = 100
print(p.x)

struct YKPoint {
    var x = 0.0
}
let p = YKPoint()   //报错  ->Cannot assign to property: 'p' is a 'let' constant
p.x = 100
print(p.x)

我们看下源码,啥也看不出来。。。用let和var修饰,只有这两处不一样,其他的方法什么的都一样,甚至let修饰的也有getter&setter

使用let修饰
@_hasStorage @_hasInitialValue let p: YKPoint { get }
// p
sil_global hidden [let] @main.p : main.YKPoint : $YKPoint


使用var修饰
@_hasStorage @_hasInitialValue var p: YKPoint { get set }
// p
sil_global hidden @main.p : main.YKPoint : $YKPoint

三.mutating

mutate [ˈmjuːteɪt] 变化,产生突变

struct Point {
    var x = 0.0

    func moveBy(_ off: Double) {
        self.x += off
    }
}
图片.png
// Point.moveBy(_:)
sil hidden @main.Point.moveBy(Swift.Double) -> () : $@convention(method) (Double, Point) -> () {
// %0 "off"                                       // user: %2
// %1 "self"                                      // user: %3
bb0(%0 : $Double, %1 : $Point):
  debug_value %0 : $Double, let, name "off", argno 1 // id: %2
  debug_value %1 : $Point, let, name "self", argno 2 // id: %3
  %4 = tuple ()                                   // user: %5
  return %4 : $()                                 // id: %5
} // end sil function 'main.Point.moveBy(Swift.Double) -> ()'
struct Point {
    var x = 0.0

    mutating func moveBy(_ off: Double) {
        self.x += off
    }
}

看下内部实现,
1.$@convention(method) (Double, Point) -> ()变成了$@convention(method) (Double, @inout Point) -> (), Point加了个inout属性
2.self变成var了
3.看来加了mutating后编译器就调皮了的改了些东西

// Point.moveBy(_:)
sil hidden @main.Point.moveBy(Swift.Double) -> () : $@convention(method) (Double, @inout Point) -> () {
// %0 "off"                                       // user: %2
// %1 "self"                                      // user: %3
bb0(%0 : $Double, %1 : $*Point):
  debug_value %0 : $Double, let, name "off", argno 1 // id: %2
  debug_value_addr %1 : $*Point, var, name "self", argno 2 // id: %3
  %4 = tuple ()                                   // user: %5
  return %4 : $()                                 // id: %5
} // end sil function 'main.Point.moveBy(Swift.Double) -> ()'

四.final

final关键字 不能被继承,不能被子类重写

五.@objc

import Foundation

class Dog: NSObject{
    func eat() {
        print("狗吃东西")
    }
}
let dog = Dog()
dog.eat()

编写swift文件时,编译器会自动生成对应的target-Swift.h头文件,这样OC就能调用swift的类了

图片.png

但是如果你不给方法或者属性加@objc属性, 头文件里面是不会看到该方法的。


图片.png

给方法加上@objc再编译一下

import Foundation
class Dog: NSObject{
    @objc func eat() {
        print("狗吃东西")
    }
}
let dog = Dog()
dog.eat()

就能看到swift的方法了

图片.png

我们看下sil,发现除了原有的eat方法,还生成了一个@objc eat方法,而这个@objc eat方法内部调用了swift的eat方法。

// Dog.eat()
sil hidden @main.Dog.eat() -> () : $@convention(method) (@guaranteed Dog) -> () {
// %0 "self"                                      // user: %1
bb0(%0 : $Dog):
  debug_value %0 : $Dog, let, name "self", argno 1 // id: %1
  %2 = tuple ()                                   // user: %3
  return %2 : $()                                 // id: %3
} // end sil function 'main.Dog.eat() -> ()'

// @objc Dog.eat()
sil hidden [thunk] @@objc main.Dog.eat() -> () : $@convention(objc_method) (Dog) -> () {
// %0                                             // users: %4, %3, %1
bb0(%0 : $Dog):
  strong_retain %0 : $Dog                         // id: %1
  // function_ref Dog.eat()
  %2 = function_ref @main.Dog.eat() -> () : $@convention(method) (@guaranteed Dog) -> () // user: %3
  %3 = apply %2(%0) : $@convention(method) (@guaranteed Dog) -> () // user: %5
  strong_release %0 : $Dog                        // id: %4
  return %3 : $()                                 // id: %5
} // end sil function '@objc main.Dog.eat() -> ()'

六.dynamic

import Foundation

class Dog: NSObject{
    dynamic func eat() {
    }
}
let dog = Dog()
dog.eat()

demo: 在分类中替换原来的eat方法

import Foundation

class Dog{
    dynamic func eat() {
        print("eat")
    }
}
extension Dog {
    @_dynamicReplacement(for: eat)
    func dynamicEat() {
        print("dynamicEat")
    }
}

let dog = Dog()
dog.eat()   //dynamicEat

七.@objc + dynamic

将eat方法编译成objc_msgSend方法, 直接走OC底层那一套
0x100003df1 <+49>: callq 0x100003e92 ; symbol stub for: objc_msgSend

八.weak&unowned

这是一个循环引用的例子,因为cls->block->cls循环引用,所以是不会打印LYKClass deinit

class LYKClass {
    var count = 0
    var block: (()->())? = nil
    
    deinit {
        print("LYKClass deinit")
    }
}

let cls = LYKClass()
cls.block = {
    cls.count += 1;
}

我们可以使用weak修饰一下,报错了Value of optional type 'LYKClass?' must be unwrapped to refer to member 'count' of wrapped base type 'LYKClass',说我使用weak后,cls有可能为空,需要cls后加个?

let cls = LYKClass()
cls.block = {[weak cls] in
    cls.count += 1;  正确写法:cls?.count += 1;
}
//加weak会调用 weakinit方法

如果我们换成unowned呢?
不用加?了,可以直接编译通过

let cls = LYKClass()
cls.block = {[unowned cls] in
    cls.count += 1;
}

九. .self&.Type

对象 -> 类 -> 元类

class LYKClass {
    var age: Int = 10
    var name: String = "water"
}

let lykCls = LYKClass()

var s1 = lykCls.self
var s2 = LYKClass.self
var s3 = lykCls.self.self.self

print(s1)   //macSwiftDemo.LYKClass,代表s1是个LYKClass类型
print(s2)   //type metadata for macSwiftDemo.LYKClass,代表s2是个元类
print(s3)   //macSwiftDemo.LYKClass,和s1一样,这是因为
//po lykCls:<LYKClass: 0x10113c200>  po s1:<LYKClass: 0x10113c200>
//lykCls = s1,所以再多的self还是自己

十.indirect

如果枚举内部迭代了枚举,需要用到indirect

indirect enum Suanfa{
    case number(Int)
    case add(Suanfa, Suanfa)
    case sub(Suanfa, Suanfa)
}
func calculate(_ yourSuanfa: Suanfa) -> Int{
    switch yourSuanfa{
    case let .number(i):
        return i
    case let .add(s1, s2):
        return calculate(s1) + calculate(s2)
    case let .sub(s1, s2):
        return calculate(s1) - calculate(s2)
    }
}

let num1 = Suanfa.number(10)
let num2 = Suanfa.number(20)
let add = Suanfa.add(num1, num2)
let sub = Suanfa.sub(add, Suanfa.number(5))
let fianlNum = calculate(sub)
print(fianlNum)//25

本质上是开辟堆空间来存放枚举的值

图片.png

FinalQuestion

// YKPoint.x.getter
sil hidden [transparent] @main.YKPoint.x.getter : Swift.Double : $@convention(method) (YKPoint) -> Double {
// %0 "self"                                      // users: %2, %1
bb0(%0 : $YKPoint):
  debug_value %0 : $YKPoint, let, name "self", argno 1 // id: %1
  %2 = struct_extract %0 : $YKPoint, #YKPoint.x   // user: %3
  return %2 : $Double                             // id: %3
} // end sil function 'main.YKPoint.x.getter : Swift.Double'

// YKPoint.x.setter
上一篇 下一篇

猜你喜欢

热点阅读