swift4.0和3
2017-12-06 11:53:00 weixin_34356138 阅读数 55

总结下,在升级过程中,遇到的问题,都是干货,不玩虚的。
这东西也不是一天两天就能完成的,慢慢来吧。(持续更新)
遇到啥,写啥,没有规律的。参考下就行了。

  1. 用xcode9,打开之前的项目,运用xcode自带的转换器,先转换一次,然后按照提示,一步步改对应文件

UIView.hidden -> UIView.isHidden

UIAlertView->UIAlertController
let alertController = UIAlertController(title: "提示", message: "我是内容", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: nil)
let okAction = UIAlertAction(title: "确认", style: .default, handler:{
(UIAlertAction) -> Void in
print("点击了确认")
})
// 当添加的UIAlertAction超过两个的时候,会自动变成纵向分布
alertController.addAction(cancelAction)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64((CGFloat(duration) + 0.5)) * NSEC_PER_SEC)), dispatch_get_main_queue(), {
()->Void in
print("啦拉拉")
});
-->
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) {
print("啦拉拉")
}
用起来超级简单啊!!!

CGRectMake --> CGRect(x: 0, y:0, width:0 height: 0)
CGRectGetMinX(frame) --> frame.minX
CGRectGetMinY(frame) --> frame.minY

  1. 关于 CocoaPods 第三方库升级,如果用到了 swift 语言的三方库,请打开 use_frameworks!

  2. 由于我们的项目是 OC + swift 混编,在升级的过程中遇到一个问题,我用pod导入 SnapKit 4.0.0,导入的时候,build,报错;
    直接拖拽文件夹,build,报错
    最终方法,下载完源码之后,拖拽 SnapKit.xcodeproj 项目到项目中,在导入
    SnapKit.framework,就能编译通过,并且能正常使用

749216-65c2aef5c8f4b88e.png
70134A2B-163F-4661-A2AF-4D7DA897A5CB.png
749216-57beff51875ff5a8.png
6021CF53-9ED9-4158-AD0B-A0A33401FB97.png
  1. 由于我们的项目是 OC + swift 混编,在OC中调用swift方法,变量时,记着添加 @objc 关键字

  2. 通知的写法改变了

