Swift 合理使用结构体,枚举,extensions
枚举
Swift枚举是first-class types,它有很多Swift class具有的特性
- Associated Values 关联值
- 计算属性
- 实例方法
- 构造函数
- 遵循协议
- 支持extensions
好了,那么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 : _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(@noescape f: (Wrapped) throws -> U) rethrows -> U? /// Returns `nil` if `self` is `nil`, `f(self!)` otherwise. @warn_unused_result public func flatMap(@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相关的特性
- 定义属性来存储值
- 定义方法来提供功能
- 定义subscripts,来实现用下标访问[]
- 定义构造函数,来初始化
- 遵循某一个协议
- 支持extenstion
当然,有一些特性是它不具有的
- 继承
- 类型转换和runtime类型检查
- deinit方法,来进行销毁时候的资源释放
- 引用计数,让多个引用指向同一个实例。
比如,在OC中,你通常这样写一个Model
//.h 头文件 @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的优势在于
- Struct是值类型,每次传递的时候,都会进行一次拷贝,也就是说,在多线程的环境下,它是线程安全的,当你把一个Struct作为参数传递给一个Class的时候,你不需要担心这个Class会修改我原始的Struct
- Struct不需要考虑内存泄漏
通常,当以下一条或者多条满足的时候,你可以优先考虑使用结构体
- 这个数据结构主要目的是用来封装一系列相关的值的时候
- 当传递值的时候,希望传递的是拷贝的时候
- 这个数据结构本身存储的数据也是值类型,也就是说传递的时候是值传递
- 这个数据结构不需要从其他地方继承。
Extensions
Swift的Extensions可以给class,struct,enum,protocol添加功能性的方法/属性/subscripts。这和Objective C的Category很像。但是Swift Extension更加强力,并且不像Category,它不需要名字。
通过Extensions,你可以
- 增加计算属性,或者计算类型的属性
- 定义实例方法和类型方法
- 提供新的构造函数
- 定义和使用嵌套类型
- 定义subscripts
- 让一个类型遵循一个协议
红色的部分是个人觉得比较容易忽略的。
协议扩展
协议扩展是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 }
extensions也可以用来设计接口
系统的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(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
我们可以自定义myMap来定义个自己的映射方法。
extension SequenceType{ public func myMap(@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的常用用途
分离代码逻辑
把部分逻辑放倒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 : 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 } } } 原文:http://blog.csdn.net/hello_hwc/article/details/51870667#t1