Swift傻傻分不清楚系列(九)闭包

本页包含内容:

  • 闭包表达式(Closure Expressions)
  • 尾随闭包(Trailing Closures)
  • 值捕获(Capturing Values)
  • 闭包是引用类型(Closures Are Reference Types)
  • 非逃逸闭包(Nonescaping Closures)
  • 自动闭包(Autoclosures)

闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。

闭包可以捕获和存储其所在上下文中任意常量和变量的引用。这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。

在函数章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:

  • 全局函数是一个有名字但不会捕获任何值的闭包
  • 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
  • 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包

Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:

  • 利用上下文推断参数和返回值类型
  • 隐式返回单表达式闭包,即单表达式闭包可以省略return关键字
  • 参数名称缩写
  • 尾随(Trailing)闭包语法

闭包表达式(Closure Expressions)

嵌套函数是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。

闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了sort(_:)方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。

sort 方法(The Sort Method)

Swift 标准库提供了名为sort的方法,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,sort(_:)方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被sort(_:)方法修改。

下面的闭包表达式示例使用sort(_:)方法对一个String类型的数组进行字母逆序排序.以下是初始数组值:

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

sort(_:)方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false

该例子对一个String类型的数组进行排序,因此排序闭包函数类型需为(String, String) -> Bool

提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为sort(_:)方法的参数传入:

func backwards(s1: String, s2: String) -> Bool {return s1 > s2
}
var reversed = names.sort(backwards)
// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

如果第一个字符串(s1)大于第二个字符串(s2),backwards(_:_:)函数返回true,表示在新的数组中s1应该出现在s2前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母"B"大于字母"A",字符串"Tom"大于字符串"Tim"。该闭包将进行字母逆序排序,"Barry"将会排在"Alex"之前。

然而,这是一个相当冗长的方式,本质上只是写了一个单表达式函数 (a > b)。在下面的例子中,利用闭包表达式语法可以更好地构造一个内联排序闭包。

闭包表达式语法(Closure Expression Syntax)

闭包表达式语法有如下一般形式:

{ (parameters) -> returnType instatements
}

闭包表达式语法可以使用常量、变量和inout类型作为参数,不能提供默认值。也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。

下面的例子展示了之前backwards(_:_:)函数对应的闭包表达式版本的代码:

reversed = names.sort({ (s1: String, s2: String) -> Bool inreturn s1 > s2
})

需要注意的是内联闭包参数和返回值类型声明与backwards(_:_:)函数类型声明相同。在这两种方式中,都写成了(s1: String, s2: String) -> Bool。然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。

闭包的函数体部分由关键字in引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。

由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码:

reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )

该例中sort(_:)方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。

根据上下文推断类型(Inferring Type From Context)

因为排序闭包函数是作为sort(_:)方法的参数传入的,Swift 可以推断其参数和返回值的类型。sort(_:)方法被一个字符串数组调用,因此其参数必须是(String, String) -> Bool类型的函数。这意味着(String, String)Bool类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略:

reversed = names.sort( { s1, s2 in return s1 > s2 } )

实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,都可以推断出闭包的参数和返回值类型。 这意味着闭包作为函数或者方法的参数时,您几乎不需要利用完整格式构造内联闭包。

尽管如此,您仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则可以采用完整格式的闭包。而在sort(_:)方法这个例子里,闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。

单表达式闭包隐式返回(Implicit Return From Single-Expression Clossures)

单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:

reversed = names.sort( { s1, s2 in s1 > s2 } )

在这个例子中,sort(_:)方法的参数类型明确了闭包必须返回一个Bool类型值。因为闭包函数体只包含了一个单一表达式(s1 > s2),该表达式返回Bool类型值,因此这里没有歧义,return关键字可以省略。

参数名称缩写(Shorthand Argument Names)

Swift 自动为内联闭包提供了参数名称缩写功能,您可以直接通过$0$1$2来顺序调用闭包的参数,以此类推。

如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:

reversed = names.sort( { $0 > $1 } )

在这个例子中,$0$1表示闭包中第一个和第二个String类型的参数。

运算符函数(Operator Functions)

实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的String类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。而这正好与sort(_:)方法的参数需要的函数类型相符合。因此,您可以简单地传递一个大于号,Swift 可以自动推断出您想使用大于号的字符串函数实现:

