当前位置:K88软件开发文章中心编程语言Objective-CObjective-C01 → 文章内容

对象之间的通讯

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-10 11:12:24

由 
如此轻盈 
创建,
最后一次修改 
2016-08-12 对象间的通讯对象之间需要通信,
这也是所有软件的基础。
再非凡的软件也需要通过对象通信来完成复杂的目标。
本章将深入讨论一些设计概念,
以及如何依据这些概念来设计出良好的架构。
BlocksBlocks 是 Objective-C 版本的 lambda 或者 closure(闭包)。
使用 block 定义异步接口:- (void)downloadObjectsAtPath:(NSString *)path completion:(void(^)(NSArray *objects, NSError *error))completion;
当你定义一个类似上面的接口的时候,
尽量使用一个单独的 block 作为接口的最后一个参数。
把需要提供的数据和错误信息整合到一个单独 block 中,
比分别提供成功和失败的 block 要好。
以下是你应该这样做的原因:通常这成功处理和失败处理会共享一些代码(比如让一个进度条或者提示消失);Apple 也是这样做的,
与平台一致能够带来一些潜在的好处;block 通常会有多行代码,
如果不是在最后一个参数的话会打破调用点;使用多个 block 作为参数可能会让调用看起来显得很笨拙,
并且增加了复杂性。
看上面的方法,
完成处理的 block 的参数很常见:第一个参数是调用者希望获取的数据,
第二个是错误相关的信息。
这里需要遵循以下两点:若 objects 不为 nil,
则 error 必须为 nil若 objects 为 nil,
则 error 必须不为 nil因为调用者更关心的是实际的数据,
就像这样:- (void)downloadObjectsAtPath:(NSString *)path completion:(void(^)(NSArray *objects, NSError *error))completion { if (objects) { // do something with the data {
else { // some error occurred, '
error'
variable should not be nil by contract {
{
此外,
Apple 提供的一些同步接口在成功状态下向 error 参数(如果非 NULL) 写入了垃圾值,
所以检查 error 的值可能出现问题。
深入 Blocks一些关键点:block 是在栈上创建的 block 可以复制到堆上block 有自己的私有的栈变量(以及指针)的常量复制可变的栈上的变量和指针必须用 __block 关键字声明如果 block 没有在其他地方被保持,
那么它会随着栈生存并且当栈帧(stack frame)返回的时候消失。
当在栈上的时候,
一个 block 对访问的任何内容不会有影响。
如果 block 需要在栈帧返回的时候存在,
它们需要明确地被复制到堆上,
这样,
block 会像其他 Cocoa 对象一样增加引用计数。
当它们被复制的时候,
它会带着它们的捕获作用域一起,
retain 他们所有引用的对象。
如果一个 block指向一个栈变量或者指针,
那么这个block初始化的时候它会有一份声明为 const 的副本,
所以对它们赋值是没用的。
当一个 block 被复制后,
__block 声明的栈变量的引用被复制到了堆里,
复制之后栈上的以及产生的堆上的 block 都会引用这个堆上的变量。
用 LLDB 来展示 block 是这样子的:最重要的事情是 __block 声明的变量和指针在 block 里面是作为显示操作真实值/对象的结构来对待的。
block 在 Objective-C 里面被当作一等公民对待:他们有一个 isa 指针,
一个类也是用 isa 指针来访问 Objective-C 运行时来访问方法和存储数据的。
在非 ARC 环境肯定会把它搞得很糟糕,
并且悬挂指针会导致 Crash。
__block 仅仅对 block 内的变量起作用,
它只是简单地告诉 block:嗨,
这个指针或者原始的类型依赖它们在的栈。
请用一个栈上的新变量来引用它。
我是说,
请对它进行双重解引用,
不要 retain 它。
谢谢,
哥们。
如果在定义之后但是 block 没有被调用前,
对象被释放了,
那么 block 的执行会导致 Crash。
__block 变量不会在 block 中被持有,
最后... 指针、引用、解引用以及引用计数变得一团糟。
self 的循环引用当使用代码块和异步分发的时候,
要注意避免引用循环。
总是使用 weak 引用会导致引用循环。
此外,
把持有 blocks 的属性设置为 nil (比如 self.completionBlock = nil) 是一个好的实践。
它会打破 blocks 捕获的作用域带来的引用循环。
例子:__weak __typeof(self) weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) { [weakSelf doSomethingWithData:data];
{
];
不要这样做:[self executeBlock:^(NSData *data, NSError *error) { [self doSomethingWithData:data];
{
];
多个语句的例子:__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) { __strong __typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) { [strongSelf doSomethingWithData:data];
[strongSelf doSomethingWithData:data];
{
{
];
不要这样做:__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) { [weakSelf doSomethingWithData:data];
[weakSelf doSomethingWithData:data];
{
];
你应该把这两行代码作为 snippet 加到 Xcode 里面并且总是这样使用它们。
__weak __typeof(self)weakSelf = self;
__strong __typeof(weakSelf)strongSelf = weakSelf;
这里我们来讨论下 block 里面的 self 的 __weak 和 __strong 限定词的一些微妙的地方。
简而言之,
我们可以参考 self 在 block 里面的三种不同情况。
直接在 block 里面使用关键词 self在 block 外定义一个 __weak 的 引用到 self,
并且在 block 里面使用这个弱引用在 block 外定义一个 __weak 的 引用到 self,
并在在 block 内部通过这个弱引用定义一个 __strong 的引用。
1. 直接在 block 里面使用关键词 self如果我们直接在 block 里面用 self 关键字,
对象会在 block 的定义时候被 retain,
(实际上 block 是 copied 但是为了简单我们可以忽略这个)。
一个 const 的对 self 的引用在 block 里面有自己的位置并且它会影响对象的引用计数。
如果 block 被其他 class 或者/并且传送过去了,
我们可能想要 retain self 就像其他被 block 使用的对象,
从他们需要被block执行dispatch_block_t completionBlock = ^{ NSLog(@"%@", self);
{
MyViewController *myController = [[MyViewController alloc] init...];
[self presentViewController:myController animated:YES

[1] [2] [3] [4]  下一页


对象之间的通讯