NotificationCenter.default.addObserver(self, selector: #selector(notificationAction(notification:)), name: NSNotification.Name(rawValue: kNotificationFeedbackMessage), object: nil)

通知方法前边要加上 @objc
@objc func notificationAction(notification:NSNotification){}

6.对应的UICollectionView/UITableView的方法都要重写,不过可以查找替换,另外要实现UICollectionViewDelegateFlowLayout,要不对应的layout方法不走。
备注:如果方法名不能自动提示的话,可以通过官方文档查找到

7.要想适配X,需要用到安全区(safe area),它最低支持 iOS 9,binggo,很多API被废弃了,继续解决吧,亲们。0.0

升级完之后,要干嘛,当然是要适配了,请看 iphoneX 适配那些事

2018-01-20 02:44:00 weixin_33736649 阅读数 3
2620644-80987e982daf4bc8.png
Swift-4

最近完成了Swift 4.0的迁移,记录下迁移过程中遇到的坑

Agenda

Swift 4.0 简介
Swift 4.0 语法变化简介
Swift 4.0 Migration 流程
Swift 4.0 Migration 过程中遇到的“坑”

Swift 4.0 简介

Swift 4.0的目标由ABI(Application Binary Interface)稳定,变为了源码兼容,其中ABI的兼容和源码的兼容都代表什么意思呢?

  • ABI 兼容:Swift 3 预先编译出来的库,不用重新编译,也可以在 Swift 4 中正常链接
  • 源码兼容:Swift 3 编写的源码不用经过修改,就可以在 Swift 4 中正常编译

Swift 3.2,源码兼容,Swift 3.0 几乎不需要修改,只需重新编译,Swift 4.0 与 Swift 3.2 framework支持混编,可见Swift 4.0的兼容性还是比较高的

2620644-11619c9dde374dfa.png
Swift-Re-Learn

Swift 4.0 语法变化简介

2620644-3b314644bb80d25b.png
grammar-changes

Swift 4.0语法变化还是不少的,但是值得一提的大概是以下四个:

String

首先是String再次变成了一个Collection,Swift 1.0的时候,String是一个Collection类型,Swift 2.0将其变成了一个独有的String类型,Swift 4.0其再次变成了Collection类型

let greeting = "Hello, World!"
greeting.count
for char in greeting {
    print(char)
}

其中substring方法被弃用,现在我们使用集合的方式来查找子字符串,且子字符串现在是一个新类型即Substring

let greeting = "Hello, World!"
let comma = greeting.index(of: ",")!
let substring = greeting[..<comma]

private

以前我们总抱怨Swift中的Extension访问不到private的属性或方法,我们不得不使用fileprivate来解决这个问题,在Swift 4.0中,这个问题得到了解决,我们终于可以在Extension中访问到private的属性了。

Swift 3.x
class Demo {
  fileprivate let id: Int

  init(id: Int) {
    self.id = id
  }
}

extension Demo: Equatable {
  static func ==(lhs: Demo, rhs: Demo) -> Bool {
    return lhs.id == rhs.id
  }
}
Swift 4.0
class Demo {
  private let id: Int

  init(id: Int) {
    self.id = id
  }
}

extension Demo: Equatable {
  static func ==(lhs: Demo, rhs: Demo) -> Bool {
    return lhs.id == rhs.id
  }
}

支持组合Class和Protocol来定义变量

let demo: (Demo & DemoProtocol)?

@objc

最后也是最大的变化,@objc,Apple将Swift从默认与OC兼容改为了手动兼容,现在我们如果想在OC代码中调用Swift代码,我们需要手动在属性和方法前添加@objc关键字。如下:

@objc let demo: Demo!
@objc func doSomething()

苹果对此的解释是这减小了App的尺寸,举例的话即Apple自己的Music App减少了大约6%,但是我觉得,这可能以为着Apple打算分裂Swift和OC语言了,甚至Apple打算弃用Objective-C语言了。

Swift 4.0 Migration 流程

2620644-d699debbc38ead20.png
Swift

环境

Xcode 9

Xcode 9是Apple官方推出的IDE,自带Swift 4.0的编译环境。

Xcode 8.3

有些开发人员可能会进行不止一个项目的开发,可能会希望保留Xcode 8.3,这时我们也可以使用Xcode 8.3来开发Swift 4。其步骤如下:

  • 从 swift.org 下载最新的 Swift 4.0 snapshot
  • 运行安装程序
  • 在Xcode中,Xcode > Toolchains > Manage Toolchains… 然后选择 snapshot

流程

2620644-19f7fdef5c357990.png
migration-process

迁移流程如上图所示,具体可分为如下几步:

  • 从Build Settings中选择Swift版本
2620644-05cefb1b1181d73e.png
select-version
  • 使用内置工具完成迁移 Edit > Convert > To Current Swift Syntax…

[图片上传失败...(image-d7396a-1516387382216)]

  • 选择要迁移的Target
2620644-a1bdd3ff156e6121.png
select-target
  • 选择你希望迁移工具的迁移行为
2620644-e624b677bd7473af.png
select-convertion-mode
  • 编译你的代码

之后编译你的代码,你会看到如下图所示的警告,将其对应的方法添加@objc即可

2620644-86d2a146955db9dd.png
objc-warning

  • 在Build Settings中将Swift 3 @objc Inference设置为 Default
2620644-3cb9cb8d5572319b.png
swift-3-objc-inference

Swift 4.0 迁移过程中遇到的问题

2620644-43f324561aaccace.png
questions

@objc 带来的“坑”

虽然90%的@objc都会在编译期间被检查出来,但是仍然有一些我们无法在编译检查出来,就是运行时的错误。

if ([controller respondsToSelector:@selector(method_name)])
{
    [controller performSelector:@selector(method_name)];
}

上面这对代码问题在于,如果我们method_name没有加@objc,那么我们上面的if判断就会失败,并且没有警告或错误,你会发现你的功能直接失效了。

[demo setValue:value toKeyPath:path];

上面这对代码问题在于,我们Set keyPath的时候,如果被set的path对应的属性没有加@objc,那么我们调用setKeyPath就会造成App崩溃,这里同样不会有警告或错误,如果错误本身不是在主要功能中,很可能被我们忽略而造成线上Bug。

Cocoapods Swift3, Swift4 版本混编的问题

前文我们提到了Swift这个版本的源码兼容性很高,且支持Swift3.2和Swift4.0 framework的混编,然而...

2620644-ccba9de6732bb3cb.png
problems

我们会发现当我们将项目的Swift版本改为4.0之后,我们所有Cocoapods安装的依赖的Swift版本也变为了4.0,这导致我们完全浪费了Apple的Swift3.2和4.0 framework混编的优势,导致我们不得不等待我们所有依赖的库升级到Swift4.0之后才能升级我们自己的Target到Swift4.0,或者不得不用一个私有的Pod Module自己升级,增加了许多不必要的工作量,这里给大家分享一下我们项目上的解决方案。

Pod install hook

第一种是我们修改Pod install hook,来将我们希望的依赖的Swift版本手动改为Swift 3.2版本,如下面代码所示,我们将Quick改为了Swift3.2版本(Quick最为常用的测试库,从1.2.0版本开始支持Swift 4.0,之前版本为1.1.0不支持Swift4.0)。

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      if config.build_settings['PRODUCT_NAME'] == 'Quick'
        config.build_settings['SWIFT_VERSION'] = 3.2
      end
    end
  end
end
xcconfig文件

我们可以将Swift的版本写入xcconfig文件中,删除Build Settings里面的设置,这样也可以让我们的Pod module的Swift版本设置为3.2版本

如果我们使用的库连3.2都编译不过,或者说使用的库Swift版本低于3.0(因为Swift 3的代码不许要修改就可以用Swift3.2的编译器编译),那么我建议我们应该暂停升级Swift4.0,先将这个库替换成在持续更新的库。

总结

总得来说,这次版本迁移并不像Swift前几个版本那样困难,想对兼容性也比较好。本文介绍了Swift 4.0的语法变化,例如@objcprivate的问题,之后讨论了迁移的基本流程,最后讨论了我遇到的一些"坑",例如@objcCocoapods带来的问题。现在我们该开始期待Swift 5.0了,希望下个版本可以做到ABI稳定。

转载于:https://www.jianshu.com/p/185e1ab062fb

2017-06-23 09:49:00 weixin_34007886 阅读数 20

笔者这两天运用所学到的swift相关东西,写了一个swift版本的轮播图,
一是为了工程需要,
二是对swift进行加深了解,探究一下swift中面向函数编程和面试协议编程。

两种方法

    /// 创建图片和文字轮播图
    ///
    /// - Parameters:
    ///   - frame: 轮播图位置
    ///   - delegate: SCCycleScrollView代理
    ///   - imageArray: 图片数据源,默认值nil
    ///   - titleArray: 文字数据源,默认值[]
    ///   - placeholderImage: 占位图片
    /// - Returns: 返回SCCycleScrollView对象
    open class func cycleScrollView(
         frame: CGRect, delegate: SCCycleScrollViewDelegate?,
         imageArray: [AnyObject]? = nil, 
         titleArray: [String]? = [], 
         placeholderImage: UIImage?
         ) -> SCCycleScrollView
    /// 创建文字轮播图
    ///
    /// - Parameters:
    ///   - frame: 轮播图位置
    ///   - delegate: SCCycleScrollView代理
    ///   - titleArray: 文字数据源,默认值[]
    /// - Returns: 返回SCCycleScrollView对象
    open class func cycleScrollView(
         frame: CGRect, 
         delegate: SCCycleScrollViewDelegate, 
         titleArray: [String]? = []
         ) 
         -> SCCycleScrollView

多种属性

//>>>>>>>>>>>>>>>>>>>>>>  SCCycleScrollView属性接口 >>>>>>>>>>>>>>>>>>>>>>>>>>>
    
    /// SCCycleScrollView代理
    weak open var delegate: SCCycleScrollViewDelegate?
    
    /// 轮播图滚动方向,默认水平
    open var scrollDirection: UICollectionViewScrollDirection = .horizontal
    
    /// 定时时间间隔,默认1.0秒
    open var timeInterval: CGFloat = 1.0
    
    //>>>>>>>>>>>>>>>>>>>>>>  title属性接口 >>>>>>>>>>>>>>>>>>>>>>>>>>>
    
    /// 文字颜色,默认非纯白(在图片类型和文字类型中通用)
    open var titleColor: UIColor = UIColor(red: 0xe5 / 255.0, green: 0xf0 / 255.0, blue: 0xf4 / 255.0, alpha: 1.0)
    
    /// 文字大小,默认18(在图片类型和文字类型中通用)
    open var titleFont: UIFont = UIFont.systemFont(ofSize: 18)
    
    /// 文字左侧边距,默认10
    open var titleLeftMargin: CGFloat = 10
    
    /// 文字背景条颜色,默认黑色
    open var titleContainerBackgroundColor: UIColor? = UIColor.black
    
    /// 文字背景条透明度,默认0.6
    open var titleContainerAlpha: CGFloat = 0.6
    
    //>>>>>>>>>>>>>>>>>>>>>>  pageControl属性接口 >>>>>>>>>>>>>>>>>>>>>>>>>>>
    
    /// 当前指示器颜色,默认白色
    open var currentPageIndicatorTintColor: UIColor = UIColor.white
    
    /// 非选中指示器颜色,默认亮灰色
    open var pageIndicatorTintColor: UIColor = UIColor.lightGray
    
    /// pageControl底部边距,默认0
    open var pageControlBottomMargin: CGFloat = 0
    
    /// pageControl右侧边距,默认0
    open var pageControlRightMargin: CGFloat = 0
    
    /// 只有一张图片时是否隐藏pageControl,默认隐藏
    open var isHiddenOnlyPage: Bool = true
    
    /// 外界获取pageControl的size(只读)
    open var pageControlSize: CGSize

    /// cell类型:图片类型和文字类型
    open var cellType: CycleScrollViewCellType?

    //>>>>>>>>>>>>>>>>>>>>>>  数据源 >>>>>>>>>>>>>>>>>>>>>>>>>>>
    
    /// 图片数据源
    open var imageArray: [AnyObject]?
    /// 文字数据源
    open var titleArray: [String]? 

演示图片

1846524-7fc2f68ff126a507.gif
轮播图演示

Github

SCCycleScrollView轮播图

有什么不好的地方还请多多指教 , 发现bug希望您能反馈一下

QQ: 767616124

Email: s787753577@163.com

2018-01-29 09:06:00 weixin_33814685 阅读数 8

泛型


import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
       pringElemtFormArr(a: stringArr)
        pringElemtFormArr(a: intArr)
        pringElemtFormArr(a: doubleArr)
        
        
        donoting(x: 123)
        donoting(x: "123")
        donoting(x: true)
        
        
    }

    var stringArr = ["hi","hello","bye"]
    var intArr = [1,2,3]
    var doubleArr = [1.1,2.2,3.3]

    //普通方法
    func printAreingArr(a:[String]) {
        for s in a {
            print(s)
        }
    }

    func pringIntArr(a:[Int]) {
        for i in a {
            print(i)
        }
    }
    
    
    //泛型的方法 T代表任意类型
    func pringElemtFormArr<T>(a:[T]) {
        for element in a {
            print(element)
        }
    }
    func donoting<T>(x:T) -> T {
        return x
    }
}