reversed = names.sort(>)

尾随闭包(Trailing Closures)

如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用:

func someFunctionThatTakesAClosure(closure: () -> Void) {// 函数体部分
}// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({// 闭包主体部分
})// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {// 闭包主体部分
}

在闭包表达式语法一节中作为sort(_:)方法参数的字符串排序闭包可以改写为:

reversed = names.sort() { $0 > $1 }

如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉:

reversed = names.sort { $0 > $1 }

当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。举例来说,Swift 的Array类型有一个map(_:)方法,其获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。

当提供给数组的闭包应用于每个数组元素后,map(_:)方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。

下例介绍了如何在map(_:)方法中使用尾随闭包将Int类型数组[16, 58, 510]转换为包含对应String类型的值的数组["OneSix", "FiveEight", "FiveOneZero"]

let digitNames = [0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]

如上代码创建了一个数字位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。

您现在可以通过传递一个尾随闭包给numbersmap(_:)方法来创建对应的字符串版本数组:

let strings = numbers.map {(number) -> String invar number = numbervar output = ""while number > 0 {output = digitNames[number % 10]! + outputnumber /= 10}return output
}
// strings 常量被推断为字符串类型数组,即 [String]
// 其值为 ["OneSix", "FiveEight", "FiveOneZero"]

map(_:)为数组中每一个元素调用了闭包表达式。您不需要指定闭包的输入参数number的类型,因为可以通过要映射的数组类型进行推断。

在该例中,局部变量number的值由闭包中的number参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是常量),闭包表达式指定了返回类型为String,以表明存储映射值的新数组类型为String

闭包表达式在每次被调用的时候创建了一个叫做output的字符串并返回。其使用求余运算符(number % 10)计算最后一位数字并利用digitNames字典获取所映射的字符串。

注意
字典digitNames下标后跟着一个叹号(!),因为字典下标返回一个可选值(optional value),表明该键不存在时会查找失败。在上例中,由于可以确定number % 10总是digitNames字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的String类型的值。

digitNames字典中获取的字符串被添加到output的前部,逆序建立了一个字符串版本的数字。(在表达式number % 10中,如果number16,则返回658返回8510返回0。)

number变量之后除以10。因为其是整数,在计算过程中未除尽部分被忽略。因此16变成了158变成了5510变成了51

整个过程重复进行,直到number /= 100,这时闭包会将字符串output返回,而map(_:)方法则会将字符串添加到所映射的数组中。

在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在map(_:)方法的括号内。

捕获值(Capturing Values)

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

Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。

举个例子,这有一个叫做makeIncrementor的函数,其包含了一个叫做incrementor的嵌套函数。嵌套函数incrementor()从上下文中捕获了两个值,runningTotalamount。捕获这些值之后,makeIncrementorincrementor作为闭包返回。每次调用incrementor时,其会以amount作为增量增加runningTotal的值。

func makeIncrementor(forIncrement amount: Int) -> () -> Int {var runningTotal = 0func incrementor() -> Int {runningTotal += amountreturn runningTotal}return incrementor
}

makeIncrementor返回类型为() -> Int。这意味着其返回的是一个函数,而不是一个简单类型的值。该函数在每次调用时不接受参数,只返回一个Int类型的值。

makeIncrementer(forIncrement:)函数定义了一个初始值为0的整型变量runningTotal,用来存储当前跑步总数。该值通过incrementor返回。

makeIncrementer(forIncrement:)有一个Int类型的参数,其外部参数名为forIncrement,内部参数名为amount,该参数表示每次incrementor被调用时runningTotal将要增加的量。

嵌套函数incrementor用来执行实际的增加操作。该函数简单地使runningTotal增加amount,并将其返回。

如果我们单独看这个函数,会发现看上去不同寻常:

func incrementor() -> Int {runningTotal += amountreturn runningTotal
}

incrementer()函数并没有任何参数,但是在函数体内访问了runningTotalamount变量。这是因为它从外围函数捕获了runningTotalamount变量的引用。捕获引用保证了runningTotalamount变量在调用完makeIncrementer后不会消失,并且保证了在下一次执行incrementer函数时,runningTotal依旧存在。

注意
为了优化,如果一个值是不可变的,Swift 可能会改为捕获并保存一份对值的拷贝。
Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。

