Swift知识点(二)

news2024/11/15 22:56:48

6. 闭包表达式与闭包

闭包表达式(Closure Expression)

闭包表达式是一种在简短行内就能写完闭包的语法

也就是,闭包表达式,只是一种简洁、快速实现闭包的语法

Swift 的闭包表达式拥有简洁的风格,鼓励在常见场景中实现简洁,无累赘的语法
常见的优化包括:

  • 利用上下文推断形式参数和返回值的类型;
  • 单表达式的闭包可以隐式返回;
  • 简写实际参数名;
  • 尾随闭包语法。

函数的定义

  • 可以通过func定义一个函数
func sum(_ v1: Int, _ v2: Int) -> Int
{
	v1 + v2
}

可以理解,就是前面说的,函数也是闭包
闭包就是可以捕获上下文中的常量或变量

  • 也可以通过闭包表达式定义一个函数
    闭包表达式有如下的一般形式:
{ (parameters) -> (return type) in
    statements
}
等价于:
{
	(参数列表) -> 返回值类型 in
	函数体代码
}

闭包表达式语法能够使用常量形式参数、变量形式参数和输入输出形式参数,但不能提供默认值
可变形式参数也能使用,但需要在形式参数列表的最后面使用。
元组也可被用来作为形式参数和返回类型。

闭包的函数整体部分由关键字 in 导入,这个关键字表示:
闭包的形式参数类型和返回类型定义已经完成,并且闭包的函数体即将开始。

通常我们说的闭包更多指的是闭包表达式,也就是没有函数名称的代码块,因此也叫做匿名闭包。

var fn = {
	(v1: Int, v2: Int) -> Int in
	return v1 + v2
}
调用:fn(10, 20)

或者
{
	(v1: Int, v2: Int) -> Int in
	return v1 + v2
}(10, 20)
闭包表达式的简写
函数定义:
func exec(v1: Int, v2: Int, fn:(Int, Int) -> Int){
	print(fn(v1, v2))
}

调用方法一:
exec(v1: 10, v2: 20, fn:{
	(v1: Int, v2: Int) -> Int in
	return v1 + v2
})

由于类型可以被推断出来,因此,类型可以不写。
因为类型可以不写,返回值类型不写,那么->也可以省略
简写为:

调用方法二:

exec(v1: 10, v2: 20, fn:{
	v1, v2 in
	return v1 + v2
})

单个表达式闭包能够通过从它们的声明中删掉 return 关键字来隐式返回它们单个表达式的结果

调用方法三:

exec(v1: 10, v2: 20, fn:{
	v1, v2 in
	v1 + v2
})

在闭包里面,可以使用 $0、$1分别代表闭包表达式里面第1个参数和第2个参数,因此,可以简化为:

调用方法四:

exec(v1: 10, v2: 20, fn:{ $0 + $1 })

其实这次是省略了v1, v2 in。里面没有了参数列表,也就不需要in做区分,因此,前面可以省略

更简化的方法:

exec(v1: 10, v2: 20, fn: + )

简直不是人。。。

其他优化例子
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
reversedNames = names.sorted(by: { $0 > $1 } )

这里, $0 和 $1 分别是闭包的第一个和第二个 String 实际参数。

尾随闭包

如果函数的最后一个参数为闭包,则该闭包也被称为尾随闭包
如果你需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数且闭包表达式很长,使用尾随闭包将增强函数的可读性。
尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式,但它仍然是这个函数的实际参数。

  func someFunctionThatTakesAClosure(closure:() -> Void){
  }
  someFunctionThatTakesAClosure({
  })
  someFunctionThatTakesAClosure() {
  }

还是上面的例子:
可简写为:
reversedNames = names.sorted() { $0 > $1 }
如果闭包表达式作为函数的唯一实际参数传入,而你又使用了尾随闭包的语法,那你就不需要在函数名后边写圆括号了:
reversedNames = names.sorted { $0 > $1 }

真是简写的没法了

闭包(Closure)

定义: 一个函数和它所捕获的变量\常量环境组合起来,称为闭包