Swift中mutating关键字

Swift中protocol的功能比OC中强大很多,不仅能再class中实现,同时也适用于struct、enum。
使用 mutating 关键字修饰方法是为了能在该方法中修改 struct 或是 enum 的变量,在设计接口的时候,也要考虑到使用者程序的扩展性。所以要多考虑使用mutating来修饰方法。


import UIKit

struct GenericArr<T> {
    var items = [T]()  //创建数组
    
    mutating func push(item:T) {
        items.append(item)
    }
}
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
       var myPhone = ["ipone5","iphone6","iphone7"]
        var LisaArr = GenericArr(items: myPhone)
        LisaArr.push(item: "iphon7 plus")
        print( LisaArr.items)
    }

}


2017-08-15 11:23:18 xiangzhihong8 阅读数 2595

WWDC 2017 带来了很多惊喜,在这次大会上,Swift 4 也伴随着 Xcode 9 测试版来到了我们的面前,虽然正式版要8月底9月初才会公布,但很多强大的新特性正吸引我们去学习它。根据大会上已经开放的新特性,先一睹为快。

体验

Swift 4包含在Xcode 9中,您可以从Apple的开发者门户下载最新版本的Xcode 9(您必须拥有一个活跃的开发者帐户)。 每个Xcode测试版将在发布时捆绑最新的Swift 4快照。在阅读时,您会注意到[SE-xxxx]格式的链接。 这些链接将带您到相关的Swift Evolution提案。 如果您想了解有关任何主题的更多信息,请务必查看。