下面是一个使用makeIncrementor的例子:

let incrementByTen = makeIncrementor(forIncrement: 10)

该例子定义了一个叫做incrementByTen的常量,该常量指向一个每次调用会将runningTotal变量增加10incrementor函数。调用这个函数多次可以得到以下结果:

incrementByTen()
// 返回的值为10
incrementByTen()
// 返回的值为20
incrementByTen()
// 返回的值为30

如果您创建了另一个incrementor,它会有属于它自己的一个全新、独立的runningTotal变量的引用:

let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven()
// 返回的值为7

再次调用原来的incrementByTen会在原来的变量runningTotal上继续增加值,该变量和incrementBySeven中捕获的变量没有任何联系:

incrementByTen()
// 返回的值为40

注意
如果您将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,您将创建一个在闭包和该实例间的循环强引用。Swift 使用捕获列表来打破这种循环强引用。

闭包是引用类型(Closures Are Reference Types)

上面的例子中,incrementBySevenincrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是引用类型

无论您将函数或闭包赋值给一个常量还是变量,您实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用incrementByTen是一个常量,而并非闭包内容本身。

这也意味着如果您将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:

let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// 返回的值为50

非逃逸闭包(Nonescaping Closures)

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注@noescape,用来指明这个闭包是不允许“逃逸”出这个函数的。将闭包标注@noescape能使编译器知道这个闭包的生命周期(译者注:闭包只能在函数体中被执行,不能脱离函数体执行,所以编译器明确知道运行时的上下文),从而可以进行一些比较激进的优化。

func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {closure()
}

举个例子,sort(_:)方法接受一个用来进行元素比较的闭包作为参数。这个参数被标注了@noescape,因为它确保自己在排序结束之后就没用了。

一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如:

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: () -> Void) {completionHandlers.append(completionHandler)
}

someFunctionWithEscapingClosure(_:)函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你试图将这个参数标注为@noescape,你将会获得一个编译错误。

将闭包标注为@noescape使你能在闭包中隐式地引用self

class SomeClass {var x = 10func doSomething() {someFunctionWithEscapingClosure { self.x = 100 }someFunctionWithNoescapeClosure { x = 200 }}
}let instance = SomeClass()
instance.doSomething()
print(instance.x)
// prints "200"completionHandlers.first?()
print(instance.x)
// prints "100"

自动闭包(Autoclosures)

自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够用一个普通的表达式来代替显式的闭包,从而省略闭包的花括号。

我们经常会调用一个接受闭包作为参数的函数,但是很少实现那样的函数。举个例子来说,assert(condition:message:file:line:)函数接受闭包作为它的condition参数和message参数;它的condition参数仅会在 debug 模式下被求值,它的message参数仅当condition参数为false时被计算求值。

自动闭包让你能够延迟求值,因为代码段不会被执行直到你调用这个闭包。延迟求值对于那些有副作用(Side Effect)和代价昂贵的代码来说是很有益处的,因为你能控制代码什么时候执行。下面的代码展示了闭包如何延时求值。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// prints "5"let customerProvider = { customersInLine.removeAtIndex(0) }
print(customersInLine.count)
// prints "5"print("Now serving \(customerProvider())!")
// prints "Now serving Chris!"
print(customersInLine.count)
// prints "4"

尽管在闭包的代码中,customersInLine的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,customerProvider的类型不是String,而是() -> String,一个没有参数且返回值为String的函数。

将闭包作为参数传递给函数时,你能获得同样的延时求值行为。

// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(customerProvider: () -> String) {print("Now serving \(customerProvider())!")
}
serveCustomer( { customersInLine.removeAtIndex(0) } )
// prints "Now serving Alex!"

serveCustomer(_:)接受一个返回顾客名字的显式的闭包。下面这个版本的serveCustomer(_:)完成了相同的操作,不过它并没有接受一个显式的闭包,而是通过将参数标记为@autoclosure来接收一个自动闭包。现在你可以将该函数当做接受String类型参数的函数来调用。customerProvider参数将自动转化为一个闭包,因为该参数被标记了@autoclosure特性。

// customersInLine is ["Ewa", "Barry", "Daniella"]
func serveCustomer(@autoclosure customerProvider: () -> String) {print("Now serving \(customerProvider())!")
}
serveCustomer(customersInLine.removeAtIndex(0))
// prints "Now serving Ewa!"

