Index

Using Swift with Cocoa and Objective C 总结

本文是阅读Apple官方文档Using Swift with Cocoa and Objective-C (Swift 2.2)的总结,方便以后翻阅

类方法和便捷初始化方法

OC的类方法转到swift中为convenience initializers

可失败的初始化

OC中失败的初始化多返回 nil,可失败的初始化方法 可以用nullable标记。OC中可失败和不可失败的初始化方法转到swift表示为init(...) init?(...) 否则 为init!(...)

存取属性

OC中的@property声明的属性转到swift中,规则如下:
  • 有为空性属性的属性(nonnull, nullable, and null_resettable)转到swift对应于 optional 和 non-optional类型
  • OC的只读属性 在swift中表示为 计算属性 并只有一个getter({ get }
  • OC的weak属性 在swift中用 weak关键字表示 (weak var
  • OC中的assign, copy, strong, or unsafe_unretained 在swift中表示为一些 适当的存储
  • OC中属性的原子性(atomic and nonatomic) 在swift中没有对应的属性声明的映射,但是从swift中调用OC中的属性时,其原子性的实现仍然有效
  • OC中的属性getter= and setter= 在swift中忽略

id的兼容性

swift中的AnyObject对应OC中的id,可以在AnyObject类型的对象上 调用任意OC的方法和属性 并且不需要类型转换,包括用@objc属性标记的OC兼容的属性和方法

Unrecognized Selectors and Optional Chaining

因为AnyObject类型的对象的 值 到runtime时才回确定,所以很可能导致不安全的代码。跟OC一样 可能会抛出Unrecognized Selector
swift使用optionals保证代码的安全,可以像使用protocol中的可选方法一样,使用optional chaining语法 在AnyObject类型的对象上调用方法
note:在AnyObject类型的对象上存取一个 属性 总是返回一个optional value。如果这个属性 正常情况下 返回optional的值,那么返回值会是双层包装的optional类型,如AnyObject?!
虽然swift不强制解包装 AnyObject对象调用方法的返回值,但是为了安全编码,推荐使用解包装
// myObject has AnyObject type and NSDate value
let myCount = myObject.count
// myCount has Int? type and nil value
let myChar = myObject.characterAtIndex?(5)
// myChar has unichar? type and nil value
if let fifthCharacter = myObject.characterAtIndex?(5) {
    print("Found \(fifthCharacter) at index 5")
}
// conditional branch not executed

向下转换 AnyObject

AnyObject类型的对象的底层数据类型已知或者可推断时,推荐向下转换到一个确定类型。因为AnyObject可以是任意类型,向下转换到确定类型不保证一定成功。
条件类型转换操作符as? 返回一个optional value的目标类型。
若确定是某一种类型,可使用强制类型转换符as!。注意 一旦强转失败 在runtime时会出错

Nullability and Optionals

OC中为初始化的指针为NULL(nil),在swift中 所有类型的值都是non-null的,并用optional类型去封装值缺失,用nil表示
OC中的Nullability可以用(_Nullable _Nonnull nullable nonnull null_resettable)标记,或者用NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END宏包裹某个区域。如果OC代码没有这些标记,转swift时 不能区分到底是optional还是non-optional引用 所以只能标记为隐式解封装的optional(implicitly unwrapped optional)
  • _Nonnull标记 转为non-optional
  • _Nullable标记 转为optional
  • 没有Nullability标记声明的 转为 implicitly unwrapped optional

轻量级泛型

OC中的Foundation框架的集合类的泛型才能转到swift中,其他类型的泛型不能转到swift 及没有泛型
@property NSArray<NSDate *> *dates;
@property NSSet<NSString *> *words;
@property NSDictionary<NSURL *, NSData *> *cachedData;

// swift
var dates: [NSDate]
var words: Set<String>
var cachedData: [NSURL: NSData]

extensions

swift的extensions相当于OC的category。extensions可以扩展已存在类 结构体 枚举类型 属性(属性扩展 必须为 计算属性),还可以扩展在OC中定义的这些类型。
extensions不能向类 结构体 枚举中添加存储属性,只能是 计算属性
extensions不需子类化 就可以使一个类遵守某个protocol,如果这个protocol是定义在swift中的,那么还可以使swift和OC中定义的结构体和枚举实现该protocol。
extensions还可以重写OC类型中 已存在的方法和属性

Closures

OC中的block等价与swift中的closures。混编时 block会自动转为swift中的closures 并标记为@convention(block)属性。swift中closures长这个样子:
let completionBlock: (NSData, NSError) -> Void = { data, error in
    // ...
}
closuresblock兼容,可以将swift中的closures传给OC中的block,如果一个closures和一个swift函数有相同的参数类型,神之可以把函数名传给OC中的blockclosuresblock最大的区别是 后者捕获到的变量是copy, 而前者是mutable,简言之 closures的默认行为相当于 block中的捕获到用__block修饰的变量

避免循环引用

OC中循环引用出现的条件:self strong/copy block,并且这个存在于上的block内部出现了self,使用的标准姿势如下(这可是apple的demo奥):
__weak typeof(self) weakSelf = self;
self.block = ^{
   __strong typeof(self) strongSelf = weakSelf;
   [strongSelf doSomething];
};
swift中处理closures的循环引用 要讲self指定为unowned,姿势如下:
self.closure = { [unowned self] in
    self.doSomething()
}

对象比较

swift中有两种比较操作符 =====: 前者比较对象内容是否相同,默认调用isEqual方法; 后者比较对象是否指向同一个引用,检查指针是否指向同一地址。 在自定义子类中 要自己实现isEqual方法,这涉及到对象等同性的判断,同OC
note:swift自动提供equality(==)和 identity(===)操作符的逻辑实现:!= 和 !==, 这些不要重写

hashing

这部分同OC,NSObject的子类在等同性判断时 也要实现hash属性

swift的类型兼容性

OC转swift的类中,属性 方法 下标 初始化方法等是兼容可用的,但是一些swift特有的feature是不支持的,如下:
  • 泛型 generics
  • 元组 tuples
  • swift中的 非Int原始数据类型 的枚举
  • swift中定义的结构体
  • swift中的顶级函数
  • swift中的全局变量
  • swift中的类型别名 Typealiases
  • swift风格的可变参数 variadics
  • 嵌套类型 Nested types
  • 柯里化函数
swift的API转OC与OC转swift相同:
  • swift中的 optional 类型标记为__nullable
  • swift中的 non-optional 类型标记为 __nonnull
  • swift的常量存储属性和计算属性转到OC中为read-only属性
  • swift的变量存储属性转为OC中的read-write属性
  • swift中的type method转为OC的类方法
  • swift的初始化方法和实例方法转为OC的实例方法
  • swift的抛出errors的方法转为OC的方法时 会在方法名后后缀一个NSError **类型的参数。当改swift方法没有返回值时,相应的OC方法会有一个BOOL类型的返回值
  • 在OC代码中,不能子类化一个swift类
class Jukebox: NSObject {
    var library: Set<String>
    
    var nowPlaying: String?
    var isCurrentlyPlaying: Bool {
        return nowPlaying != nil
    }
    
    init(songs: String...) {
        self.library = Set<String>(songs)
    }
    
    func playSong(named name: String) throws {
        // play song or throw an error if unavailable
    }
}

// Here’s how it’s imported by Objective-C:

@interface Jukebox : NSObject
@property (nonatomic, copy) NSSet<NSString *> * __nonnull library;
@property (nonatomic, copy) NSString * __nullable nowPlaying;
@property (nonatomic, readonly) BOOL isCurrentlyPlaying;
- (nonnull instancetype)initWithSongs:(NSArray<NSString *> * __nonnull)songs OBJC_DESIGNATED_INITIALIZER;
- (BOOL)playSong:(NSString * __nonnull)name error:(NSError * __nullable * __null_unspecified)error;
@end

配置swift在OC中的接口

在需要控制swift API向OC暴露时,可以使用@objc(name)属性去改变类 属性 方法 枚举类型 枚举成员暴露到OC中的名字
向swift函数提供一个OC的name时,如果用的selector语法,要在selector的参数后面加:
在swift中使用@objc(name)属性时,转到OC时 没有命名空间。 当把一个可归档的OC类迁移到swift时,可以使用@objc(name)属性指定与OC类相同的类名(因为已归档对象会存储归档的类名),这样就可以把原先已经归档的数据恢复到swift类中。
swift中@nonobjc属性 会使swift的声明在OC中无法使用。这个可以用来解决 桥接方法 的环,或者 重新加载OC导入的类中的方法。如果OC的方法被swift的方法重写 并且该方法不能暴露给OC,比如指定一个参数为变量,那么这个方法必须标记为@nonobjc

Requiring Dynamic Dispatch

当在OC的runtime时 引入swift的API时,不能保证属性 方法 下标和初始化方法的动态派发。swift的编译器可能会绕过runtime 反虚拟化或者inline成员存取 来优化代码。 使用dynamic可以要求成员的存取方法在OC的runtime动态派发。在使用KVO或者OC runtime 的method_exchangeImplementations函数时,需要使用该修饰符。这样swift编译器inline的方法实现或者反虚拟化的存取方法不会被使用。
note:用dynamic修饰的 不能用 @nonobjc属性

OC selectors

OC selector在swift中用Selector结构体表示。构造一个selector用#selector表达式。如 let mySelector = #selector(MyViewController.tappedButton)(注意OC方法是其中的子表达式)。OC方法的引用可以括号化,可以用as运算符消除重载函数之间的歧义,如let anotherSelector = #selector(((UIView.insertSubview(_:at:)) as (UIView) -> (UIView, Int) -> Void))
用performSelector发送消息
performSelector的方法 同步执行 并隐式返回一个unwrapped optional unmanaged instance (Unmanaged<AnyObject>!),因为该方法返回值的类型和拥有关系在编译器不能确定。 Unmanaged Objects

用OC的行为方式写swift类

继承OC类

subclassName: superclassName 如果要重写OC中的类 要用override关键字

NSCoding

NSCoding协议必须实现init(coder:) 实现该协议的类的 子类中 如果有一个或多个自定义的初始化方法 或者 其他没有初始化值的属性 也必须要重写这个方法
从SB或者用NSUserDefaults or NSKeyedArchiver加载的对象 必须要完全实现改方法

实现协议

OC中的协议转到swift中,会在父类后面用,分隔 跟随协议名称
声明一个类型遵守单个协议 id<SomeProtocol> -> var textFieldDelegate: UITextFieldDelegate
声明一个类型遵守多个协议 id<SomeProtocol, AnotherProtocol> -> var tableViewController: protocol<UITableViewDataSource, UITableViewDelegate>
note:因为在swift中 类的命名空间和协议未定义,所以OC中的NSObject协议映射为NSObjectProtocol

初始化方法 和 反初始化方法

#####

密码学技术笔记

转载 留着以后看,哈哈

#对称加密: #####DES
DES是1977年美国联邦信息处理标准中使用的一种对称密码技术,曾今被美国和其他国家政府银行使用。 不过现在已被暴力破解,我们除了用它解密以前的密文外,已不再使用DES了。不过这里我们可以用它来了解下什么是对称加密。

#####加密和解密 DES是一种把64比特明文加密成64比特的密文的对称密码算法,密钥长度56位,其中每隔7比特有个错误检查比特。结果DES密钥总长度为64比特。

#####分组密码 DES以64比特明文为一个单位进行加密,这64比特单位称为分组。so,以分组为单位处理密码的算法称为分组密码。DES属于分组密码。 DES每次只能加密64比特数据,如果明文较长,就需要对DES加密进行迭代,迭代的具体方式称为模式。后文会描述。 图DES加密与解密流程

#####三重DES 三重DES只是将DES重复3次,为了增加DES强度

#####AES 目前很安全 。。。。。

###分组密码模式 …..好多…..

#非对称加密 ####公钥密码 在对称密码中,由于加密解密的密钥是相同的,因此密钥配送就成了问题。如过使用公钥密码,就解决了配送问题。 #####密钥配送问题:

解决密钥配送有多个方法,但都很难处理
1.双方通信时如果要加密,密钥不能在通信内容中。否则会被中间人窃听。
2.双方可以事先共享密钥,如果双方离得很近自然好说。但如果离得很远就不能在消息里传播密钥,因为可能会被窃听。邮寄存储卡也可能会被窃取。假如一个公司有1000人需要彼此加密通信,那密钥数可能会达到50万个,这个不现实。
3.如果有个密钥分配中心存储密钥,那1000人的数据库会有1000个密钥。但如果员工增加,密钥也要增加,数据库负荷会加大,如果数据库瘫痪,全公司的通信就会瘫痪。密钥分配中心也可能会遭到攻击.
4.Diffie-Hellman密钥交换。是个方案。我们在后续会讨论

#####公钥密码解决配送问题 两种密钥。一种是加密密钥,一种是解密密钥
这里有公钥和私钥,公钥和私钥既可以当做加密密钥,也可以做解密密钥。只是使用场景不同
你既可以用公钥加密,私钥解密。也可以用公钥解密,私钥加密。区别只是在于使用场景。
公钥和私钥是一一对应的,一对公钥和私钥统称为密钥对
我们来看一种公钥加密,私钥解密的情景。 Alice要发消息给Bob 1.Bob要接受消息,生成密钥对。私钥自己保管,公钥发给Alice 2.公钥被截获也没关系。因为公钥只负责加密 3.Alice用公钥对消息加密,发送给Bob。 4.即使密文中间被截获也没关系,公钥无法解密 5.Bob用自己私钥揭秘 如图所示 #####公钥密码的问题 1.公钥是否是正确合法的.假如中间人给你换了个公钥呢?
2.处理速度要比对称加密慢很多,大约只有百分之一 我们后面讨论这两个问题

####RSA RSA加密
密文 = 明文 E mod N (mod是取模的意思)
E和N的组合就是公钥 简称 “公钥是(E,N)”

RSA解密
明文 = 密文 D mod N
D和N的组合就是私钥

####中间人攻击 这个逻辑很简单,中间人替换掉了接受者得公钥,向对方提供了自己的公钥。结果发送者用的中间人的公钥加密消息发送出去,中间人接受到消息并用自己私钥解密得到明文。再伪造一个假消息用接受者的公钥加密还给接受者。具体流程如图:

#混合密码技术 组成机制:

  • 用对称密码加密消息
  • 通过伪随机数生成器生成对称密码加密中使用的会话密钥
  • 用公钥加密会话密钥
  • 从混合密码系统外部赋予公钥密码时使用的密钥

#######混合密码系统加密解密过程 加密:

  • 使用伪随机生成器生成会话密钥 ,会话密钥加密明文生成密文
  • 用接受者的公钥加密会话密钥
  • 两个密文组合后发送出去

解密:

  • 收到消息先解密会话密钥,用接受者的私钥解密,得到会话密钥
  • 用会话密钥解密加密过得密文,即可得到消息

#认证 #####单向散列函数 输入称为消息,输出称为散列值。
单向散列函数可以根据消息的内容计算出散列值,而散列值就可以被用来检查消息的完整性。
######单向散列函数的性质:

  • 根据任意长度的消息计算出固定长度的散列值
  • 能够快速计算出散列值
  • 消息不同散列值也不同
    • 两个不同消息产生同一个散列值的情况称为碰撞
    • 要找到和该消息具有相同散列值的另外一条消息是非常困难的。这一性质称为弱抗碰撞性
    • 要找到散列值相同的两条不同消息是非常困难的。称为强抗碰撞性
  • 具备单向性

######关于术语 单项散列函数又称为消息摘要函数,哈希函数或者杂凑函数

######实际应用

  • 检测软件是否篡改
  • 基于口令的加密(PBE):原理是将口令和炎(伪随机数)混合后计算其散列值,然后将这个散列值用作加密密钥
  • 消息认证码:检测并防止通信过程中得错误,篡改和伪装
  • 数字签名
  • 伪随机数生成器
  • 一次性口令

###消息认证码 消息的认证是指:“消息来自正确的发送者”。通过使用消息认证码,我们就可以同时识别出篡改和伪装,既可以确认消息完整性,也可以进行认证。 消息认证码的输入包括任意消息的长度和一个接受者和发送者之间的共享密钥,输出是固定长度,这个数据称为MAC值。
消息认证码是一种与密钥相关联的单项散列函数。
如图:

使用步骤:如图

###数字签名
数字签名就是将公钥反过来用实现的。
公钥加密就是用公钥加密消息,用私钥解密密文。
而数字签名是用私钥对消息生成签名,传输数字签名的密文,最后用公钥解密验证签名。
如图所示:


解释一下:用私钥加密所得的密文,只能用公钥才能解密。这就是说,如果某个公钥成功解密了密文,那就证明这段密文是由与他配对的私钥进行加密所得的。

#####数字签名方法

  • 直接对消息签名
  • 对消息的散列值签名
    如图:

###通过RSA实现数字签名

签名 = 消息的D次方 mod N  
D和N就是签名者的私钥  
有签名求得的消息 = 签名的E次方 mod N  
E和N就是签名者的公钥
数字签名无法解决的问题

公钥必须属于真正的发送者。即使数字签名算法再强大,如果你得到的公钥是伪造的,那么数字签名也会完全失败。我们需要使用一种社会性的基础设施,即公钥基础设施。简称PKI。
见下文。

#证书
公钥证书和身份证,驾照很相似,里面有姓名,证件号,地址等个人信息,以及属于此人的公钥,由认证机构施加数字签名。只要看到公钥证书,我们就可以知道认证机构认定该公钥的确属于此人。公钥证书简称证书。

#####证书应用场景
如图:

#公钥基础设施(PKI) 公钥基础设施是为了能够更有效地运用公钥而制定的一系列规范和规格的总称。

#####PKI的组成要素

  • 用户———使用PKI的人
  • 认证机构————颁发证书的人
  • 仓库————保存证书的数据库

######用户 分两种:1.一种是使用PKI注册自己的公钥的人。 2.另一种是使用已注册的公钥的人。

######认证机构(CA)
认证机构接受对证书进行管理的人。认证机构具体行为如下:

  • 生成密钥对
  • 在注册公钥时对本人进行身份认证
  • 生成并颁发证书
  • 作废证书

#####仓库
保管证书的数据库,也叫证书目录

PKI组成要素

#密钥,随机数与应用技术

####什么是密钥

  • 密钥本身就是一个巨大的数字,数字大小不重要,重要的是密钥空间的大小,也就是说可能出现密钥的总数量。因为密钥空间越大,暴力破解就越困难。密钥空间大小有密钥长度决定。
  • 密钥与明文是等价的,假如明文具有100万的价值,那么这段密钥也具有100万的价值;假如明文值1亿,密钥也值1亿。
  • 不要使用自己开发的密码算法进行加密,而是使用一个经过全世界密码雪茄共同验证的密码算法。如果你使用了自己的密钥,可能会出现你的密钥被长时间破解了,你依然无法知晓。 信息的机密性不应该依赖密码算法本身,而是依赖于妥善保管的密钥。

####Diffie-Hellman 密钥交换
Diffie-Hellman 密钥交换算法,通信双方仅通过交换一些可以公开的信息就能够生产出共享密钥的

##基于口令的密码(PBE) 基于口令的密码就是一种根据口令生成的密钥并用该密钥进行加密的方法。其中加密和解密使用同一种密钥
PBE加密过程如图:

3个步骤:
1.生成KEK
2.生成会话密钥并加密
3.加密消息

PBE解密过程:
个人觉得是不是箭头反了?

#随机数
#####随机数的用途

  • 生成密钥
    用于对称密码和消息认证码

  • 生成密钥对
    用于公钥密码和数字签名

  • 生成初始化向量(IV)
    用于分组密码的CBC,CFB和OFB模式

  • 生成nonce
    用于防御重放攻击以及分组密码的CTR模式等

  • 生成盐
    用于基于口令的密码(PBE)等

其中生成密钥和生成密钥对是最重要的,即使密码强度再高,只要攻击者知道了密钥,就会立刻变得形同虚设。因此我们需要用随机数来生成密钥,使之无法被攻击者看穿。

#####随机数的性质

  • 随机性
  • 不可预测性
  • 不可重现性

如图

#####伪随机数生成器
通过硬件生成的随机数列是根据传感器收集的热量,声音的变化等事实上是无法预测的。像这样的设备就称为随机数生成器。
而可以生成随机数的软件则称为伪随机数生成器。因为软件无法生成真随机数。

伪随机数生成器具有“内部状态”,并根据外部输入的“种子”来生成伪随机数列

#####伪随机数生成器的内部状态
伪随机数生成器的内部状态,是指伪随机数生成器岁管理的内存中的数值。

当有一条伪随机数的请求时,伪随机数生成器会根据内存中的数值进行技术,并将结果输出。随后改变自己的内部状态。因此,根据内部状态计算伪随机数的方法和改变内部状态的方法组合起来,就是伪随机数生成的算法。

#####伪随机数生成器的种子
伪随机数的种子是用来对伪随机数生成器的内部状态进行初始化。伪随机数生成器是公开的,但种子是需要自己保密的。由于种子不可以被攻击者知道,因此不可以使用容易被预测的值。

#PGP—密码技术的完美组合 用PGP加密
用PGP解密

用PGP生成数字签名

PGP验证数字签名

PGP生成数字签名并加密

PGP解密并验证数字签名

frame与Autolayout(Masonry)的几点结论

在做开发的时候,UI适配方面老代码都是使用frame做UI布局,新的代码却是用的Autolayout,我们主要是用的三方库masonry写约束完成布局。在这个过程中。我一直有个错误的认识,Autolayout不会改变frame。网上看到很多blog也这么认为。这么认为多是由于设置完约束之后,打印frame的值为CGRectZero或者一个错误值造成的。但是在我调试一段代码时,情况却超出了我原来(错误)的认识。为什么打印的frame完全正确?

答案是设置约束后frame的更新并不是实时的,需要等到根据约束计算好view的位置和尺寸之后才会刷新frameAutolayout的本质是通过一套约束规则去确定一个viewsuperview中的位置和尺寸。既是位置和尺寸那不就是frame嘛。所以我通过文档和实验得出了以下几个结论:

  • Autolayout是一套关于view位置和尺寸的约束规则
  • Autolayout改变可以改变viewframe,但是frame的改变并不能改变Autolayout的约束规则(当translatesAutoresizingMaskIntoConstraints = YES时,会根据autoresizingMask的相应设置更新相应的约束,但是这很可能会与既有的约束冲突,iOS7很有可能crash。设置translatesAutoresizingMaskIntoConstraints = NO是推荐的做法,所以我们可以认为更改frame不会改变约束),约束应该通过约束更新的方式改变。想要在Autolayout改变后立即看到frame的变化,请调用以下代码来立即刷新UI的布局
- (void)setNeedsLayout;
- (void)layoutIfNeeded;
  • Autolayoutframe能否混用,答案是一定程度上可以,牢记上面一条两者之间的相互影响关系。在约束布局的UI视图中,混用frame布局,一定要先调用上面的这段代码,这样就能得到superView和相关view准确的frame。但是修改frame不会修改约束使得混用的情况的变得复杂很容易出错,所以还是大众化的建议:最好不要混用。
  • 在一些业务处理时,在约束布局后,需要获取view的宽高尺寸等信息,不用说,先调用上面的代码。这也可以说是一种混用的情况吧。
  • 一些人认为在viewDidLayoutSubviewsviewDidAppear 中布局已经确定,可以获取实际的frame,诚然如此。这样也比每次调通上面的代码效率高。但是这只适用于进入页面后布局不在变化的情况,假如在当前页面一些操作触发了约束变化,那么这种做法就不适用了。上面这段代码虽然效率低,但是适用范围更大。

iOS动画总结-frame,Autolayout,Masonry

按照鄙人一贯风格,废话不多说,进入正题。本文是对iOS动画基础知识的总结,以便以后查阅,有不对的地方,敬请指正。

iOS动画的实现依赖于Core Animation,Apple的官方文档地址Core Animation Guide

Core Animation是iOS和OS X平台上负责图形渲染与动画的基础框架。具体为何物,相信看官已经有了一个感性的认识,具体Google之。iOS平台上的动画分为隐式动画和显式动画。Core Animation基于一个假设:屏幕上的任何东西都可以(可能)做动画。动画并不需要你在Core Animation中手动打开,相反需要明确地关闭,否则他会一直存在。

隐式动画

隐式动画的意思是说我们并没有指定任何动画的类型。我们仅仅改变了一个属性,然后Core Animation来决定如何并且何时去做动画。 但当你改变一个属性,Core Animation是如何判断动画类型和持续时间的呢?实际上动画执行的时间取决于当前事务(CATransaction)的设置,动画类型取决于图层行为。隐式动画的默认duration为0.25s。简而言之,任何在一次run loop循环中属性的改变都会被集中起来,然后做一次0.25s的动画。 我们直接改变CALayer的可动画属性,会有隐式动画的效果,但是改变UIView的对应属性就没有隐式动画的效果,因为UIView将关联的图层的动画禁用了。想在UIView上做动画,只能用显示动画 或者 用其他方式打开禁用。
关于隐式动画,就简单的讲这么多,我们的重点是在显式动画上。

显式动画

Core Animation类及常用属性
Core Animation的类图

如类图所示,Core Animation遵守CAMediaTimingCAAction协议。CAAnimation主要分为三类:动画组、属性动画和过渡动画,其中属性动画又分为基础动画和关键帧动画。
各类常用属性
  • CAMediaTimingCALayerCore Animation都实现了这个协议,它模拟了一个定时系统的层级,它的每个对象都描述了从父类对象到本地的时间值的映射。从父类时间线到本地转化的步骤见文档。
    • 宗旨:Core Animation的时间都是相对的,每个动画都有它自己描述的时间,可以独立地加速,延时或者偏移。记住这点,可以方便理解属性的含义。
    • duration:动画的持续时长,它可能和动画进行的真实duration不一样,这个依赖与superlayer的time space或者就是speed。默认值是0,但不真是0,只是一个默认值,动画真实duration是0.25s。
    • beginTime:对象相对父类对象的开始时间,即动画的开始时间,但是是相对于父类的时间成了。获取当前时间用CACurrentMediaTime()函数。常见场景:animation group,里面有多个动画时,beginTime就是其parent object—animation group开始时间的一个偏移。至于偏移量根据业务需求而定,但是一定要注意animation group内的所有动画要有一个共同的时间起点,不要每次都是获取当前时间再加上偏移量,否则容易出错。
    • speed:指定从父类时间空间到receiver的时间空间是怎样映射的。是一个时间的倍数,默认1.0,减少它会减慢图层/动画的时间,增加它会加快速度。如果2.0的速度,那么对于一个duration为1.0的动画,实际上在0.5秒的时候就已经完成了。
    • timeOffset:和beginTime类似,但是和增加beginTime导致的延迟动画不同,增加timeOffset只是让动画快进到某一点,例如,对于一个持续1秒的动画来说,设置timeOffset为0.5意味着动画将从一半的地方开始。
    • autoreverses:设置在每次间隔交替循环过程中自动回放。这个值设置为TRUE后动画会沿着原执行路径自动返回,这会加倍动画的实际执行时间。
    • repeatCount:动画重复次数,浮点型,即动画可以重复小数次。小数次的部分会压缩整个动画效果,举个例子,设置为0.5,那么整个动画过程仍然是完整的,但是整个动画的持续时间为原来实际执行时间的0.5倍,还有注意如果autoreverses设置为TRUE,小数次部分没有autoreverses效果;还有一点该属性与duration属性类似,0为默认值,设置为0时并不是重复0次,而是1次。想要动画重复次数无限,可以用系统的宏定义INFINITYHUGE_VALFMAXFLOAT 其中前两个是一样的。到此我们可以总结一个公式动画实际执行时间 = duration * (int)repeatCount * (autoreverses ? 2 : 1) + duration * (repeatCount - (int)repeatCount)
    • repeatDuration:这是除了repeatCount外创建重复动画的另一种方式。它让动画重复一个指定的时间,而不是指定次数。
    • fillMode:这个属性定义的是动画在活动时间之外的行为。这就涉及到模型图层和呈现图层了。如果不了解layer动画的这个两个图层的含义。简单的说这个属性的定义就是 动画开始之前和动画结束之后,被设置动画的属性的值到底是多少。这个属性就是用动画开始和结束的值填充开始之前和结束之后的时间,让动画保持开始之前那一帧,或者动画结束之后的那一帧。fillMode是一个NSString类型,可以接受如下四种常量:
      • kCAFillModeForwards:向前填充动画状态
      • kCAFillModeBackwards:向后填充动画状态
      • kCAFillModeBoth:向前向后填充动画状态
      • kCAFillModeRemoved:默认值,动画结束后显示模型图层的指定值。

      当不是默认值的时候,还需要设置removeOnCompletion = NO配合才能起到效果。但是需要自己在在不需要动画的时候把动画移除掉。

  • CAnimation:核心动画基础类,为抽象类,不能直接使用,实现CAMediaTiming协议
    • timingFunction:调速功能属性,定义动画的步速。CAMediaTimingFunction类型,系统预定义的步速类型分为以下几种:
      • kCAMediaTimingFunctionLinear:线型步速,即匀速
      • kCAMediaTimingFunctionEaseIn:先慢后快(慢进快出)
      • kCAMediaTimingFunctionEaseOut:先快后慢(快进慢出)
      • kCAMediaTimingFunctionEaseInEaseOut:先慢后快再慢
      • kCAMediaTimingFunctionDefault:默认值,中间比较快
    • delegate:设置动画委托,有两个委托方法:
            - (void)animationDidStart:(CAAnimation *)anim;
            - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
            
* `removedOnCompletion`:是否自动在动画结束时将动画从渲染树上移除,默认是`YES` - `CAAnimationGroup`:动画组,将一些列动画组合到一起。
* `animations`:`CAAnimation`数组类型,成员为其他类型的动画 - `CAPropertyAnimation`:属性动画,继承于`CAnimation`
* `keyPath`:要做动画的的属性的`keyPath`,使用kvc匹配到`CALayer`的可动画属性

***注意:组动画的duration和组内的成员动画的duration不同时,若成员动画duration>组动画duration,则成员动画会在组动画的活动时间后截断不再执行后面的部分,反之,成员动画会在组动画的活动时间后继续执行。*** - `CABasicAnimation`:基础动画类,继承于`CAPropertyAnimation`,并增加了以下三个`id`类型的属性:
* fromValue:要设置的`keyPath`属性对应的动画开始之前属性的值
* toValue:要设置的`keyPath`属性对应的动画结束之后的值 
* byValue::要设置的`keyPath`属性对应的动画执行过程中改变的值

三个属性的取值如下表,需要注意的是,三个属性使用时组合方式有很多种,但是不能同时使用三个属性,否则会有冲突。
![](../images/CoreAnimationSummary/CABasicAnimationValue.png) - `CAKeyframeAnimation`:关键帧动画
* `values`:对象数组,提供每一个关键帧的属性数据。在数组赋值的时候,如果要考虑好的平滑过渡效果,需要将数组中第一个对象和最后一个对象设置为动画开始之前和动画结束后模型图层的属性值。这是因为`CAKeyframeAnimation`并不能自动把当前值作为第一帧,这样动画开始和结束后会有突然跳帧突变的效果。
* `path`:关键帧动画的执行路径,优先级要高于`values`,当设置此属性时,`values`属性不再起作用。
* `timingFunctions`:`CAMediaTimingFunction`类型数组,指定帧与帧之间过渡的步速。 - `CATransition`:过渡动画,前面的动画类型都是对可动画属性起作用,对不可动画属性做动画,就需要是用`CATransition`。
* `type`:过渡动画的动画类型,系统提供了四种过渡动画:
    - kCATransitionFade 渐变效果
    - kCATransitionMoveIn 进入覆盖效果
    - kCATransitionPush 推出效果
    - kCATransitionReveal 揭露离开效果
* `subtype`:过渡动画的动画方向,系统提供四个方向:
    - kCATransitionFromRight 从右侧进入
    - kCATransitionFromLeft 从左侧进入
    - kCATransitionFromTop 从顶部进入
    - kCATransitionFromBottom 从底部进入
* `startProgress`:动画开始点,取值范围[0,1]
* `endProgress`:动画结束点,取值范围[0,1]

动画的三种实现方式

一:UIView层的方法调用,长相大概是下面这样子
[UIView animateWithDuration:1.0f animations:^{
    // TODO ... 可动画属性设置
} completion:^(BOOL finished) {
    // TODO ... 可动画属性的动画结束后的值
}];
二:UIView层的方法调用,[begin commit]模式,长相大概是下面这样子
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0f];  // 注意[UIView setAnimation***]这些方法一定要写在可动画属性设置的前面,写在后面,设置不一定有效果,自己可以try一下
    // TODO ... 可动画属性设置
[UIView commitAnimations];
三:CALayer层的方法调用,使用Core Animation中的子类,长相大概是下面这样子
CABasicAnimation *anima = [CABasicAnimation animationWithKeyPath:@"position"];
anima.fromValue = [NSValue valueWithCGPoint:CGPointMake(x, y)];
anima.toValue = [NSValue valueWithCGPoint:CGPointMake(x, y)];
anima.duration = 2.0f;
[someView.layer addAnimation:anima forKey:@"positionAnimation"];
需要注意的是 在使用frameAutolayout(指apple原生约束方式)Masonry这三种常见方式布局的时候,实现位移、缩放等动画的方式也略有不同。使用约束布局的view做动画相对简单,只需要改变目标view的属性 关联的view会自动有动画效果,而使用frame布局时,需要将所有关联view的相应属性都做改变。
还有就是为了平滑过渡,无论使用那种动画方式都要设置好模型图层即动画开始和结束之后的属性值,否则会有跳帧突然回到初始状态的现象。

三种动画实现方式对frameAutolayout(指apple原生约束方式)Masonry的适用

对于frame布局的view,大可以按照以前的方法去做。而使用约束方式布局的view因为设置的约束多样,同一个布局效果可能有多种设置约束的方式,而且实现一些效果时修改相应的约束很难(尤其是约束用masonry写的时候,哪位大神精通这一块,请不吝赐教),所以在做位移、缩放等几何动画时,对UIView建议使用CGAffineTransform transform仿射变换属性、CALayer建议使用CATransform3D transform属性。这些知识可以看一下扩展阅读中的内容。这里就不重复了。

NOTE!

在使用委托方法时,先VC中把动画保存为一个属性,然后在委托方法中比较判断,这不起作用,因为委托方法中传入的animation参数是原始值的一个deep copy。 在委托方法中判断animation的正确姿势是kvc & Dictionary, CAAnimation遵守kvc协议,它的设计像一个NSDictionary,所以我们可以用-setValue:forKey:-valueForKey:方法设置一个标识tag,根据这个tag去判断就好了,还可以做很多其他的工作奥,比如传递animaiton所属的VC,使用很灵活。
姿势示范:怎样在委托方法中区分多个动画
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    // 错误打开方式,永远不会执行
    if (anim == self.anim) {
        //TODO:
    }
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    // 正确的打开方式
    if ([anim valueForKey:@"key"] isEqualToString:@"anim") { // key-anim 键值对是初始化动画时设置好的
        //TODO:
    }
}