版本迁移

由于Swift 4新增了很多的新的语法特性,这些语法和思想完全区别于Swift 3及以下版本。因此,使用Swift迁移工具将为您处理大部分更改,在Xcode中,您可以导航到编辑/转换/到当前Swift语法…以启动转换工具。

语法改进

extension 中可以访问 private 的属性

例如有如下代码:

struct Date: Equatable, Comparable {
    private let secondsSinceReferenceDate: Double
    static func ==(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
    }
    static func <(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
    }
}

上面代码定义了一个 Date 结构体,并实现 Equatable 和 Comparable 协议。为了让代码更清晰,可读性更好,一般会把对协议的实现放在单独的 extension 中,这也是一种非常符合 Swift 风格的写法。

struct Date {
    private let secondsSinceReferenceDate: Double
}
extension Date: Equatable {
    static func ==(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
    }
}
extension Date: Comparable {
    static func <(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
    }
}

但是在 Swift 3 中,编译就报错了,因为 extension 中无法获取到 secondsSinceReferenceDate 属性,因为它是 private 的。所以,在 Swift 3 中必须把 private 改为 fileprivate。但是如果用 fileprivate,属性的作用域就会更大,可能会不小心造成属性的滥用。

struct Date {
    fileprivate let secondsSinceReferenceDate: Double
}
...

