8. Protocols, Blocks, and Animation

Protocols

协议就是编译器的语法,表达式为:

id <MyProtocol> obj

意思是,声明了一个 id 类型的对象,但是加上了协议信息。这样可以告诉编译器和阅读代码的人更多的代码信息。

声明协议

声明协议的方法与 @interface 一样,我们在 @interface 里放的都是所有的公有方法和属性,或者在 @implement 头部来声明私有的 @interface。协议只不过把 @interface 换成了 @protocol

协议只是方法的声明,没有实现部分。所以 @protocol 没有对应的 @implementation,我们说的只是方法。

协议中的方法默认是必须实现的。所以如果有人想实现协议中的某个方法,那么就必须实现全部的这些方法。

这些是示例:

@protocol Foo<Xyzzy>
- (void)someMethod;
@optional
- (void)methodWithArgument:(BOOL)argument;
@property (readonly) int readonlyProperty; // getter (only) is part of this protocol
@property NSString *readwriteProperty;     // getter and setter are both in the protocol
- (int)methodThatReturnsSomething;
@end

你可以在代码中添加 @optional 来把一部分方法变成可选的。现在上面的代码中只有 - (void)someMethod; 是必须实现的了。

@protocol Foo<Xyzzy>
- (void)someMethod;
@optional
- (void)methodWithArgument:(BOOL)argument;
@required
@property (readonly) int readonlyProperty; // getter (only) is part of this protocol
@property NSString *readwriteProperty;     // getter and setter are both in the protocol
- (int)methodThatReturnsSomething;
@end

还可以添加 @required 来标记必须实现的方法,那现在只有 - (void)methodWithArgument:(BOOL)argument; 是可选的了。

Foo 后面的 <Xyzzy> 表示如果你要实现 Foo 的协议那么你同还也必须实现 Xyzzy 协议中全部必须实现的方法。大体上有点像是父协议,但不是继承关系。这里可以添加多个协议,比如 <Xyzzy, NSObject>

NSObject 协议

NSObject 协议基本上就是一个包括了 NSObject 类中全部方法的协议。为什么要同时创建一个叫做 NSObject 的协议和一个叫 NSObjetct 的类呢?两者几乎有着完全相同的方法。

是因为我们在声明协议时,有些方法是必须的,但我们还想让实现这个协议的对象(本质上说就是 NSObject 对象)有一个父协议,所以我们直接将 NSObject 中全部的方法放到一个大协议中,然后让 NSObject 来实现这个协议。

协议的使用

在 iOS 中协议主要用于委托和数据源。

Blocks

Block 就是一段代码,它可以嵌入别的代码中,可以作为参数进行传递,储存在数组中。

Block 能够捕获局部变量但是以 只读 的方式进行。如果想要改变它们的值,则必须在声明变量的时候添加 __block 比如:__block BOOL stop = NO;,那么你就可以在 block 实现中来改变 stop 的值。这步的本质就是将 stop 从栈中移到堆中,这样他就可以在 block 中使用了,使用完后编译器就会将信息复制回堆中,再放回栈上。

只要 block 存在,那么它就会使用强指针指向所使用的变量,直到 block 被释放。

block 不是对象,但它有时表现的像是对象,因为他可以被存储,所以 block 可以像对象一样被存储,但并不像对象那样可以理解消息,实际上只能理解 copy 消息。

何处使用 Block

  • 可以用于枚举,枚举字典中的键和值。
  • 可以用于动画。
  • 可以用于排序,想数组放松消息,让数组进行排序,或者比较两个对象的大小。
  • 可以进行通知,当完成某个操作后可以直接说「执行这个 block」。
  • 用于完成处理程序同样很常见,某个操作需要一段时间才能完成,会在后台线程中完成,当它完成时,执行这个 block。

动画

+ (void)animateWithDuration:(NSTimeInterval)duration
                     delay:(NSTimeInterval)delay
                   options:(UIViewAnimationOptions)options
                animations:(void (^)(void))animations
                completion:(void (^)(BOOL finished))completion;

第一个参数代表这个动画出现在这个屏幕的时间,要记住你所做的改变会立即生效,但会过一段时间才显示出来。delay 代表等待多长时间再开始执行。animations 这个参数很重要,在这个 block 中你可以修改 frame,还可以修改 center,center 和 frame 是关联的,还可以修改 transform 和 alpha。下面是示例:

[UIView annimateWithDuration:3.0
                       delay:0.0
                     options:UIViewAnimationOptionBeginFromCurrentState
                  animations:^{ myView.alpha = 0.0; }
                  completion:^(BOOL fin) {if (ifn) [myView removeFromSuperview];}];

UIViewAnimationOptions

  • BeginFromCurrentState 如果你开启这个选项,如果还有一个正在执行的动画,动画的对象与我想要的是同一个,那么执行我的动画时从他们的当前状态开始。所以如果我要修改 alpha 让他逐渐消失,alpha 变成 0.2 之类的,然后我让另一个动画开始执行,alpha 增大到 0.7,如果开启了这个参数,那么 alpha 会从 0.2 变为 0.7,如果不开启就会从 0 开始。因为 0 才是 alpha 的真实值,然后增大到 0.7.因此 BeginFromCurrentState 可以用来拦截其他动画。之所以这样做是因为设置动画参数后会立即生效。