函数的定义有两种方式:func和闭包表达式

  • 一个函数,一般指的是:定义在函数内部的函数(嵌套函数)
  • 一般它捕获的是外层函数的局部变量\常量
//定义Fn式一个函数:参数Int,返回值Int
typealias Fn = (Int) -> Int

//定义一个函数:函数名:getFn,参数:空,返回值类型Fn
func getFn() -> Fn{
    //局部变量
    var num = 0
    //内部方法
    func plus(_ i: Int) -> Int {
        num += i
        return num
    }
    //getFn函数的返回值
    return plus
}//返回的plus和num形成了闭包(也就是plus+num就称为闭包)

var fn = getFn()
print(fn(1))
print(fn(2))
print(fn(3))
print(fn(4))
打印结果:
1
3
6
10

按说var fn = getFn()执行完毕后,局部变量num就被释放了。后续fn(1)此时再使用num,应该报错
但,其实是没错的,因为num已经被存储在堆空间了,因此没有被释放

并且,四次调用,访问的都是同一块堆上的内存空间num,因此,num的值被保留下来了(可以连加)

问:为啥num被存储到堆上面呢?

因为,返回出去的函数plus,里面用到了上层函数(getFn)的局部变量,就会分配堆空间,捕获该局部变量

并且,调用一次getFn()函数,就会分配一个单独的堆空间去存储num

是将num的值0,存储在堆空间。栈空间的num=0已经被回收了

var fn1 = getFn()
var fn2 = getFn()
print(fn1(1))
print(fn2(2))
print(fn1(3))
print(fn2(4))
打印:
1
2
4
6

可以把闭包想象成是一个类的实例对象

  • 内存在堆空间
  • 捕获的局部变量\常量就是对象的成员(存储属性)
  • 组成闭包的函数就是类内部定义的方法
//类
class Closure{
    var num = 0
    func plus(_ i: Int) -> Int {
        num += i
        return num
    }
}

var cs1 = Closure()
var cs2 = Closure()
print(cs1.plus(1))
print(cs1.plus(2))
print(cs2.plus(3))
print(cs2.plus(4))
打印:
1
3
3
7

闭包能够捕获和存储定义在其上下文中的任何常量和变量的引用,这也就是所谓的闭合并包裹那些常量和变量,因此被称为“闭包”

全局和内嵌函数,实际上是特殊的闭包。
闭包符合如下三种形式中的一种:

  • 全局函数是一个有名字但不会捕获任何值的闭包;(全局函数是一个闭包)
  • 内嵌函数是一个有名字且能从其上层函数捕获值的闭包;(内嵌函数也是一个闭包)
  • 闭包表达式是一个轻量级语法所写的可以捕获其上下文中常量或变量值的没有名字的闭包

捕获值

一个闭包能够从上下文捕获已被定义的常量和变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍能够在其函数体内引用和修改这些值。

函数和闭包都是引用类型
无论你什么时候赋值一个函数或者闭包给常量或者变量,你实际上都是将常量和变量设置为对函数和闭包的引用

自动闭包

有一个如下的比较函数:如果v1>0,则返回v1,否则返回v2

func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
    return v1 > 0 ? v1 : v2
}

print(getFirstPositive(1, 2))
print(getFirstPositive(-1, 2))
打印:
1
2

当v2传入的是一个函数(参数为空,返回值为Int)的时候,即使v1>0,第二个参数也被调用了一次

func getNumber() -> Int {
    let a = 10
    let b = 11
    print("----")
    return a + b
}
print(getFirstPositive(1, getNumber()))
打印:
----
1

已经判断出v1 > 0了,其实后面的函数没必要执行

为了提高性能,可以将函数getFirstPositive的第二个参数修改为函数:

func getFirstPositive2(_ v1: Int, _ v2: () -> Int) -> Int {
    return v1 > 0 ? v1 : v2()
}

print(getFirstPositive2(1, {
    print("1111")
    return 10
}))
print(getFirstPositive2(-1, {
    print("22222")
    return 10
}))
打印:
1
22222
10

