在本文中,我们将带你了解Swift中开关盒的穷举状态在这篇文章中,同时我们还将给您一些技巧,以帮助您实现更有效的2.6Swift中的元组(Tuples)[Swift原创教程]、4.7Swift中swi
在本文中,我们将带你了解Swift中开关盒的穷举状态在这篇文章中,同时我们还将给您一些技巧,以帮助您实现更有效的2.6 Swift中的元组(Tuples) [Swift原创教程]、4.7 Swift中swift中的switch 语句、6.5 Swift中的闭包(Closure)详解 [Swift原创教程]、Apple / Swift中的Swift函数对象包装器。
本文目录一览:- Swift中开关盒的穷举状态
- 2.6 Swift中的元组(Tuples) [Swift原创教程]
- 4.7 Swift中swift中的switch 语句
- 6.5 Swift中的闭包(Closure)详解 [Swift原创教程]
- Apple / Swift中的Swift函数对象包装器
Swift中开关盒的穷举状态
苹果文档说
每个switch语句必须详尽无遗。也就是说,所考虑类型的每个可能值都必须与切换条件之一匹配。
所以在新的Xcode中,我放置了这样的代码
println(UInt16.min); // Output : ''0''println(UInt16.max); // Output : ''65535''var quantity : UInt16 = 10;switch quantity {case 0...65535: //OR case UInt16.min...UInt16.max: println();default: println();}
现在,如果我删除默认部分,则会出现编译器错误:
切换必须详尽无遗
是否要添加丢失的案例?固定
所以我的问题是我提到的一个案例,因为case0...65535:
我没有提到UInt16
??的所有案例值。但是我仍然遇到错误?为什么会出现此错误,我错过了什么吗?
答案1
小编典典Swift仅switch
在处理enum
类型时才真正验证一个块是否完整。即使打开,除了和之外都Bool
需要一个default
块:true``false
var b = trueswitch b {case true: println("true")case false: println("false")}// error: switch must be exhaustive, consider adding a default clause
有enum
,但是,编译器很乐意只能看两种情况:
enum MyBool { case True case False}var b = MyBool.Trueswitch b {case .True: println("true")case .False: println("false")}
如果default
出于编译器的需要而需要包含一个块,但没有任何要做,则可以使用该break
关键字:
var b = trueswitch b {case true: println("true")case false: println("false")default: break}
2.6 Swift中的元组(Tuples) [Swift原创教程]
原文:http://www.coolketang.com/staticCoding/5a992424a22b9d0044390e21.html
1. 本节课将为你演示元组的使用。元组包含不同的数据类型,并组成一个复合值。元组是使用逗号隔开,并使用括号括起来的0个或多个类型组成的列表。
2. 定义一个新的元组常量,该常量包含一个整型元素和一个字符串元素,所有元素被包含在左右小括号之内,元素之间以逗号分隔。
3. 你还可以将元组赋值给一个带有元素名称的元组,元素名称的个数要和原元组一一对应。
4. 然后就可以在右侧的实时反馈区,查看元组中的各个元素的值了。
5. 查看元组中的第二个元素的值。
6. 使用键值对的方式,创建第二个元组对象,它也拥有两个元素,第一个元素为字符串类型,第二个元素为整型元素。
7. 接着观察第二个元组对象,在右侧的实时反馈区,显示了元组中的各个元素的值。
8. 通过指定的键,查看元组中第一个元素的值。
9. 接着通过指定的键,查看元组中第二个元素的值。
10. 如果你没有给元组的元素命名,那么可以使用点语法,通过定义好的元组变量或常量,获取它的第1个或其它的元素。
4.7 Swift中swift中的switch 语句
/**
switch 语句
*/
let str = "aAbBacdef"
let str2 = "aAbBadef"
let str3 = "aAbBadeff"
// var array = [];
for c in ["A","a",str3]
{
switch c {
// case "a":
case "a","A":
print("ldd")
// 必须有
default:
print("dd")
}
}
/**
case "a":
case "A":
print("ldd")
在 C语言中, 这样写 无论遇到 a A 都会执行 print("ldd");
在 swift中就不允许这样子了,但是可以这样子写
case "a","A": 中间用逗号隔开
*/
// switch value {
// case pattern:
// code
// default:
// }
/**
c 语言中
case 下面有个 break;
如果忘了写break, 会顺序执行下面的语句,直到执行break;
但是swift语言就是看到这一点就,不要break了。比较case里面的条件后,
执行完毕后面的语句就自动退出 该switch语句了。
如果想要继续执行 用fallthrough
*/
6.5 Swift中的闭包(Closure)详解 [Swift原创教程]
原文:http://coolketang.com/staticCoding/5a99261ed50eee2ea303773d.html
1. 本节课将为你演示闭包的使用,闭包功能使编码更加简洁,符合开发语言的现代、简便明了的特点。
2. 首先定义一个函数类型的变量,它拥有两个整型参数,并返回一个整型的结果。等号下方的大括号,是用来实用一个函数功能的代码块。
3. 然后在大括号内,输入两行代码。前一行代码,用来声明函数代码块的参数和返回值信息,然后将对参数的操作,放置在in的下方。
4. 通过该函数类型的变量,即可调用函数的功能:用来获得两个参数的和。
5. 这里创建了一个函数,它同样可以实现和上方代码相同的功能。
6. 调用的方式也是一样的。
7. 在使用闭包来声明一个函数类型的变量时,可以省略对变量类型的定义,系统会自动推断出它的类型。
8. 接着使用相同的代码,实现函数的功能即可。
9. 其实我们还有更加简单的方式,在等号的右侧,通过一个省略了参数声明的闭包语句,给变量赋予了返回两个参数之和的函数功能。
10. 使用该函数变量,计算两个参数的和。
11. 这种函数变量的声明方式,仍然可以进行简化,使用$0和$1表示第一个参数和第二个参数,并且省略了return操作符。
12. 使用该函数变量,计算两个参数的和。
13. 闭包语句随处可见,例如通过系统提供的排序方法sorted,对一个数组进行排序,在此通过小于号,采用升序排列的方式。点击显示结果图标,显示结果列表。
14. 排序方法共进行了4次操作,点击显示结果图标,隐藏结果列表。
15. 继续代码的编写。
16. 输出排序后的数组,从右侧可以看出使用闭包排序后的效果。
17. 闭包语句经常和方法声明配合使用,但是闭包语句通常放在最后一个参数的位置。在此添加一个方法,该方法拥有一个函数类型的参数,最后返回由该函数参数执行后的结果。
18. 然后使用闭包语句,使用第三个参数的函数功能。
19. 这样就可以将其它的函数,作为该函数的参数进行调用。
20. 同样可以采用更加简便的方式,完成相同的功能。在此只使用一个加号,代替求和闭包。观察右侧输出的结果,和上面的结果是一致的。
21. 你甚至可以将加号,修改为乘号。
22. 无论加号、乘号,还是其它运算符号,其实都是对本行语句中的闭包语句的一种省略的写法。点击显示结果图标,查看本行代码执行的结果,并结束对闭包功能的讨论。
23.
Apple / Swift中的Swift函数对象包装器
看完之后:
- https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift
- https://github.com/rodionovd/SWRoute/blob/master/SWRoute/rd_get_func_impl.c
我了解到,Swift函数指针由swift_func_wrapper
和包装swift_func_object
(根据2014年的文章)。
我想这在Swift
3中仍然有效,但是我找不到https://github.com/apple/swift中哪个文件最能描述这些结构。
谁能帮我?
答案1
小编典典我 相信
这些细节主要是Swift的IRGen实现的一部分
-我认为您不会在源代码中找到任何友好的结构来向您展示各种Swift函数值的完整结构。因此,如果您想对此进行深入研究,建议您检查编译器发出的IR。
您可以通过运行以下命令来执行此操作:
xcrun swiftc -emit-ir main.swift | xcrun swift-demangle > main.irgen
它将发出IR(带有去斜线的符号)以进行-Onone构建。您可以在此处找到LLVM
IR的文档。
以下是一些有趣的东西,我可以通过自己在Swift 3.1版本中通过IR来学习。请注意, 所有这些都
可能在以后的Swift版本中进行更改(至少在Swift稳定到ABI之前)。不言而喻,下面给出的代码示例仅用于演示目的。并且永远不应该在实际的生产代码中使用。
粗函数值
从根本上讲,Swift中的函数值很简单-它们在IR中定义为:
%swift.function = type { i8*, %swift.refcounted* }
这是原始函数指针i8*
,以及指向其 上下文 的指针%swift.refcounted*
,其中%swift.refcounted
定义为:
%swift.refcounted = type { %swift.type*, i32, i32 }
这是一个简单的引用计数对象的结构,其中包含指向该对象的元数据的指针以及两个32位值。
这两个32位值用于对象的参考计数。在一起,它们可以表示(从Swift 4开始):
- 对象的强大且无所有权的引用计数+一些标志,包括对象是否使用本机Swift引用计数(与Obj-C引用计数相对)以及对象是否具有边表。
要么
- 指向包含上述内容的边表的指针,再加上对象的弱引用计数(在形成对对象的弱引用时,如果该对象还没有边表,则会创建一个)。
为了进一步了解Swift参考计数的内部知识,Mike
Ash撰写了一篇很棒的博客文章。
函数的上下文通常会在此%swift.refcounted
结构的末尾添加额外的值。这些值是调用函数时需要的动态事物(例如,它已捕获的任何值或已部分应用的任何参数)。在很多情况下,函数值不需要上下文,因此指向上下文的指针将只是nil
。
当调用该函数时,Swift会简单地将上下文作为最后一个参数传递。如果该函数没有上下文参数,则调用约定似乎允许无论如何安全地传递它。
函数指针与上下文指针的存储称为 稠密 函数值,这
是Swift通常存储已知类型的函数值的方式(与之相反, 瘦 函数值只是函数指针)。
因此,这解释了为什么MemoryLayout<(Int) -> Int>.size
返回16个字节的原因-
因为它由两个指针组成(每个指针的长度,即在64位平台上为8个字节)。
当将厚函数值传递给函数参数(这些参数属于非泛型类型)时,Swift似乎会将原始函数指针和上下文作为单独的参数传递。
捕获值
当闭包捕获一个值时,该值将被放入一个堆分配的盒子中(尽管在非转义闭包的情况下,该值本身可以被堆栈提升(请参阅下一节)。该框将通过上下文对象(相关IR)提供给功能。
对于只捕获单个值的闭包,Swift仅使框 本身
成为函数的上下文(无需额外的间接调用)。因此,您将获得一个函数值,该函数值类似于ThickFunction<Box<T>>
以下结构:
// The structure of a %swift.function.struct ThickFunction<Context> { // the raw function pointer var ptr: UnsafeRawPointer // the context of the function value – can be nil to indicate // that the function has no context. var context: UnsafePointer<Context>?}// The structure of a %swift.refcounted.struct RefCounted { // pointer to the metadata of the object var type: UnsafeRawPointer // the reference counting bits. var refCountingA: UInt32 var refCountingB: UInt32}// The structure of a %swift.refcounted, with a value tacked onto the end.// This is what captured values get wrapped in (on the heap).struct Box<T> { var ref: RefCounted var value: T}
实际上,我们可以通过运行以下命令亲自验证一下:
// this wrapper is necessary so that the function doesn''t get put through a reabstraction// thunk when getting typed as a generic type T (such as with .initialize(to:))struct VoidVoidFunction { var f: () -> Void}func makeClosure() -> () -> Void { var i = 5 return { i += 2 }}let f = VoidVoidFunction(f: makeClosure())let ptr = UnsafeMutablePointer<VoidVoidFunction>.allocate(capacity: 1)ptr.initialize(to: f)let ctx = ptr.withMemoryRebound(to: ThickFunction<Box<Int>>.self, capacity: 1) { $0.pointee.context! // force unwrap as we know the function has a context object.}print(ctx.pointee) // Box<Int>(ref:// RefCounted(type: 0x00000001002b86d0, refCountingA: 2, refCountingB: 2),// **value: 5**// )f.f() // call the closure – increment the captured value.print(ctx.pointee)// Box<Int>(ref:// RefCounted(type: 0x00000001002b86d0, refCountingA: 2, refCountingB: 2),// **value: 7**// )ptr.deinitialize()ptr.deallocate(capacity: 1)
我们可以看到,通过在打印出上下文对象的值之间调用该函数,我们可以观察到捕获变量的值的变化i
。
对于多个捕获的值,我们需要额外的间接操作,因为这些框无法直接存储为给定函数的上下文,并且可能被其他闭包捕获。这是通过将指向框的指针添加到末尾来完成的%swift.refcounted
。
例如:
struct TwoCaptureContext<T, U> { // reference counting header var ref: RefCounted // pointers to boxes with captured values... var first: UnsafePointer<Box<T>> var second: UnsafePointer<Box<U>>}func makeClosure() -> () -> Void { var i = 5 var j = "foo" return { i += 2; j += "b" }}let f = VoidVoidFunction(f: makeClosure())let ptr = UnsafeMutablePointer<VoidVoidFunction>.allocate(capacity: 1)ptr.initialize(to: f)let ctx = ptr.withMemoryRebound(to: ThickFunction<TwoCaptureContext<Int, String>>.self, capacity: 1) { $0.pointee.context!.pointee}print(ctx.first.pointee.value, ctx.second.pointee.value) // 5 foof.f() // call the closure – mutate the captured values.print(ctx.first.pointee.value, ctx.second.pointee.value) // 7 foobptr.deinitialize()ptr.deallocate(capacity: 1)
将函数传递给泛型类型的参数
您会注意到,在前面的示例中,我们VoidVoidFunction
对函数值使用了包装器。这是因为否则,在将其传递给泛型类型的参数(例如UnsafeMutablePointer
的initialize(to:)
方法)时,Swift会将函数值通过一些重新提取的thunk进行放置,以便将其调用约定统一为其中的参数和return通过引用传递的,而不是价值(相关IR)。
但是现在我们的函数值有一个指向thunk的指针,而不是我们要调用的实际函数。那么thunk如何知道要调用哪个函数?答案很简单–
Swift将我们想要的函数放到thunk中以在 上下文 本身中调用,因此它将看起来像这样:
// the context object for a reabstraction thunk – contains an actual function to call.struct ReabstractionThunkContext<Context> { // the standard reference counting header var ref: RefCounted // the thick function value for the thunk to call var function: ThickFunction<Context>}
我们经历的第一个重击具有3个参数:
- 指向应该存储返回值的指针
- 指向函数参数位置的指针
- 包含要调用的实际胖函数值的上下文对象(如上所示)
第一个thunk只是从上下文中提取函数值,然后调用带有4个参数的 第二个 thunk:
- 指向应该存储返回值的指针
- 指向函数参数位置的指针
- 要调用的原始函数指针
- 指向要调用的函数的上下文的指针
现在,此重击从参数指针中检索参数(如果有),然后使用这些参数及其上下文调用给定的函数指针。然后,它将返回值(如果有)存储在返回指针的地址处。
像前面的示例一样,我们可以这样测试:
func makeClosure() -> () -> Void { var i = 5 return { i += 2 }}func printSingleCapturedValue<T>(t: T) { let ptr = UnsafeMutablePointer<T>.allocate(capacity: 1) ptr.initialize(to: t) let ctx = ptr.withMemoryRebound(to: ThickFunction<ReabstractionThunkContext<Box<Int>>>.self, capacity: 1) { // get the context from the thunk function value, which we can // then get the actual function value from, and therefore the actual // context object. $0.pointee.context!.pointee.function.context! } // print out captured value in the context object print(ctx.pointee.value) ptr.deinitialize() ptr.deallocate(capacity: 1)}let closure = makeClosure()printSingleCapturedValue(t: closure) // 5closure()printSingleCapturedValue(t: closure) // 7
转义与非转义捕获
当编译器确定捕获给定局部变量不会逃避其声明的函数的生存期时,它可以通过将该变量的值从堆分配的框提升到堆栈来优化(这是有保证的)最佳化,甚至发生在-
Onone中)。然后,函数的上下文对象只需要在堆栈上存储 指向 给定捕获值的指针,因为可以保证在函数退出后不需要该 指针 。
因此,当已知捕获变量的闭包不会逃逸函数的生存期时,可以这样做。
通常,转义的闭包是以下一种情况:
- 存储在非局部变量中(包括从函数返回)。
- 被另一个转义的闭包捕获。
- 作为参数传递给函数,该参数要么标记为
@escaping
,要么不是函数类型的(请注意,这包括复合类型,例如 可选 函数类型)。
因此,以下是一些示例,其中可以认为捕获给定变量不会逃避函数的生存期:
// the parameter is non-escaping, as is of function type and is not marked @escaping.func nonEscaping(_ f: () -> Void) { f()}func bar() -> String { var str = "" // c doesn''t escape the lifetime of bar(). let c = { str += "c called; " } c(); // immediately-evaluated closure obviously doesn''t escape. { str += "immediately-evaluated closure called; " }() // closure passed to non-escaping function parameter, so doesn''t escape. nonEscaping { str += "closure passed to non-escaping parameter called." } return str}
在此示例中,因为str
仅被已知不会逃逸函数生存期的闭包捕获bar()
,所以编译器可以通过str
在堆栈上存储的值进行优化,而上下文对象仅存储指向该指针的指针(相关的IR)。
因此,每个闭包1的上下文对象看起来都像Box<UnsafePointer<String>>
,带有指向堆栈上字符串值的指针。尽管不幸的是,以类似于Schrödinger的方式,尝试通过分配和重新绑定指针来观察到这一点(像以前一样),触发编译器将给定的闭包视为转义–因此,我们再次在Box<String>
上下文中查找a
。
为了处理持有指向捕获值的指针而不是将值保存在自己的堆分配框中的上下文对象之间的差异,Swift创建了闭包的特殊实现,这些闭包将指向捕获值的指针作为参数。
然后,为每个闭包创建一个thunk,它仅接收给定的上下文对象,从中提取指向捕获值的指针,然后将其传递到闭包的专门实现中。现在,我们可以将指向此重击的指针与上下文对象一起作为粗函数值。
对于无法捕获的多个捕获值,只需将附加指针添加到框的末尾即可,即
struct TwoNonEscapingCaptureContext<T, U> { // reference counting header var ref: RefCounted // pointers to captured values (on the stack)... var first: UnsafePointer<T> var second: UnsafePointer<U>}
在这种情况下,将捕获的值从堆提升到栈的这种优化可能 特别 有益,因为我们不再需要像以前那样为每个值分配单独的框。
此外,值得注意的是,在带有内联的-O构建中,可以更积极地优化具有非转义闭包捕获的许多情况,这可能导致上下文对象被完全优化。
1.立即求值的闭包实际上不使用上下文对象,指向捕获值的指针仅在调用时直接传递给它。
关于Swift中开关盒的穷举状态的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于2.6 Swift中的元组(Tuples) [Swift原创教程]、4.7 Swift中swift中的switch 语句、6.5 Swift中的闭包(Closure)详解 [Swift原创教程]、Apple / Swift中的Swift函数对象包装器等相关知识的信息别忘了在本站进行查找喔。
本文标签: