14年Swift刚出的时候开始学习的Swift,后来由于项目需要,一直写App还是用的OC。最近打算把Swift重新捡起来,这个Objective C转Swfit系列就当成是我的复习笔记,顺便写成博客记录下来吧。
这个系列不是讲解Swift基础,主要是讲解OC(以下OC均指的是Objective C)转过来的同学有些习惯要改变了,才能更好的使用Swift的很多优秀特性。
通常,你在Objective C中,用枚举NS_ENUM来定义有限的状态,比如,假如我们要表示一个登录的结果
typedef NS_ENUM(NSInteger,LHLoginResult){
LHLoginResultSucceess, //成功
LHLoginResultFailure, //失败
LHLoginResultError, //出错了
};
其实,OC中,枚举更像一个加强版的整型
假如,把这个简单的转为Swift,那么看起来是这样子的。
enum LoginResult {
case Success
case Failure
case Error
}
如果是这么写枚举,你真的是暴殄天物了
Swift枚举是first-class types
,它有很多Swift class具有的特性
好了,那么Swift中,这样的一个“登录结果”应该如何用枚举来表示呢?
通常,失败的时候,我们希望知道失败的原因是啥,出错的时候,我们希望知道错误的原因是啥。
利用Associated Values 关联值,来实现这一点
于是,这个枚举变成了酱紫
enum LHLoginResult {
case Success
case Failure(message:String)
case Error(error:NSError)
}
等等,现在是三种结果,要是能提供一个接口,只返回给我一个Bool,告诉我登陆成功还是失败就更好了
利用Extension和计算属性,我们添加一个方便的接口
extension LoginResult{
var isSuccess:Bool{
switch self {
case .Success:
return true
default:
return false
}
}
}
通常,App需要Log出一些信息,我们继续完善这个枚举,让它支持Log
用extension,让这个枚举遵循协议CustomStringConvertible(Swift中的一个log相关的协议)
extension LoginResult:CustomStringConvertible{
var description: String {
switch self {
case .Success:
return "Success"
case let .Failure(message):
return message
case let .Error(error):
return error.localizedDescription
}
}
}
除此之外,Swift的枚举还支持RawValues
enum ASCIIControlCharacter: Character {
case Tab = "\t"
case LineFeed = "\n"
case CarriageReturn = "\r"
}
再深入一点,我们来看看Swift中一些默认使用枚举来定义的类型
Optional
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)
/// Construct a `nil` instance.
public init()
/// Construct a non-`nil` instance that stores `some`.
public init(_ some: Wrapped)
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`.
@warn_unused_result
public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
/// Returns `nil` if `self` is `nil`, `f(self!)` otherwise.
@warn_unused_result
public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
/// Create an instance initialized with `nil`.
public init(nilLiteral: ())
}
可见,Optional本身就是一个枚举,包含了两种可能性:None(nil),Some(Wrapped)(这里利用了Swift范型)。
除了枚举之外,结构体也是Objective C转过来的同学比较容易忽略的一个数据结构。
Swift的结构体和C的结构体有很大区别,它有很多Class相关的特性
当然,有一些特性是它不具有的
比如,在OC中,你通常这样写一个Model
//头文件
@interface LHPerson : NSObject<NSCopying,NSCoding>
@property (copy,nonatomic)NSString * name;
@property (assign,nonatomic)NSUInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
+ (instancetype)personWithName:(NSString *)name age:(NSUInteger)age;
@end
//.m文件
@implementation LHPerson
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age{
if (self = [super init]) {
_name = name;
_age = age;
}
return self;
}
+ (instancetype)personWithName:(NSString *)name age:(NSUInteger)age{
return [[self alloc] initWithName:name age:age];
}
#pragma mark - NSCoding
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
_name = [aDecoder decodeObjectForKey:@"name"];
_age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
if (_name != nil) [aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeInteger:_age forKey:@"age"];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone{
LHPerson * copyed = [[self.class allocWithZone:zone] init];
copyed->_age = self.age;
copyed->_name = self.name;
return copyed;
}
@end
难么,同样的Model,在Swift应该怎么写呢?
上面的Model更适合结构体来存储
通常,你可以这么写
struct Person{
var name:String
var age:Int
init(name:String,age:Int){
self.name = name
self.age = age
}
}
为了自定义Log,我们可以让结构体实现CustomStringConvertible
协议
struct Person:CustomStringConvertible{
var name:String
var age:Int
init(name:String,age:Int){
self.name = name
self.age = age
}
var description: String{
return "Name:\(name) Age:\(age)"
}
}
结构体相对于Class的优势在于
通常,当以下一条或者多条满足的时候,你可以优先考虑使用结构体
Array,Dictionary本质上都是结构体
public struct Array<Element> : CollectionType, MutableCollectionType, _DestructorSafeContainer {
public var startIndex: Int { get }
public var endIndex: Int { get }
public subscript (index: Int) -> Element
public subscript (subRange: Range<Int>) -> ArraySlice<Element>
}
//省略掉Array的Exetensions ...
Swift的Extensions可以给class,struct,enum,protocol添加功能性的方法/属性/subscripts。这和Objective C的Category很像。但是Swift Extension更加强力,并且不像Category,它不需要名字。
通过Extensions,你可以
红色的部分是个人觉得比较容易忽略的。
协议扩展是OC转过来的最容易忽略的,Swift是一个很适合《面相协议编程的语言》,很多基本的类型。比如AnyObject和Any都是协议类型(事实上,在Swift的时候,不知不觉你已经面相协议编程了)。
@objc public protocol AnyObject {
}
public typealias Any = protocol<>
其中,协议扩展最灵活的地方是,支持where语句条件扩展。
比如,这是我写Swift代码的一个自定义操作符SetUp
,用协议来定义的。
public protocol SetUp {}
extension SetUp where Self: AnyObject {
//Add @noescape to make sure that closure is sync and can not be stored
public func SetUp(@noescape closure: Self -> Void) -> Self {
closure(self)
return self
}
}
extension NSObject: SetUp {}
然后,我只是希望,当AnyObject遵循这个协议的时候具有具有SetUp
.接着,用第二次协议扩展,来让NSObject遵循这个协议。于是,任何NSObject
的子类,你都可以这么调用
let textfield = UITextField().SetUp {
$0.frame = CGRectMake(0, 0,200, 30)
$0.textAlignment = .Center
$0.font = UIFont.systemFontOfSize(14)
$0.center = view.center
$0.placeholder = "Input some text"
$0.borderStyle = UITextBorderStyle.RoundedRect
}
系统的SequenceType
是Array遵循的协议,于是你可以这样调用
let array = [1,2,3,4,5]
let array2 = array.filter({$0 > 1}).map({$0 * 2})//4 6 8 10
其中,这个map的定义如下
public func map<T>(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
我们可以自定义myMap来定义个自己的映射方法。
extension SequenceType{
public func myMap<T>(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]{
print("Map begin")
var result = [T]()
try self.forEach { (e) in
do{
let item = try transform(e)
result.append(item)
}catch let error{
throw error
}
}
print("Map end")
return result
}
}
let arr = [1,2,3]
let mapped = arr.myMap({"\($0)"})
print(mapped)
会发现Log
Map begin
Map end
["1", "2", "3"]
知名的Swift函数响应式编程开源库RxSwift,就是利用了Extensions,来让你定义自己的操作符。
把部分逻辑放倒extensions中,能够让代码更易于维护可扩展
class TableviewController: UITableViewController { } extension TableviewController{ override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // } override func numberOfSectionsInTableView(tableView: UITableView) -> Int { } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { } }
再看看系统Array
public struct Array<Element> : CollectionType, MutableCollectionType, _DestructorSafeContainer {
//...
}
extension Array : ArrayLiteralConvertible {
//...
}
extension Array : _ArrayType {
//...
}
通过extension,把代码逻辑分离开.从而实现:《对扩展开放,对修改封闭》
比如,你在OC中,的工具方法,可以这么写
Tips:这里可以不写前缀lh_
extension UIScreen{
class var lh_width:CGFloat{
get{
return UIScreen.mainScreen().bounds.size.width
}
}
class var lh_height:CGFloat{
get{
return UIScreen.mainScreen().bounds.size.width
}
}
class var lh_bounds:CGRect{
get{
return UIScreen.mainScreen().bounds
}
}
}
并不是什么高深的东西,写出来加深下印象,也分享给觉得有用的人。