可以看出,当v1>0的时候,不会执行第二个参数里面的函数
调用的时候,可以简写调用:

print(getFirstPositive2(10, {20}))
print(getFirstPositive2(-10){20})
打印:
10
20

看起来不易读

因此,swift提供了 自动闭包

func getFirstPositive3(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int {
    return v1 > 0 ? v1 : v2()
}

print(getFirstPositive3(1, 20))
print(getFirstPositive3(-1, 10))
打印:
1
10

如上所示,调用的时候,第二个参数闭包,传入的是一个Int值
其实,内部是将第二个参数20,修改为了{20}

语法糖

关于@autoclosure的注意点:

  • @autoclosure仅支持 () -> T格式的参数(无参,有返回值)
  • @autoclosure并非只支持最后一个参数
  • 空合并运算符 ?? 使用了@autoclosure技术
  • 有@autoclosure与无@autoclosure,也可以构成函数的重载

逃逸闭包

在 Swift 中,闭包是引用类型,当它被作为一个函数参数传递给一个函数时,这个函数会持有这个闭包的引用。大多数情况下,函数会立即执行闭包并返回,也就是说闭包的生命周期与函数的生命周期一致,我们称这样的闭包为非逃逸闭包。

但有一些情形下,函数在返回之后,闭包仍然被保留而未被执行,例如将闭包储存为函数外部的属性,或者在另外一个异步执行的闭包中被调用,我们称这样的闭包为逃逸闭包。

逃逸闭包必须在闭包参数名前标注 @escaping 关键字。这在使用闭包时会使你更清晰地理解闭包是如何和函数进行交互的。

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    someArray.append(completionHandler)
}

在 this example example中, completionHandler 闭包参数有 @escaping 注解,意味着这个闭包是可逃逸的。因为闭包被加入到 someArray 数组中,并不会立即被执行,而是会在函数返回之后的某个时刻执行。

逃逸闭包在某些场景下非常有用,比如异步调用,延时调用,还有存储为全局变量或类的实例变量等场景。不过需要注意的一点是,由于逃逸闭包的生命周期可能超过函数本身,可能会引起 strong reference cycle 强引用环,不恰当的使用可能导致内存问题。为了解决这个问题,你需要在闭包中显式地使用 [unowned self] 或者 [weak self] 来避免强引用环。

闭包作为一个实际参数传递给一个函数,并且在函数返回后才被调用的时候,我们就说这个闭包从函数中逃逸了
在 Swift 中,我们需要在参数前加上 @escaping 关键字声明闭包是逃逸的。
如常见的异步操作就需要使用逃逸闭包。

swift中闭包与oc中的block有什么区别与相同点?

闭包(Closure)在 Swift 中,与 Objective-C 中的 blocks 形式上有相似处,但在实现、语法以及使用上有很多不同之处。

相似点:

  1. 匿名函数块:都是一种能捕获和存储上下文环境的匿名函数块。可以当作参数在函数之间传递,并且可以在需要的时候调用。

  2. 变量捕获:两者都能在其内部访问和修改外部变量。

区别:

  1. 语法:Swift 的闭包语法更为简洁清晰,通过对参数和返回类型的尾随闭包语法,类型推断等特性,使得闭包的定义及使用变得更为简洁,提高了代码的可读性。

Objective-C 的 block 语法要求声明返回类型,而 Swift 的闭包则不需要声明(但可以声明,这取决于具体用途)。

例如,在 Swift 中,你可以这样声明一个闭包:

let swiftClosure = { (number: Int) -> String in
    return "Swift closure: \(number)"
}

在 Objective-C 中,你需要这样声明一个 block:

