YongSir

专业程序员伪装者

swift - tip002(later v2.2)

swift tips

可以预见今年的WWDC我们会迎来Swift3.0,刚刚发布的2.2版本,已经在逐渐提示有些应该会改动的地方了,根据自己写Demo的过程逐渐记录了一些:

  • for 循环的改动
    不在完全C一样的风格了,这一点也可以看到swift的现代之处了,摒弃沉重的历史包袱,所以大胆的拥抱吧,eg:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 数据遍历 - 推荐使用enumerate()
    for (index, item) in items.enumerate() {
    ...
    }

    // 更加灵活的指定间隔,使用stride
    for index int 2.stride(to: items.count, by: 2) {
    print(index) // 2, 4, 6 ...
    }

    // 降序 - reverse()
    for index in (0...100).reverse() {
    print(index) // 100, 99, 98, 97 ... 0
    }

但是,像复杂一些的多个组合逻辑的for,比如for(int i=0; i < sum && i / 2 == 0; ++i)这样,如何合适的更改,现在还没找到合适的方法,目前我用的是while + if

  • OC中的__FUNCTION__ 变为 #function, 不过这一累=类的变动Xcode都会提示你fix it
  • ++ and – 终于 deprecated 了: 话说这样的语意不明的语法早TM该废了,果然没有包袱就是好,swift3中要使用:

    1
    i += 1
  • 老古董Selector变动
    从”functionName” >>> #selector(XXX.func)

    1
    selectBtn.addTarget(self, action: #selector(ViewController.selectPhoto), forControlEvents: .TouchUpInside)
  • guard语法

    其实这个语法早在2.0版本就有了,但一直没有仔细使用,粗浅的涉猎并没有说服自己,直到3.0还保留看来是时候接受了,即便不使用的话完全也没什么影响,swift提供这种微妙的语法表达,很契合其简单明确的语言风格同时也保证了足够的健壮性

    是否真的是这样呢?如需要将string转化为UInt8UInt8本身已经实现了一个可接受string的初始化方法并且可以抛出错误,但并还需要足够的提示来处理上下文我们不能预知的问题,比如格式错误,或者超出了数值边界,所以实现一个新的Init(fromString string: String)可以抛出一个能够提供更具体错误信息的ConversionError

    Tip: guard并不意味着要替换所有的if..else 和 if let,切忌滥用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建一个字符串转化为UInt8的初始化方法,并可抛出三种Error
enum ConversionError: ErrorType {
case InvalidFormat, OutOfBounds, Unknown
}

extension UInt8 {
init(fromString string: String) throws {
if let _ = string.rangeOfString("^\\d+$", options: [.RegularExpressionSearch]) {

if string.compare("\(UInt8.max)", options: [.NumericSearch]) != NSComparisonResult.OrderedAscending {
throw ConversionError.OutOfBounds
}
if let value = UInt8(string) {
self.init(value)
} else {
throw ConversionError.Unknown
}
}
throw ConversionError.InvalidFormat
}

上边是传统的写法,我们明明只是补充一下3种可抛出错误,但这段代码很不容易看出来,至少一眼是看不出来的,如果用上了guard,效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Unit8 {

init (fromString string: String) throws {

guard let _ = string.rangeOfString("^\\d+$", options: [.RegularExpressionSearch])
else { throw ConversionError.InvalidFormat }

guard string.compare("\(UInt8.max)", options: [.NumericSearch]) != NSComparisonResult.OrderedAscending
else { throw ConversionError.OutOfBounds }

guard let value = UInt(string)
else { throw ConversionError.Unknown }

self.init(value)
}
}

同样是想表达判断throw指定异常,使用guard语句比传统的明确很多,甚至到了一眼看出的程度

这里也能看到,原则是判断在前,最后执行self.init(value)

自swift变量的设计Optional这一特性之初,从语言层面上虽说是现代的严谨的,但其实在很多时候增加了我们的代码量,常常时不时的需要嵌套的if let才能保证后面对变量的强制解包的有效性,特别是在遇到复杂的Optional chain情况下。

在刚刚学习的很长一段时间内都是靠着编译器的提示来过日子,还常常出现各种crush,这一点也算得上是入门swift 的最大门槛了,但总学if letswift本身简洁的特质不符,好在终于有guard帮我们分担了

guard对我来说的另一个好处是,它不用让我随时操心if逻辑不全的问题了,很多时候我在写的代码时总是自以为是的认为逻辑清晰,只写if而不是保证if .. else成对出现,这在逻辑复杂时或者嵌入式开发中迟早会遇到大的问题,但平白的写很多个else { }总是显得不那么美观,感谢有了guard🙏

比如在一个典型的Alamofire请求之中:

1
2
3
4
5
6
guard response.result.isSuccess
else {
let alert = UIAlertView(title: "网络异常", message: "请检查设备网络设置", delegate: nil, cancelButtonTitle: "确定")
alert.show()
return
}

👆的写法有待商榷,实际上很多人都不建议这样去写,else里面应该很简单,其次格式也不一定要这样,但是目前来看个人还是很认同这种规范的

  • SupplementaryView的使用注意
  1. register要指定kind,自定义UICollectionReusableView
  2. dataSource代理中装填数据:
    使用switch区分装填

    1
    func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView
  3. 最重要的layout中要设置headerReferenceSize,否则代理方法not be called

  4. 最大的一个坑,再给supplyView添加subView时,使用bounds而不能是frame,否则就会出现所谓drop的现象

    1
    2
    3
    4
    5
    override init(frame: CGRect) {
    super.init(frame: frame)
    label.frame = bounds // 而不是frame
    addSubview(label)
    }
  • 接上舒,还有几个不常用的方法,是作为cell滑动以及加动画场合的使用
    1
    2
    3
    4
    5
    6
    7
    public func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath)

    public func collectionView(collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, atIndexPath indexPath: NSIndexPath)

    public func collectionView(collectionView: UICollectionView, didEndDisplayingCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath)

    public func collectionView(collectionView: UICollectionView, didEndDisplayingSupplementaryView view: UICollectionReusableView, forElementOfKind elementKind: String, atIndexPath indexPath: NSIndexPath)

想不起来就去文档中看看,这里记下来一次