扩展延伸阅读

Masonry基础使用

CATransform3D的基本属性

OC的关键字

开发中遇到了一些不常见的关键字,原来只是一知半解,所以做了一些功课,总结了以下。


@encode()

在使用NSNumber时,很可能会遇到这个关键字。在用masonry时,也发现了这个关键字的使用场景。我们可以把intBOOL等封装成NSNumber对象,但是翻过来,我们怎么知道NSNumber内封装的到底是什么原型的数据呢。就会用到@encode()

Objective-C 的数据类型,甚至自定义类型、函数或方法的元类型,都可以使用ASCII编码。@encode(aType) 可以返回该类型的C字符串(char *)的表示。使用举例如下:

id value = [dic valueForKey:key];  
if([value isKindOfClass:[NSNumber class]]){  
    const char * pObjCType = [((NSNumber*)value) objCType];  
    if (strcmp(pObjCType, @encode(int))  == 0) {  
        NSLog(@"字典中key=%@的值是int类型,值为%d",key,[value intValue]);  
    } else if (strcmp(pObjCType, @encode(float)) == 0) {  
        NSLog(@"字典中key=%@的值是float类型,值为%f",key,[value floatValue]);  
    } else if (strcmp(pObjCType, @encode(double))  == 0) {  
        NSLog(@"字典中key=%@的值是double类型,值为%f",key,[value doubleValue]);  
    } else if if (strcmp(pObjCType, @encode(BOOL)) == 0) {  
        NSLog(@"字典中key=%@的值是bool类型,值为%i",key,[value boolValue]);  
    }
}

@class

这个关键字我用的很多,总而言之一句话,.h文件内,能用@class就别导入头文件。


@dynamic

这个关键字是相对于@synthesize的,用于动态合成property。编译器不会合成accessorruntime时动态解析。


@defs

这个关键字把OC对象转化成对应的struct结构体。这个struct与原OC类具有相同的内存布局。OC类底层也是C struct和一些方法的封装。涉及非常底层的操作或优化的时候才会用到。示例代码如下:

struct { @defs( NSObject) }


@autoreleasepool

用于ARC下代替NSAutoreleasePool的关键字。一般用于降低app的内存峰值,包裹敏感代码。


@compatibility_alis

用于给一个类设置一个别名。这样就不用重构以前的类文件就可以用新的名字来替代原有名字。示例代码如下

@compatibility_alias AliasClassName ExistingClassName


附:OC保留的关键字列表

@class
@defs
@protocol @required @optional @end
@interface @public @package @protected @private @property @end
@implementation @synthesize @dynamic @end
@throw @try @catch @finally
@synchronized @autoreleasepool
@selector @encode
@compatibility_alias
@”string”

扩展阅读

关于Objective-C warning的笔记

那些被遗漏的Objective-C保留字

The Complete List of Objective-C 2.0 @ Compiler Directives