NSString *(^objcBlock)(NSInteger) = ^(NSInteger number) {
    return [NSString stringWithFormat:@"Objective-C block: %ld", number];
};
  1. 内存管理:Objective-C 的 block 在捕获外部变量时需要注意 __block 、 __weak 标识符的使用,否则很容易造成循环引用。而 Swift 的闭包提供了 [unowned self] 和 [weak self] 语法来避免循环引用。

  2. 可访问性:Swift 的闭包能访问其作用域内的所有常量和变量,这包括其他函数中的参数和变量,这点在 Objective-C 的 block 中不完全支持。

  3. 可修改可逃逸性:在 Swift 中,闭包默认为不可逃逸,必要时可以使用 @escaping 关键字修改。Objective-C 的 block 默认为可逃逸,而且不能修改。

  4. 运行时处理:Objective-C 的 blocks 在运行时声明,Swift 的闭包在编译时声明。

总结来说,虽然 Swift 中的闭包和 Objective-C 中的 blocks 在面向函数编程的功能上有诸多相似之处,但在语法清晰度、内存管理、作用域访问性、可逃逸性等方面,Swift 的闭包具有较大优势。这些优点使得闭包适应更多的编程场景,提高了代码的可读性和可维护性。


8. 属性

Swift中,跟实例相关的属性可以分为两大类:存储属性、计算属性

存储属性(Stored Property)

类似于成员变量
存储在实例的内存中
结构体、类可以定义存储属性
枚举不可以定义存储属性

在创建结构体或类的实例时,必须为所有的存储属性设置一个合适的初值
可以在初始化器里设置,也可以为属性分配一个默认值

struct Circle{
	//存储属性
	var radius: Double
	//计算属性
	var diameter: Double{
		set{
			radius = newValue / 2
		}
		get{
			radius * 2
		}
	}
}


var circle = Circle(radius: 5)
print(circle.radius)//5
print(circle.diameter)//10
//只需要设置radius的值,就可以通过计算属性中get方法确定diameter的值


circle.diameter = 12
print(circle.radius)//6
print(circle.diameter)//12
//通过修改计算属性diameter的set方法,修改了radius的方法
对存储属性的懒加载

lazy只能用于var变量,不能用于let常量

lazy的两种用法:
方法一:
lazy var str: String = "Hello"

方法二:使用闭包

lazy var names: NSArray = {
    let names = NSArray()
    print("只在首次访问输出")
    return names
}()

疑问:为何使用闭包可以做到懒加载?

计算属性(Computed Property)

计算属性不是直接存储值,而是提供get和set方法
get:用来取值,封装取值的过程
set:用来设值,封装设值得过程

计算属性是用来间接修改其他属性值的

本质就是方法(函数)
不占用实例的内存
结构体、枚举、类都可以定义计算属性

上例中,set方法传入的新值默认叫做newValue
也可以自定义:

set(newName){
	radius = newName / 2
}

只读计算属性:只有get,没有set

struct Circle{
	//存储属性
	var radius: Double
	//计算属性
	var diameter: Double{
		get{
			radius * 2
		}
	}
}

可以简写:

struct Circle{
	//存储属性
	var radius: Double
	//计算属性
	var diameter: Double{
		radius * 2
	}
}

定义计算属性只能使用var,不能使用let
原因是,计算属性的值可能会发生变化(即使是只读计算属性)

属性监听器

计算属性本身就有监听,不需要考虑为其添加监听的事
对于存储属性,可以通过willSet和didSet来监听属性的改变
举个例子:
在这里插入图片描述
需要注意的是: willSet和didSet在初始化过程中是不会被调用的
也就是,在上面例子中,第11行初始化的时候不会调用willSet和didSet


9. 方法(Method)

定义在类、结构体、枚举内部的函数,被称为方法
方法又分为:实例方法和类型方法

  • 实例方法(Instance Methods):通过实例调用
  • 类型方法(Class Methods):通过类型调用。用staticclass关键字定义

在类型方法中调用self,self就代表类
在实例方法中调用self,self就代表实例

下标(subscrip)

使用subscrip可以给任意类型(枚举、结构体、类)增加下标功能
subscrip的语法类似实例方法、计算属性,其本质就是方法(函数)
举一个下标的例子:

class Point {
    var x = 0.0, y = 0.0
    //类似方法定义,只是没有func关键字,没有方法名
    subscript(index: Int) -> Double {
    	//类似计算属性
        set {
            if(index == 0){
                x = newValue
            }else if(index == 1){
                y = newValue
            }
        }
        
        get {
            if index == 0{
                return x
            }else if(index == 1){
                return y
            }
            return 0
        }
    }
}