而在 Swift 4 中,private 的属性的作用域扩大到了 extension 中,并且被限定在了 struct 和 extension 内部,这样就不需要再改成 fileprivate 了。

类型和协议的组合类型

考虑以下如下代码:

protocol Shakeable {
    func shake()
}

extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }

func shakeEm(controls: [???]) {
    for control in controls where control.state.isEnabled {
    }
    control.shake()
}

???处怎么写呢?在Swift 3中可以这么写。

func shakeEm(controls: [UIControl]) {
    for control in controls where control.isEnabled {
        if control is Shakeable {
            (control as! Shakeable).shake()
        }
    }
}

在Swift 4中,如果将类型和协议用 & 组合在一起使用,代码就可以这么写了。

protocol Shakeable {
    func shake()
}

extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }

func shakeEm(controls: [UIControl & Shakeable]) {
    for control in controls where control.state.isEnabled {
        control.shake()
    }// Objective-C API
@interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem
@property (nullable, weak) NSView <NSTextInputClient> *client;
@end
}

Associated Type 追加Where 约束语句

在 Swift 4 中可以在 associated type 后面声明的类型后追加 where 语句,其语法格式如下:

associatedtype Element where <xxx>

下面是 Swift 4 标准库中 Sequence 中 Element 的声明:

protocol Sequence {
    associatedtype Element where Self.Element == Self.Iterator.Element
    // ...
}

它限定了 Sequence 中 Element 这个类型必须和 Iterator.Element 的类型一致。通过 where 语句可以对类型添加更多的约束,使其更严谨,避免在使用这个类型时做多余的类型判断。

Key Paths 语法

先来看看Swift 3的Key Paths语法:

@objcMembers class Kid: NSObject {
    dynamic var nickname: String = ""
    dynamic var age: Double = 0.0
    dynamic var friends: [Kid] = []
}

var ben = Kid(nickname: "Benji", age: 5.5)

let kidsNameKeyPath = #keyPath(Kid.nickname)

let name = ben.valueForKeyPath(kidsNameKeyPath)
ben.setValue("Ben", forKeyPath: kidsNameKeyPath)

在Swift 4中上面的代码可以这样写:

struct Kid {
    var nickname: String = ""
    var age: Double = 0.0
    var friends: [Kid] = []
}

var ben = Kid(nickname: "Benji", age: 8, friends: [])

let name = ben[keyPath: \Kid.nickname]
ben[keyPath: \Kid.nickname] = "BigBen"

相比 Swift 3,Swift 4 的 Key Paths 具有以下优势:

  1. 类型可以定义为 class、struct;
  2. 定义类型时无需加上 @objcMembers、dynamic 等关键字;
  3. 性能更好;
  4. 类型安全和类型推断,例如 ben.valueForKeyPath(kidsNameKeyPath) 返回的类型是
    Any,ben[keyPath: \Kid.nickname] 直接返回 String 类型;
  5. 可以在所有值类型上使用;

