用 Objective-C 等面向对象语言编程时,“对象”(object)就是“基本构造单元”(building block),开发者可以通过对象来存储并传递数据。在对象之间传递数据并执行任务的过程就叫做“消息传递”(Messaging)。若想编写出高效且易维护的代码,就一定要熟悉这两个特性的工作原理。
当应用程序运行起来以后,为其提供相关支持的代码叫做“Objective-C 运行期环境”(Objective-C runtime),它提供了一些使得对象之间能够传递消息的重要函数,并且包含创建类实例所用的全部逻辑。在理解了运行期环境中各个部分协同工作的原理之后,你的开发水平将会进一步提升。
理解“属性”这一概念
“属性”(property)是 Objective-C 的一项特性,用于封装对象中的数据。Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问,也就是 getter 和 setter 方法。
12345678@interface EOCPerson: NSOjbect {@public ...
Objective-C 语言的起源
Objective-C 与 C++,Java 等面向对象的语言类似,不过在很多地方还是有所差别。Objective-C 使用“消息结构”(messaging structure)而非“函数调用”(function calling)。
消息结构的语言与函数调用的语言关键区别在于:
使用消息结构的语言,其运行时所应执行的代码由运行环境决定,也就是说,在运行时才会检查对象类型。接收一条消息之后,究竟应执行何种代码,由运行期环境而非编译器来决定;
使用函数调用的语言,运行时所执行的代码由编译器决定;
要点:
Objective-C 为 C 语言添加了面向对象特性,是其超集。Objective-C 使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接收一条消息之后,究竟应执行何种代码,由运行期环境而非编译器来决定。
理解 C 语言的核心概念有助于写好 Objective-C 程序。尤其要掌握内存模型与指针。
在类的头文件中尽量少引入其他头文件
在类的头文件中(.h)我们一般不需要知道引入的某个类的全部细节,这时候,我们可以用 @class 关 ...
编程语言
未读我们都知道 Objective-C 是一门动态的语言,有的时候我们会使用 Runtime 处理一些在 Objective-C 上面无法实现或者很难实现的功能。例如:在扩展中添加属性;动态的获取属性的名称,方法名等。那么究竟什么是 Runtime?
什么是 Runtime ?
Runtime 简称运行时。Objective-C 就是运行时机制,也就是在程序运行时候的一些机制,其中最主要的是消息机制。对于我们熟悉的C语言,函数的调用在编译的时候会决定调用哪个函数。但对于 Objective-C 的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
也就有了下面这两点结论:
在编译阶段,Objective-C 可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
在编译阶段,C语言调用未实现的函数就会报错。
Swift 中的 Runtime
好了上面说了这么多都是在说 Objective-C 的 Runtime,那么 Swift 的 Runtime 是什么样的呢?我们来写几行代码看一下。
123456 ...
无并发,不编程。并发在开发中是非常重要的一个技术,运用并发技术,可以写出高性能的程序,并发能够有效地利用多核心 CPU 的优势来提高数据处理的速度。作为一个码农,学好并发是十分有必要的。iOS有四种多线程编程的技术,分别是:NSThread,Cocoa NSOperation,GCD(全称:Grand Central Dispatch), pthread。今天我们就重点讲一讲 GCD 中的并发,锁和线程同步。
GCD 中的并发
GCD 队列默认就是串行的(serial),在 GCD 中创建并发队列是如下所示:
1let concurrent = DispatchQueue(label: "com.demo.concurrentQueue", attributes: .concurrent)
DispatchQueue 的 attributes 参数还有一个取值:initiallyInactive,这是可以手动管理队列执行时间的参数。当一个队列声明为 initiallyInactive 时,这个队列不会自动开始执行,必须要调用 activate() 方法。对应的还有 ...
但是,我仍然不明白,KVC 和属性访问(property accessor)方法有什么区别?
KVC 是调用属性访问器方法或以其他方式访问属性的一种方法。
“以其他方式访问”是什么意思?
对于 KVC 而言,没有访问器方法的实例变量会被视为非正式属性。如果没有找到匹配的访问器对,它将直接获取或设置实例变量的值。(是的,在现代代码中这是不值得使用的,总是为任何你想访问的任何东西声明一个 @property,反之,不要使用 KVC 来访问任何不属于公共属性的东西。)
属性访问器方法是 KVC 在其存在的情况下会调用的方法(KVC 和每个理智的程序员都倾向于直接 ivar 访问)。访问者可以获取或设置实例变量,如合成访问者所做的那样,或者访问某些其他存储。
访问器是实现,属性是接口,KVC 是使用它们的一种方式。
如何区分调用 setValue:forKeyPath 的点和简单访问器?
键路径是一个字符串,而属性访问表达式是一个表达式。编译器检测属性访问表达式并将其转换为一个或多个 Objective-C 消息,而键路径由 KVC 在运行时检测。
所以,当你使用键路径时:
1[some ...
我在使用 Playground 的时候,喜欢将自定运行(Automatically Run)修改为手动运行(Manually Run),因为自动运行有时会导致我的 Xcode 卡住,体验非常不好。但是 Playground 并没有相应的快捷键去手动执行 Playground,这又导致手动运行的时候操作繁琐,每次都要靠鼠标去点,作为一个程序员怎么能忍受这种没有效率的操作 😂。接下来我们为手动执行 Playground 添加一个快捷键。
打开系统偏好设置中的快捷键设置
系统偏好设置 -> 键盘 -> 快捷键
选中最后一个,应用快捷键
设置快捷键
点击加号,添加一个快捷键。应用程序选择 Xcode,菜单标题填写 Execute Playground,快捷键设置为自己想要的快捷键即可。
注意:快捷键不能设置成 command + R 因为这会和 Xcode 本身的运行快捷键冲突。
我想要创建一个 Observable
产生特定的一个元素: just
经过一段延时: timer
从一个序列拉取元素: from
重复的产生某一个元素: repeatElement
存在自定义逻辑: create
每次订阅时产生: deferred
每隔一段时间,发出一个元素: interval
在一段延时后: timer
一个空序列,只有一个完成事件: empty
一个任何事件都没有产生的序列: never
我想要创建一个 Observable 通过组合其他的 Observables
任意一个 Observable 产生了元素,就发出这个元素: merge
让这些 Observables 一个接一个的发出元素,当上一个 Observable 元素发送完毕后,下一个 Observable 才能开始发出元素: concat
组合多个 Observables 的元素
当每一个 Observable 都发出一个新的元素: zip
当任意一个 Observable 发出一个新的元素: combineLatest
我想要转换 Observable的元素后,再将它们发出来
对每 ...
响应式框架
未读just 、 from 和 of
just 和 from 都能够将元素转化为 Observable 但是他们有本质的区别:
just创建 Observable 发出唯一的一个元素。 just 操作符将某一个元素转换为 Observable。
示例:
1234567let oJust = Observable.just([1, 2, 3, 4, 5])oJust.subscribe(onNext: { print($0)}).dispose()// [1, 2, 3, 4, 5]
相当于
12345let oJust = Observable<Array<Int>>.create { (observer) -> Disposable in observer.onNext([1, 2, 3, 4, 5]) observer.onCompleted() return Disposables.create()}
from将其他类型或者数据结构转换为 Observable 。当你在使用 Obse ...
Error Handling - 错误处理
一旦序列里面产出了一个 error 事件,整个序列将被终止。RxSwift 主要有两种错误处理机制:
retry - 重试
catch - 恢复
retryWhen如果请求 JSON 失败,我们需要等待5秒后进行重试操作,重试4次如果全部失败则抛出错误:
1234567891011121314151617// 请求 JSON 失败时,等待 5 秒后重试,// 重试 4 次后仍然失败,就将错误抛出let maxRetryCount = 4 // 最多重试 4 次let retryDelay: Double = 5 // 重试延时 5 秒rxJson .retryWhen { (rxError: Observable<Error>) -> Observable<Int> in return rxError.flatMapWithIndex { (error, index) -> Observable<Int> in gu ...
Observer - 观察者
AnyObserver
AnyObserver 可以用来描叙任意一种观察者。
例如:
打印网络请求结果:
1234567URLSession.shared.rx.data(request: URLRequest(url: url)) .subscribe(onNext: { data in print("Data Task Success with count: \(data.count)") }, onError: { error in print("Data Task Error: \(error)") }) .disposed(by: disposeBag)
可以看作是:
1234567891011121314let observer: AnyObserver<Data> = AnyObserver { (event) in switch event { case .ne ...