var p = Point()
p[0] = 11.1
p[1] = 22.2
print(p.x)
print(p.y)
print(p[0])
print(p[1])
打印:
11.1
22.2
11.1
22.2

使用subscrip后,就可以直接对对象像数组一样操作,取p[0]、p[1]了

subscrip定义的返回值类型,决定了get方法的返回值类型,也决定了newValue的值
首先,返回值类型与get出去的类型,肯定是保持一致的。

subscrip可以没有set方法,但是必须有get方法

取值嘛

subscrip如果只有get方法,则get可以省略(语法糖)

上述下标是对象方法
下标也可以写成类方法:

class Sum {
    static subscript(v1: Int, v2: Int) -> Int{
        return v1 + v2
    }
}
//下标访问,使用[],传两个参数,用,隔开即可
print(Sum[10, 20])//30

在 Swift 中,静态下标是一种语法糖,它让我们可以用中括号 [] 访问或设置类型的特定值,而不需要调用一个方法或访问一个属性。

继承(Inheritance)

  • 值类型不支持继承,只有类支持继承

  • 在oc中,NSObject、NSProxy是基类,其余都继承NSObject

  • 在swift中,只要是不继承其他类的,都称为基类

  • swift是单继承

有继承就有重写:

重写

子类可以重写父类的:下标、方法、属性,重写必须调用override
方法重写很正常,OC中常用的
下标的本质就是方法,因此也可以重写
属性,有计算属性,本质也是方法,因此,也可以重写

子类重写父类的方法,当使用子类调用该方法的时候,调用子类的方法
如果在执行子类方法的同时,还想让父类方法也执行,那么,需要调用super.methd()

实例方法、下标重写
class Animal {
    func speak(){
        print("Animal speak")
    }
    
    subscript(index: Int) -> Int {
        return index
    }
}

class Cat: Animal {
    override func speak() {
        super.speak()
        print("Cat speak")
    }
    //加override
    override subscript(index: Int) -> Int {
    	//调用父类的下标调用方法super[index]
        return super[index] + 1
    }
}

//多态:父类类型指向子类类型
//animal虽然是父,但其实子类Cat里面东西多(继承animal的东西+自己的东西)
let animal = Cat()
animal.speak()
print(animal[6])
打印:
Animal speak
Cat speak
7
类方法、下标重写
  • class修饰的类方法、下标,可以重写
  • static修饰的类方法、下标,不允许重写

这也是被class修饰的类方法、下标与被static修饰的类方法、下标的区别

属性重写
  • 子类可以将父类的属性(不管是存储属性,还是计算属性),重写为计算属性
  • 子类不可以将父类重写为存储属性
  • 只能重写var属性,不能重写let属性
  • 重写时,属性名、类型要一致
  • 子类重写后的属性权限 不能小于 父类属性的权限

父类只读 --> 重写后的子类 可以为读写属性
父类读写 --> 重写后的子类 必须为读写属性
不可以将一个继承来的读写属性重写为一个只读属性

存储属性、计算属性都可以重写
属性重写提供了:get、set、willSet、didSet四个关键字对属性进行重写

一个重写计算属性的例子:

import Foundation

class Car {
    var speed:Int {//计算属性
        set {
            print("car-set")
        }
        get {
            return 10
        }
    }
}

class Tank: Car {
    override var speed:Int {//计算属性
        set {
            print("tank-set")
        }
        get {
            return 1
        }
    }
}

let tank = Tank()
print(tank.speed)

结果:1

Tank继承Car,本来car的speed有一个初值10,现在tank的初值变为了1


看完计算属性的重写,我们再看下存储属性的重写:
在这里插入图片描述报错,那么加个override是否就可以了呢?

在这里插入图片描述依然报错
那么,究竟怎么重写父类的存储属性呢???

一个重写存储属性的例子:

import Foundation

class Car {
    var speed:Int = 10
}