下标支持泛型

Swift 支持通过下标来读写容器中的数据,但是如果容器类中的数据类型定义为泛型,以前的下标语法就只能返回 Any,在取出值后需要用 as? 来转换类型。在Swift 4中,下标也可以使用泛型。

struct GenericDictionary<Key: Hashable, Value> {
    private var data: [Key: Value]

    init(data: [Key: Value]) {
        self.data = data
    }

    subscript<T>(key: Key) -> T? {
        return data[key] as? T
    }
}

let dictionary = GenericDictionary(data: ["Name": "Xiaoming"])

let name: String? = dictionary["Name"] // 不需要再写 as? String

字符串

Unicode 字符串

在 Unicode 中,有些字符是由几个其它字符组成的,比如 é 这个字符,它可以用 \u{E9} 来表示,也可以用 e 字符和上面一撇字符组合在一起表示 \u{65}\u{301}。例如:

这里写图片描述
这个 family 是一个由多个字符组合成的字符,打印出来的结果为 一个家庭。上面的代码在 Swift 3 中打印的 count 数是 4,在 Swift 4 中打印出的 count 是 1。

更快的字符处理速度

Swift 4 的字符串优化了底层实现,对于英语、法语、德语、西班牙语的处理速度提高了 3.5 倍。对于简体中文、日语的处理速度提高了 2.5 倍。

去掉了 characters

Swift 3 中的 String 需要通过 characters 去调用的属性方法,在 Swift 4 中可以通过 String 对象本身直接调用,例如:

let values = "one,two,three..."
var i = values.characters.startIndex

while let comma = values.characters[i...<values.characters.endIndex].index(of: ",") {
    if values.characters[i..<comma] == "two" {
        print("found it!")
    }
    i = values.characters.index(after: comma)
}

在Swift 4 可以把上面代码中的所有的 characters 都去掉:

let values = "one,two,three..."
var i = values.startIndex

while let comma = values[i...<values.endIndex].index(of: ",") {
    if values[i..<comma] == "two" {
        print("found it!")
    }
    i = values.index(after: comma)
}

One-sided Slicing

Swift 4 新增了一个语法糖 … 可以对字符串进行单侧边界取子串。例如:

let values = "abcdefg"
let startSlicingIndex = values.index(values.startIndex, offsetBy: 3)
let subvalues = values[startSlicingIndex...] // One-sided Slicing
// defg

将String 当做 Collection 来用

Swift 4 中 String 可以当做 Collection 来用,并不是因为 String 实现了 Collection 协议,而是 String 本身增加了很多 Collection 协议中的方法,使得 String 在使用时看上去就是个 Collection。例如:
翻转字符串

let abc: String = "abc"
print(String(abc.reversed()))
// cba

遍历字符

let abc: String = "abc"
for c in abc {
    print(c)
}
/*
a
b
c
*/

Map、Filter、Reduce

// map
let abc: String = "abc"
_ = abc.map {
    print($0.description)
}

// filter
let filtered = abc.filter { $0 == "b" }

// reduce
let result = abc.reduce("1") { (result, c) -> String in
    print(result)
    print(c)
    return result + String(c)
}
print(result)

Substring

这里写图片描述

在 Swift 中,String 的背后有个 Owner Object 来跟踪和管理这个 String,String 对象在内存中的存储由内存其实地址、字符数、指向 Owner Object 指针组成。Owner Object 指针指向 Owner Object 对象,Owner Object 对象持有 String Buffer。当对 String 做取子字符串操作时,子字符串的 Owner Object 指针会和原字符串指向同一个对象,因此子字符串的 Owner Object 会持有原 String 的 Buffer。当原字符串销毁时,由于原字符串的 Buffer 被子字符串的 Owner Object 持有了,原字符串 Buffer 并不会释放,造成极大的内存浪费。
在 Swift 4 中,做取子串操作的结果是一个 Substring 类型,它无法直接赋值给需要 String 类型的地方。必须用 String() 包一层,系统会通过复制创建出一个新的字符串对象,这样原字符串在销毁时,原字符串的 Buffer 就可以完全释放了。例如:

