1

前言

作者自己说自己很喜欢swift,因为他喜欢Haskell。可能看上了swift支持函数式编程的缘故。

中间扯皮各种略。。。

扯到函数编程刚开始不习惯但是会带来方便。。略结束。。。

栗子

ex by oc:

- (NSAttributedString *)attributedString:(NSString *)input 
{
    return [[NSAttributedString alloc] initWithString:input];
}

看上去无公害,但是参数如果是nil,那边会导致崩溃。而且更坑爹是在运行时才会发现这个问题。这种问题一旦是你的下游程序猿(用户)去接手的时候,找这个BUG就不是很好找了咯。

对比Swift写法:

extension NSAttributedString {
    init(string str: String)
}

如果可以传入一个nil值得参数,那么接口会变成这样:

extension NSAttributedString {
    init(string str: String?)
}

这种写法的优势在于更强的解释性,也省去了看文档的时间。更不会导致运行时错误。

建议

尽量避免无用选项关键字。用选项关键字是确实需要才使用的,比如你的参数是颜色,颜色需要符合某种要求。如果此时你的参数不满足这种要求那么你希望返回nil:

func parseColorFromHexString(input: String) -> UIColor? {
    // ...
}

枚举

枚举是swift得新特性,与OC的枚举有显著的不同。OC中的枚举说白了就是整型。

让我们考虑下布尔类型。我们只需要考虑真或假。
在选项关键字也是这样的原理,也只有两种情况:有值跟nil.swift中选项与布尔同样都用枚举定义。不同的是选项枚举是带绑定值得。我们来看下各自的定义:

enum Boolean {
    case False
    case True
}

enum Optional<A> {
    case Nil
    case Some(A)
}

二选一枚举经常在函数式编程中用于你在两件事物中进行选择的场景。举个栗子,如果你想返回一个整型或者错误,你可以使用Either<Int, NSError>。如果你要存一个布尔或者字符串的字典,你可以使用Either<Bool, String>作为Key值。

清楚的知道什么时候该用枚举什么时候不该用枚举有一点难度,当我们为一组相近类型得数据创建结构的时候。栗子,如果我们用swift来封装Github的接口,我们需要把各种暴露出的接口用枚举型来表示。为了获得用户头像,我们提供用户名。获取用户版本库我们提供了用户名跟,排序布尔值。

enum Github {
    case Zen
    case UserProfile(String)
    case Repositories(username: String, sortAscending: Bool)
}

定义开发性接口使用枚举真是棒极了。接口列表是受限的,我们可以定义各类接口。如果遗漏了某个枚举值我们会接受到一个告警。如果我们擅自添加了一类情况,我们需要更新所有用到该类枚举类型得代码。

其他使用枚举的时候不能随意添加case,除非有代码权限。这是一个非常好的限制。假想下你可以添加一个情形到Bool或者Optional,那么你所有用来它得方程都得重写。

让我们拿货币转换器做个栗子:

enum Currency {
    case Eur
    case Usd
}

这样我们可以根据货币字符串来获取货币符号:

func symbol(input: Currency) -> String {
    switch input {
        case .Eur: return "€"
        case .Usd: return "$"
    }
}

func format(amount: Double, currency: Currency) -> String {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .CurrencyStyle
    formatter.currencySymbol = symbol(currency)
    return formatter.stringFromNumber(amount)
}

在swift不能简单去继承父类来扩展枚举类型。需要通过用协议来实现枚举的扩展。

protocol CurrencySymbol {
    func symbol() -> String
}

现在,我们可以让Currency成为协议的一个实例,现在我们可以将input参数剔除,因为默认传入self

extension Currency : CurrencySymbol {
   func symbol() -> String {
        switch self {
            case .Eur: return "€"
            case .Usd: return "$"
        }
    }
}

重写format

func format(amount: Double, currency: CurrencySymbol) -> String {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .CurrencyStyle
    formatter.currencySymbol = currency.symbol()
    return formatter.stringFromNumber(amount)
}

现在我们可以通过让各种各样的数据类型来遵循这个协议的方式让我们的代码具有灵活的扩展性,比如现在有比特币类型:

struct Bitcoin : CurrencySymbol {
    func symbol() -> String {
        return "B⃦"
    }
}

现在通过协议而并非具体类的方式来应对各种数据类型参数值得方式可以使我们的代码更易于扩展。你可以使用便利的扩展搭配使用协议,你的代码将更具语言表达力!请根据你的实际情况决定你的忌口是开放还是封闭!

类型安全

swift还有一个优势在于它的类型(数据)安全,正如前面说到的optionals。我们可以将类型检查从运行时提到编译时通过一些技巧。举个数组的栗子,数组是泛型的,它可以容纳同一类型的元素。不可以在字符串数组内添加整型。(当然你可以通过Either来弄出一个混合数组。。 - -||)

又假设我们要将currency converter变成一个通用的转换器。如果我们用Double来表示总数,这样会导致一定的混淆。举个栗子,100.0可能意味着100美元,100千克,或者其他代表100的东东。我们要做的是让类型系统来为我们根据物理量创建出相对应得数据类型。举个栗子,我们可以定义这样的一个类型来描述货币:

struct Money {
    let amount : Double
    let currency: Currency
}

同样,我们可以定义质量的数据结构:

struct Mass {
    let kilograms: Double
}

这样可以增加代码的可读性,假设我们有如下重量方程的接口:

func pounds(input: Double) -> Double

这样显然太晦涩了,我们可以这么写:

func pounds(input: Mass) -> Double

这样写的好处有二,其一是代码解释性强了,第二编译器会帮我们进行类型检查。

不可变

另一个swift支持的特性是内置支持值不可变。在Cocoa框架里面有很多API的参数设置为不可变额。还有成双出现得一些类表达可变不可变(NSString vs. NSMutableString, NSArray vs. NSMutableArray)。在swift中就简单明了多了:
var修饰的变量可变而let修饰的变量不可变。好处是接口更具有表达性以及涉及多线程操作更为容易。

总结

令作者倍感欣慰的是编译器做了原来我们读文档需要的活儿。然后也预测了下未来。。。略。。


Cruise_Chan
729 声望71 粉丝

技能树点歪了...咋办