class Tank: Car {
    override var speed:Int {
        get {
            return super.speed
        }
        set {
            print("tank - set")
        }
    }
}

let tank = Tank()
print(tank.speed)

结果: 10

从上面可以看出,重写属性,不管是计算属性还是存储属性,都需要借助get、set
也可以得出:存储属性重写为计算属性

例子2:
在这里插入图片描述

以上都是重新对象属性
对于类型属性:

  • class修饰的计算类型属性可以被子类重写

class不允许修饰存储属性,所以,上面只能是计算类型属性

  • static修饰的类型属性(存储、计算),不可以被子类重写
属性观察器

可以在子类中,为父类属性(除了只读计算属性、let属性)增加属性观察器(也就是willSet、didSet)

那么willSet、didSet如何使用呢?

在这里插入图片描述

final

如果方法、属性、下标被final修饰,则不允许被子类重写
如果类被final修饰,则不允许被继承


10. 多态原理、初始化、可选链

多态

let animal = Animal()
//父类指针,指向子类对象,这种称为多态
//animal虽然是父,但其实子类Cat里面东西多(继承animal的东西+自己的东西)
animal = Cat()

struck与class创建的值,调用方法时的区别

从汇编打断点来看:
struct建立的值,调用对应方法的时候,在编译期已经决定调用哪一个方法,是直接调用,调用性能快
class建立的值,调用对应方法的时候,在运行期才决定要调用哪一个方法,是动态调用,调用性能相比struct要弱一点

class对象(堆空间)里面存储的内容:

前8个字节存放:类型信息(类似OC中的isa),其本身是一个指针,指向的是另外一处堆空间,再这个堆空间里面,存有很多类型相关的信息:比如,要调用的方法地址

再8个字节存放:引用计数(retainCount)

剩余的存放:属性信息

遵循8的倍数原则

在这里插入图片描述

var dog1 = Dog()
var dog2 = Dog()

dog1与dog2的前8个字节,内存地址是一样的,也就是类型信息是一样的(只有一份)

从函数调用上,引申出一个问题:

Swift的派发机制

Swift 具有丰富的方法派发机制,这涉及到如何在运行时确定调用哪个方法。主要有以下几种机制:

  1. 直接派发(Direct Dispatch):此类方法调用发生在编译阶段,编译器会直接在调用处"内联"这个函数的实现。这种派发方式最快最高效,因为它避免了函数调用的开销。常见于结构体和枚举的方法派发。

  2. 虚表派发(Table Dispatch,常见于 v-table):这是面向对象语言中最常见的派发方式,Swift 的类方法默认使用这种方式。在这种机制中,如果有一个类 Animal,我们创建了一个子类 Dog 并重写了某个方法,例如 bark() ,在运行时如果子类调用 bark() ,此时会通过虚表找到子类 Dog 中 bark() 方法的地址去调用。

  3. 消息派发(Message Dispatch):在 Swift 中,这主要发生在和 Objective-C 交互的部分,例如使用了 dynamic 关键字或者 @objc 注解的方法。这种机制的弹性最大,但也是最慢的。例如:

@objc dynamic func myMethod() {
    print("This method uses Objective-C message dispatch.")
}

根据这些派发机制,有几个重要的注意点:

  • 尽可能使用直接派发。这是最快的,并且可以让编译器优化你的代码。只有当你需要使用类的多态特性时,才需要其他的派发机制。

  • 虚表派发能够提供面向对象编程的多态性,但是也增加了一定的开销。如果类或方法是 final 的,那么它们就可以使用直接派发,因为这些类和方法不能被重写。

  • 消息派发是最灵活的,但也是最慢的。一般来说,应避免使用,除非你需要和 Objective-C 的运行时进行交互,或使用一些动态特性。

举个具体的例子,让以上的派发机制更加形象化:

struct MyStruct {
    func method() {}  // 直接派发,因为是值类型
}

class MyClass {
    func method() {}  // 默认虚表派发,因为是类
}

final class MyFinalClass {
    func method() {}  // 直接派发,因为类是final
}

class MyDynamicClass {
    @objc dynamic func method() {}  // 消息派发,因为 @objc dynamic
}

初始化器

类、结构体、枚举都可以定义初始化器
有两种初始化器:

  • 指定初始化器(designated initializer)
  • 便捷初始化器(convenience initializer)

其他主要知识点:

  • 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器
  • 默认初始化器:类的指定初始化器
  • 类偏向于少量指定初始化器,一个类通常只有一个指定初始化器
  • 默认初始化器,指定初始化器。当自定义了带参数的指定初始化器后,编译器生成的不带参的初始化器就没了
  • 便捷初始化器,要最终调用指定初始化器。自定义便捷初始化器后,默认初始化器还在

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1549038.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VLAN的原理及配置

文章目录 一、VLAN的概述1、VLAN的概念2、VLAN的优势 二、静态VLAN三、静态VLAN的配置1.VLAN的范围2.VLAN基本配置 四、Trunk和access的作用参考 一、VLAN的概述 1、VLAN的概念 VLAN就是将网络从逻辑上划分为若按个小的网络,也就是虚拟局域网。 2、VLAN的优势 使…

KingSCADA|如何实现文本显示设备的实时通讯状态?

哈喽,你好啊,我是雷工! 在SCADA项目中,有些要求在界面上实时显示SCADA系统与设备的实时通讯状态,来及时了解PLC或其他设备与SCADA系统的通讯状态是否正常,以及简单的通讯异常分析,在KingSCADA中该如何实现通讯状态的文本显示呢? 接下来用简单的样例介绍KingSCADA如何实…

Vue3尚硅谷张天禹笔记