let big = downloadHugeString()
let small = extractTinyString(from: big)

mainView.titleLabel.text = small // Swift 4 编译报错

mainView.titleLabel.text = String(small) // 编译通过

多行字符串字面量

Swift 3 中写很长的字符串只能写在一行。

func tellJoke(name: String, character: Character) {
    let punchline = name.filter { $0 != character }
    let n = name.count - punchline.count
    let joke = "Q: Why does \(name) have \(n) \(character)'s in their name?\nA: I don't know, why does \(name) have \(n) \(character)'s in their name?\nQ: Because otherwise they'd be called \(punchline)."
    print(joke)
}
tellJoke(name: "Edward Woodward", character: "d")

字符串中间有换行只能通过添加 \n 字符来代表换行。Swift 4 可以把字符串写在一对 “”” 中,这样字符串就可以写成多行。

func tellJoke(name: String, character: Character) {
    let punchline = name.filter { $0 != character }
    let n = name.count - punchline.count
    let joke = """
        Q: Why does \(name) have \(n) \(character)'s in their name?
        A: I don't know, why does \(name) have \(n) \(character)'s in their name?
        Q: Because otherwise they'd be called \(punchline).
        """
    print(joke)
}
tellJoke(name: "Edward Woodward", character: "d")

Swift 标准库

Encoding and Decoding

当需要将一个对象持久化时,需要把这个对象序列化,往常的做法是实现 NSCoding 协议,写过的人应该都知道实现 NSCoding 协议的代码写起来很痛苦,尤其是当属性非常多的时候。Swift 4 中引入了 Codable 帮我们解决了这个问题,这和Java等面向对象语言有异曲同工之妙。例如:

struct Language: Codable {
    var name: String
    var version: Int
}

想让这个 Language 对象的实例持久化,只需要让 Language 符合 Codable 协议即可,Language 中不用写别的代码。符合了 Codable 协议以后,可以选择把对象 encode 成 JSON 或者 PropertyList。

Encode操作

let swift = Language(name: "Swift", version: 4)
if let encoded = try? JSONEncoder().encode(swift) {
    // 把 encoded 保存起来
}

Decode操作

if let decoded = try? JSONDecoder().decode(Language.self, from: encoded) {
    print(decoded.name)
}

Sequence

在Swift 3中,

protocol Sequence {
    associatedtype Iterator: IteratorProtocol
    func makeIterator() -> Iterator
}

由于 Swift 4 中的 associatedtype 支持追加 where 语句,所以 Sequence 做了这样的改进。

protocol Sequence {
    associatedtype Element
    associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
    func makeIterator() -> Iterator
}

Swift 4 中获取 Sequence 的元素类型可以不用 Iterator.Element,而是直接取 Element。例如:

protocol Sequence {
    associatedtype SubSequence: Sequence 
        where SubSequence.SubSequence == SubSequence,
              SubSequence.Element == Element
}

通过 where 语句的限定,保证了类型正确,避免在使用 Sequence 时做一些不必要的类型判断。

Protocol-oriented integers

整数类型的协议也做了修改,新增了 FixedWidthInteger 等协议,具体的协议继承关系如下:
这里写图片描述

Dictionary and Set enhancements

这里简单的罗列了Dictionary 和 Set 增强的功能点:

  • 通过 Sequence 来初始化;
  • 可以包含重复的 Key
  • Filter 的结果的类型和原类型一致
  • Dictionary 的 mapValues 方法
  • Dictionary 的默认值
  • Dictionary 可以分组
  • Dictionary 可以翻转

NSNumber

在 Swift 4 中,把一个值为 999 的 NSNumber 转换为 UInt8 后,能正确的返回 nil,而在 Swift 3 中会不可预料的返回 231。

let n = NSNumber(value: 999)
let v = n as? UInt8 // Swift 4: nil, Swift 3: 231

MutableCollection.swapAt(::)

MutableCollection 现在有了一个新方法 swapAt(::) 用来交换两个位置的值,例如:

var mutableArray = [1, 2, 3, 4]
mutableArray.swapAt(1, 2)
print(mutableArray)
// 打印结果:[1, 3, 2, 4]

Xcode改进

New Build System

