Judging by the number of talks, articles and discussions related to reactive programming in Swift, it looks like the community has been taken by the storm. It’s not that the concept of reactiveness itself is a new shiny thing. The idea of using it for the development within the Apple ecosystem had been played with for a long time. Frameworks like ReactiveCocoa have existed for years and did an awesome job at bringing the reactive programming to the Objective-C. However, the new and exciting feat ...
逃逸闭包和非逃逸闭包
逃逸闭包(escaping closure),什么是逃逸闭包?苹果官方给的定义是:当一个闭包作为一个参数传递给函数,但是它是在函数返回之后调用的,这时候,这个闭包就称为逃逸闭包。当你声明一个将闭包作为参数的函数时,你可以在参数的类型之前用 @escaping 来表明这个闭包是允许逃逸的。
闭包可以逃逸的一种方式是存储在函数之外定义的变量中。作为例子,许多启动异步操作的函数都将闭包参数放在异步执行完毕之后的操作中执行(completion handler)。该函数在开始操作后返回,但在操作完成之前不会调用闭包 — 闭包需要逃逸,稍后调用,举个例子:
1234var completionHandlers: [() -> Void] = []func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler)}
someFunctionWithEsc ...
在 Swift 中使用通知,在写通知名称的时候,并不能像 Objective-C 那样简单方便,一个字符串搞定。刚开始使用 Swift 通知时,感觉各种不爽,需要这样写:
1Notification.Name("myNotification")
光取个名字就需要这么长一串代码,真的有悖于 Swift 给我的那种简洁的印象。下面我们就来优化一下。
方法一:简单粗暴的 Notification.Name 扩展
最简单的方式就是对 Notification.Name 进行扩展,代码如下:
123extension Notification.Name { static let myNotification = Notification.Name("myNotification")}
使用的时候就能用点进行调用了:
1NotificationCenter.default.post(name: .myNotification, object: nil)
一瞬间简单了不少。不过还不够,我希望自定义的通知名称与系统的分开,并且 ...
工作日志
未读最近项目中遇到了一个崩溃,当点击图片选择保存的时候,并没有出现请求相册权限的对话框,而是直接就崩溃,并且没有任何错误信息。开始以为是权限没有添加,但是 NSPhotoLibraryUsageDescription 是添加了的,搞不懂了。去翻了翻官方的文档,果然找到了解决方式。
从 iOS 11 开始,相册的权限参数发生了变化,适配 iOS 11 还需要添加一个 NSPhotoLibraryAddUsageDescription 的参数。官方的描述如下:
NSPhotoLibraryAddUsageDescription
NSPhotoLibraryAddUsageDescription ( String - iOS) This key lets you describe the reason your app seeks write-only access to the user’s photo library. When the system prompts the user to allow access, this string is displayed as part of ...
在做 iOS 相关的约束动画时,我们一定会用到 layoutIfNeeded() 函数,但是在大多数情况下使用并没有什么问题。当与 UIScrollView 相关的控件结合使用的时候要注意了。
在开发一个 App 时(类似于浏览器的 App),需求是,滑动的时候,需要隐藏掉顶部的 navigation bar (这里我是用 UIView 自定义的一个 navigation bar)。向上滑动,让 navigation bar 移动到可视区域外消失,当然,移动的时候是有动画的,这里我用的是 constraint 相关的动画,所以我需要在最后调用一次 layoutIfNeeded() 函数。代码类似如下:
1234UIView.animate(withDuration: 0.35) { // 进行更改约束的操作 self.view.layoutIfNeeded()}
这时就出现问题了,滑动的时候,你想让滑动停止,当然是手指放在屏幕上,滑动就会立刻停止,不过在进行布局动画的时候,滑动将会继续,只有当动画完了的时候,视图才会响应你的点击事件。
为了避免上述 ...
编程语言
未读在 Swift 中函数是一等公民。
要理解 Swift 中的函数和闭包,需要先明白三件事情,按重要程度进行大致排序如下:
函数可以像 Int 或者 String 那样被赋值给变量,也可以作为另一个函数的输入参数,或者另一个函数的返回值来使用。
函数能够捕获存在于其局部作用域之外的变量。
有两种方法可以创建函数,一种是使用 func 关键字,另一种是 { }。在 Swift 中,后一种被称为闭包表达式。
函数可以被赋值给变量,也能够作为函数的输入和输出1234// 这个函数接受 Int 值并将其打印func printInt(i: Int) { print("you passed \(i)")}
要将函数赋值给一个变量,比如 funVar,我们只需要将函数名字作为值就可以了。注意在函数名后没有括号:
1let funVar = printInt
现在,我们可以使用 funVar 变量来调用 printInt 函数。注意在函数名后面需要使用括号:
1funVar(2) // 将打印 "you passe ...
栈(C 语言描述)
什么是栈(Stack)?这是计算机内存中的一个特殊区域,它存储由每个函数创建的临时变量(包括 main() 函数)。栈是一个“LIFO”(后进先出)的数据结构。它是被 CPU 管理和优化的。每次函数声明一个新变量时,它都被“压入”栈中。然后每次函数退出时,所有由该函数压入栈的变量都被释放(也就是说,它们被删除)。一旦释放栈变量,该区域的内存就可用于其他栈变量。
使用栈来存储变量的优点是内存是自动为你管理的。你无需手动分配内存,或者在你不再需要时释放内存。更重要的是,由于 CPU 如此高效地组织栈内存,读取和写入栈变量的速度非常快。
理解栈的关键是这样一个概念:当一个函数退出时,它的所有变量都从栈中弹出(因此永远丢失)。因此栈变量本质上是本地的。这与我们之前看到的一个概念(称为变量范围)或局部变量或全局变量有关。 C 编程中的一个常见错误是尝试访问某个函数内栈上创建的变量,该函数在该函数之外的某个地方(即该函数退出后)从该程序中创建。
要记住栈的另一个特性是,可以存储在栈上的变量的大小有一个限制(随OS变化)。对于在堆中分配的变量,情况并非如此。
栈的总结:
随着 ...
结构体和类的主要不同点:
结构体 (和枚举) 是值类型,而类是引用类型。在设计结构体时,我们可以要求编译器保证不可变性。而对于类来说,我们就得自己来确保这件事情。
内存的管理方式有所不同。结构体可以被直接持有及访问,但是类的实例只能通过引用来间接地访问。结构体不会被引用,但是会被复制。也就是说,结构体的持有者是唯一的,但是类却能有很多个持有者。
使用类,我们可以通过继承来共享代码。而结构体 (以及枚举) 是不能被继承的。想要在不同的结构体或者枚举之间共享代码,我们需要使用不同的技术,比如像是组合、泛型以及协议扩展等。
值类型
值语义 (value semantics) * 结构体只有一个持有者。比如,当我们将结构体变量传递给一个函数时,函数将接收到结构体的复制,它也只能改变它自己的这份复制。这叫做值语义 (value semantics),有时候也被叫做复制语义。
引用语义 (reference semantics) * 对于对象来说,它们是通过传递引用来工作的,因此类对象会拥有很多持有者,这被叫做引用语义 (reference semantics)。
因为结构体只 ...
编程语言
未读Swift 中可选值的定义如下
1234enum Optional<Wrapped> { case none case some(wrapped)}
因为 Optional 是枚举类型,所以有的时候可以用模式匹配来进行一些巧妙的操作:
使用 if case 来进行模式匹配,对非 nil 的值做 for 循环
12345let ary = [1, 2, nil, 4, nil, 44]for case let i? in ary { print(i)}// 1, 2, 4, 44
或者只对 nil 值进行循环
123for case nil in ary { print("No Value")}
这里使用了 x? 这个模式,它只会匹配那些非 nil 的值。这个语法是 .Some(x) 的简写形式,所以该循环还可以被写为:
12for case let .some(i) in ary {}
序列(Sequence)
Sequence定义:
1234protocol Sequence { associatedtype Iterator: IteratorProtocol func makeIterator() -> Iterator}
要实现一个Sequence,首先需要提供一个返回迭代器(iterator)的makeIterator() 方法。对于迭代器,它是一个满足 IteratorProtocol 协议的类型。
IteratorProtocol协议的定义:
1234protocol IteratorProtocol { associatedtype Element mutating func next() -> Element?}
IteratorProtocol 协议中唯一的一个方法是 next(),这个方法需要在每次被调用时返回序列中的下一个值。当序列被耗尽时,next() 应该返回 nil
关联类型 Element 指定了迭代器产生的值的类型。
举几个例子进行体会:
1、斐波那契 ...