1. Vue3简介 2020年9月18日,Vue.js发布版3.0版本,代号:One Piece(n 经历了:4800次提交、40个RFC、600次PR、300贡献者 官方发版地址:Release v3.0.0 One Piece vuejs/core 截止2023年10月,最…

整数的反转

给定一个整数,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零。 public class _01数字反转 {public static void main(String[] args) {Scanner input n…

二叉树|701.二叉搜索树中的插入操作

力扣题目链接 class Solution { public:TreeNode* insertIntoBST(TreeNode* root, int val) {if (root NULL) {TreeNode* node new TreeNode(val);return node;}if (root->val > val) root->left insertIntoBST(root->left, val);if (root->val < val) r…

【HTWATER】海绵城市关键控制指标一键分析,一键获得整体、单个汇水子面积的年径流总量控制率及污染物削减率的结果

慧天[HTWATER]软件简介 针对城市排水系统基础设施数据管理的需求&#xff0c;以及水文、水力及水质模拟对数据的需求&#xff0c;实现了以数据库方式对相应数据的存储。可以对分流制排水系统及合流制排水系统进行地表水文、管网水力、水质过程的模拟计算。可以对城市低影响开发…

《区块链技术:解锁数字时代的无限可能》

区块链技术作为近年来备受瞩目的创新技术&#xff0c;正以崭新的姿态改变着我们的世界。从技术进展到行业应用&#xff0c;再到面临的挑战与机遇&#xff0c;以及未来的发展趋势&#xff0c;本文将全面剖析区块链技术的发展现状和潜力。 区块链技术原理 区块链技术原理是指构成…

大数据入门(一)

大数据主要要解决&#xff1a;海量数据的采集&#xff0c;存储&#xff0c;分析计算问题。 大数据的特点&#xff1a;大量&#xff08;数据量大&#xff09;&#xff0c;高速&#xff08;数据量的累积越来越快&#xff09;&#xff0c;多样&#xff08;结构化数据和非结构化数…

HTML静态网页成品作业(HTML+CSS+JS)——中华美食八大菜系介绍(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;使用Javacsript代码实现图片轮播切换&#xff0c;共有1个页面。 二、…

逐步学习Go-并发通道chan(channel)

概述 Go的Routines并发模型是基于CSP&#xff0c;如果你看过七周七并发&#xff0c;那么你应该了解。 什么是CSP&#xff1f; "Communicating Sequential Processes"&#xff08;CSP&#xff09;这个词组的含义来自其英文直译以及在计算机科学中的使用环境。 CSP…

PTA L2-037 包装机

一种自动包装机的结构如图 1 所示。首先机器中有 N 条轨道&#xff0c;放置了一些物品。轨道下面有一个筐。当某条轨道的按钮被按下时&#xff0c;活塞向左推动&#xff0c;将轨道尽头的一件物品推落筐中。当 0 号按钮被按下时&#xff0c;机械手将抓取筐顶部的一件物品&#x…

详解智慧路灯杆网关的集中供电能力

智慧路灯杆网关是智慧杆物联网系统中不可或缺的设备。智慧杆网关不仅可以作为杆载设备与云平台、设备与设备之间的桥梁&#xff0c;促进数据的无缝传输&#xff0c;而且还能提供高效的能源管理和供电功能。 BMG8200系列交流型智慧路灯杆网关就集成了强大的供电能力&#xff0c;…

短视频矩阵系统--技术3年源头迭代

短视频矩阵系统核心技术算法主要包括以下几个方面&#xff1a; 1. 视频剪辑&#xff1a;通过剪辑工具或API从各大短视频平台抓取符合要求的视频。这些视频通常符合某些特定条件&#xff0c;如特定关键词、特定时间段发布的视频、视频点赞评论转发等数据表现良好的视频。 2. 视…

Kotlin 中的类和构造方法

1 Kotlin 中的类以及接口 对象是什么&#xff1f;任何可以描述的事物都可以看作对象。我们以鸟为例&#xff0c;来分析它的组成&#xff1a; 形状、颜色等可以看作是鸟的静态属性&#xff1b;年龄、大小等可以看作是鸟的动态属性&#xff1b;飞行、进食等可以看作是鸟的行为&…

VTK 光源 Transform 自定义BoundingBox绘制

这段代码展示了如何在 VTK 中创建光源&#xff0c;并在场景中添加光源的可视化表示。以下是关于代码的详细解释和教程&#xff1a; 创建光源 vtkSmartPointer<vtkLight> light vtkSmartPointer<vtkLight>::New();使用 vtkSmartPointer 创建了一个智能指针&#…

oracle补丁升级(19.3-19.22)

一、备份原来的opatch和数据库文件 这里要根据自己的路径&#xff1a; mv /u01/app/oracle/product/19.3.0/db_1/OPatch /u01/app/oracle/product/19.3.0/db_1/OPatch.bakcd /u01/app mkdir -p /u01/app/backup tar -pcvf /u01/app/backup/oracle_backup.tar /u01/app/oracle/…

glibc内存管理ptmalloc - 实时打印bin链的变化

前言 在《glibc内存管理ptmalloc - largebin》中我们详细解释了 largebins共63个&#xff0c;并用表格点出了每个bin的size的范围largebin在free一些内存后的状态 特别是第2点&#xff0c;我其实不太满意&#xff0c;因为只有全部free后的一个结果&#xff0c;并没有中间状态…

【jenkins+cmake+svn管理c++项目】jenkins回传文件到svn(windows)

书接上文&#xff1a;创建一个项目 在经过cmakemsbuild顺利生成动态库之后&#xff0c;考虑到我一个项目可能会生成多个动态库&#xff0c;它们分散在build内的不同文件夹&#xff0c;我希望能将它们收拢到一个文件夹下&#xff0c;并将其回传到svn。 一、动态库移位—cmake实…

工作12年了,我还没能过上自己想要的生活

写这篇文章之前&#xff0c;我想了很久&#xff0c;不知道该如何下笔&#xff0c;如何向读者说明这些年我是怎么走过来的&#xff0c;我只是依稀的记得当时的自己犹如在昨天。 2009年大学毕业&#xff0c;我和大多数的毕业生一样写简历求职。不管是招聘会还是网上投简历&#x…

容器网络隔离验证

结论&#xff0c;可以直接扫描内网路由能通的机器。 1.节点1 192.168.55.6 2.节点2 192.168.55.5 3.非节点3 192.168.55.3