Xcode 9 引入了 New Build System,可在 Xcode 9 的 File -> Project Settings… 中选择开启。
这里写图片描述

预编译 Bridging Headers 文件

对于 Swift 和 Objective-C 混合的项目,Swift 调用 Objective-C 时,需要建立一个 Bridging Headers 文件,然后把 Swift 要调用的 Objective-C 类的头文件都写在里面,编译器会读取 Bridging Headers 中的头文件,然后生成一个庞大的 Swift 文件,文件内容是这些头文件内的 API 的 Swift 版本。然后编译器会在编译每一个 Swift 文件时,都要编译一遍这个庞大的 Swift 文件的内容。

有了预编译 Bridging Headers 以后,编译器会在预编译阶段把 Bridging Headers 编译一次,然后插入到每个 Swift 文件中,这样就大大提高了编译速度(苹果宣称 Xcode 9 和 Swift 4 对于 Swift 和 Objective-C 混合编译的速度提高了 40%)。

COW Existential Containers

Swift 中有个东西叫 Existential Containers,它用来保存未知类型的值,它的内部是一个 Inline value buffer,如果 Inline value buffer 中的值占用空间很大时,这个值会被分配在堆上,然而在堆上分配内存是一个性能比较慢的操作。

Swift 4 中为了优化性能引入了 COW Existential Containers,这里的 COW 就代表 “Copy-On-Write”,当存在多个相同的值时,他们会共用 buffer 上的空间,直到某个值被修改时,这个被修改的值才会被拷贝一份并分配内存空间。

移除未调用的协议实现

struct Date {
    private let secondsSinceReferenceDate: Double
}

extension Date: Equatable {
    static func ==(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
    }
}

extension Date: Comparable {
    static func <(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
    }
}

例如,上面的代码中,Date 实现了 Equatable 和 Comparable 协议。编译时如果编译器发现没有任何地方调用了对 Date 进行大小比较的方法,编译器会移除 Comparable 协议的实现,来达到减小包大小的目的。

减少隐式 @objc 自动推断

在项目中想把 Swift 写的 API 暴露给 Objective-C 调用,需要增加 @objc。在 Swift 3 中,编译器会在很多地方为我们隐式的加上 @objc,例如当一个类继承于 NSObject,那么这个类的所有方法都会被隐式的加上 @objc。这样很多并不需要暴露给 Objective-C 也被加上了 @objc。大量 @objc 会导致二进制文件大小的增加。

class MyClass: NSObject {
    func print() { ... } // 包含隐式的 @objc
    func show() { ... } // 包含隐式的 @objc
}

在 Swift 4 中,隐式 @objc 自动推断只会发生在很少的当必须要使用 @objc 的情况,比如:

  1. 复写父类的 Objective-C 方法
  2. 符合一个 Objective-C 的协议

其它大多数地方必须手工显示的加上 @objc。减少了隐式 @objc 自动推断后,Apple Music app 的包大小减少了 5.7%。

兼容

Xcode 9 中同时集成了 Swift 3.2 和 Swift 4。

  1. Swift 3.2 完全兼容 Swift 3.1,并会在过时的语法或函数上报告警告。
  2. Swift 3.2 具有 Swift 4 的一些写法,但是性能不如 Swift 4。
  3. Swift 3.2 和 Swift 4 可以混合编译,可以指定一部分模块用 Swift 3.2 编译,一部分用 Swift 4 编译。
  4. 迁移到 Swift 4 后能获得 Swift 4 所有的新特性,并且性能比 Swift 3.2 好。

当 Xcode 正式版发布后,现有的 Swift 代码可以直接升级到 Swift 3.2 而不用做任何改动,后续可以再迁移到 Swift 4。或者直接迁移到 Swift 4 也可以,Swift 4 相比 Swift 3 的 API 变化还是不大的,很多第三方库都可以直接用 Swift 4 编译。Swift 1 到 2 和 Swift 2 到 3 的迁移的痛苦在 3 到 4 的迁移上已经大大改善了。

参考资料:

Swift 4.0 按钮

阅读数 50

Swift 4.0 新特性

阅读数 7

Swift 4.0 轮播图

阅读数 228

swift轮播图

博文 来自: TSC1235

Swift3.3 -> Swift 4.0

阅读数 22

swift 4.0 集合

阅读数 10

没有更多推荐了,返回首页