注意
过度使用autoclosures会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。

@autoclosure特性暗含了@noescape特性,这个特性在非逃逸闭包一节中有描述。如果你想让这个闭包可以“逃逸”,则应该使用@autoclosure(escaping)特性.

// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) {customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.removeAtIndex(0))
collectCustomerProviders(customersInLine.removeAtIndex(0))print("Collected \(customerProviders.count) closures.")
// prints "Collected 2 closures."
for customerProvider in customerProviders {print("Now serving \(customerProvider())!")
}
// prints "Now serving Barry!"
// prints "Now serving Daniella!"

在上面的代码中,collectCustomerProviders(_:)函数并没有调用传入的customerProvider闭包,而是将闭包追加到了customerProviders数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包将会在函数返回之后被调用。因此,customerProvider参数必须允许“逃逸”出函数作用域。


再次感谢~~~~~~~~~~~~~

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

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

相关文章

Ajax — 新闻列表

注意&#xff1a;本项目主要利用到了template&#xff0c;模板引擎进行编写 模板引擎代码下载地址 <div id"news-list"><!-- 这里放数据 --></div>.news-item {display: flex;border: 1px solid #eee;width: 700px;padding: 10px;margin-bottom: …

vim下更省心地用中文

在vim下使用中文是个麻烦。除了写代码&#xff0c;很多时候也需要做笔记。以下介绍rime输入法的一个功能&#xff0c;它可以减少vim下中文输入带来的麻烦。在***.custom.yaml下添加代码&#xff1a; "key_binder/bindings": - { when: always, accept: ReleaseEs…

Python 常见的内置模块

1. abs() 函数 描述 abs() 函数返回数字的绝对值 #!/usr/bin/pythonprint "abs(-45) : ", abs(-45) print "abs(100.12) : ", abs(100.12) print "abs(119L) : ", abs(119L)以上实例运行后输出结果为&#xff1a;abs(-45) : 45 abs(100.12) : …

Ajax — 第四天

数据交换格式 XML 写法&#xff1a; 一个文档有且只有一个根标签标签必须闭合属性值必须加引号 如果说服务器返回的数据是xml格式的 前端需要把服务器返回的xml当做document对象来处理目前无法演示&#xff0c;自己写接口的时候&#xff0c;我们可以测试一下。 JSON 写法…

检测字符串包含emoji表情

有时候在开发时会遇到不希望字符串中包含emoji表情的情况&#xff0c;Google之后发现了方法&#xff0c;但是似乎iOS9之后的emoji无法过滤&#xff0c;继续寻找方法&#xff0c;在一个NSString的扩展中发现了办法 #import <Foundation/Foundation.h>/**Category to searc…

数据库系统原理(第三章数据库设计 )

一、数据库设计概述 数据库的生命周期 数据库设计的目标&#xff1a; 满足应用功能需求&#xff08;存、取、删、改&#xff09;&#xff0c;良好的数 据库性能&#xff08;数据的高效率存取和空间的节省 共享性、完整性、一致性、安全保密性&#xff09;数据库设计的内容 数据…

Ajax — 第五天

Ajax-05 xhr&#xff08;level-2&#xff09;新特性 responseType属性和response属性 responseType: 表示预期服务器返回的数据的类型 “” &#xff0c;默认空text&#xff0c;和空一样&#xff0c;表示服务器返回的数据是字符串格式json&#xff0c;表示服务器返回的是js…

java 根据身份证号码获取出生日期、性别、年龄

1.情景展示 如何根据身份证号&#xff0c;计算出出生日期、性别、年龄? 2.解决方案 从网上找的别人的&#xff0c;因为并没有实际用到&#xff0c;所以并未对其优化&#xff01; /*** 通过身份证号码获取出生日期、性别、年龄* param certificateNo* return 返回的出生日期格式…

Swift傻傻分不清楚系列(十)枚举

本页内容包含&#xff1a; 枚举语法&#xff08;Enumeration Syntax&#xff09;使用 Switch 语句匹配枚举值&#xff08;Matching Enumeration Values with a Switch Statement&#xff09;关联值&#xff08;Associated Values&#xff09;原始值&#xff08;Raw Values&…

数据库系统原理(第四章:SQL与关系数据库基本操作 )

一、SQL概述 sql是结构化查询语言&#xff08;Structured Query Language&#xff0c;SQL&#xff09;是专门用来与数 据库通信的语言&#xff0c;它可以帮助用户操作关系数据库。 SQL的特点&#xff1a; SQL不是某个特定数据库供应商专有的语言&#xff1b; SQL简单易学 &…

selenium操作浏览器窗口最大化和刷新

实际测试过程中经常遇到打开一个页面并不是全屏显示&#xff0c;但是却希望它能够全屏显示或者新增一条记录后需要刷新一下看能不能再列表中正常显示。 于是就有了今天的关于对浏览器窗口的最大化和刷新页面。需要说明的一点&#xff1a;所有和python相关的记录都是基于3.6版本…

Git安装步骤+Mac终端配置

Git安装步骤 其实可以直接略过。因为安装的时候&#xff0c;一路 next 即可。 注意&#xff0c;安装路径中不能出现中文。安装完成后&#xff0c;不得更改安装路径。 检查Git是否安装成功 在任何文件夹&#xff0c;空白处&#xff0c;右键。如果看到 “Git Bash Here”&#xf…

Swift傻傻分不清楚系列(十一)类和结构体

本页包含内容&#xff1a; 类和结构体对比结构体和枚举是值类型类是引用类型类和结构体的选择字符串(String)、数组(Array)、和字典(Dictionary)类型的赋值与复制行为 类和结构体是人们构建代码所用的一种通用且灵活的构造体。我们可以使用完全相同的语法规则来为类和结构体定义…

数据库系统原理(第5章:数据库编程)

一、存储过程 概念&#xff1a;存储过程是一组为了完成某项特定功能的SQL语句集&#xff0c; 其实质就是一段存储在数据库中的代码。 它可以由声明式的sql语句和过程式sql语句组成。 特点&#xff1a; 可增强SQL语言的功能和灵活性良好的封装性高性能可减少网络流量可作为一种安…

科学-中医:儒医

ylbtech-科学-中医&#xff1a;儒医"儒医"是一种历史悠久的社会文化现象。阐释了"儒医"的三重境界,即良医、大医、圣医。"良医"注重技,属于知识论,追求的是"真";"大医"注重德,属于道德论,追求的是"善";"圣医…

Ajax — 第六天

Ajax-06 GET和POST的区别 字面意思不同 GET 是获取意思。想从服务器获取数据&#xff0c;用GET方式的请求POST是邮递、邮寄意思。如果提交数据到服务器&#xff0c;用POST方式 请求参数位置不同 GET 请求参数会和url拼接到一起&#xff0c;形如 api/getbooks?id2&age3PO…

iOS Tips 模拟器屏幕截图

当我们发布app到AppStore的时候&#xff0c;在itunes connect里面&#xff0c;苹果官方要求我们提供各种尺寸的屏幕截图。由于受到硬件条件的限制&#xff0c;我们不可能在每个真实的物理机器上测试并截图&#xff0c;相反如果我们能直接在模拟器上进行屏幕截图的话&#xff0c…

数据库系统原理(第6章:数据库安全与保护)

一、数据库完整性 数据库完整性是指数据库中数据的正确性和相容性。 完整性约束条件的作用对象 列级约束&#xff1a; 包括对列的类型、取值范围、精度等的约束元组约束&#xff1a; 指元组中各个字段之间的相互约束表级约束&#xff1a; 指若干元组、关系之间的联系的约束定义…

.net core WebApi 使用Swagger生成API文档

关于 Swagger Swagger能成为最受欢迎的REST APIs文档生成工具之一&#xff0c;有以下几个原因&#xff1a; Swagger 可以生成一个具有互动性的API控制台&#xff0c;开发者可以用来快速学习和尝试API。Swagger 可以生成客户端SDK代码用于各种不同的平台上的实现。Swagger 文件可…

Git — 初体验

准备工作 版本管理软件 作用 记录代码的版本实现多人协作 分类 集中式&#xff0c;典型代表 SVN分布式&#xff0c;典型代表 Git 注册远程仓库账号 远程仓库相当于中央服务器我们需要在github上或码云上注册一个账号&#xff0c;用于创建远程仓库使用注册的时候&#xff0…