2018-06-20 14:49:13 qq_37269542 阅读数 669

最近因为项目需求需要对项目代码进行升级,从之前的swift2.0版本升级到swift4.1版本。现将升级过程中遇到的一些语法变化与大家分享一下,希望会对大家有所帮助,

Swift 2.0 --> Swift 4.0

1.self.edgesForExtendedLayout = UIRectEdge.None  -->  self.edgesForExtendedLayout = UIRectEdge.init(rawValue: 0)

2.UINavigationBar.appearance().titleTextAttributes=NSDictionary(object:UIColor.whiteColor(), forKey:NSForegroundColorAttributeName) as? [String : AnyObject]  -->  UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
[NSForegroundColorAttributeName: UIColor.white]  -->  [NSAttributedStringKey.foregroundColor : UIColor.white]

3.使用Alamofire获取statusCode: res.result.error?.code  -->  res.response?.statusCode

4.CGRectZero  -->  CGRect.zero

5.从其他线程回到主线程的方法:
   dispatch_async(dispatch_get_main_queue(), { () -> Void in 

   })		--> 

   DispatchQueue.main.async {

   }

 6.	UILabel获取字体大小的属性boundingRectWithSize的改变:
 	let size = body.boundingRectWithSize(CGSizeMake(CGFloat(w-20), CGFloat(10000.0)),options:NSStringDrawingOptions.UsesLineFragmentOrigin,  attributes: [NSFontAttributeName:UIFont.systemFontOfSize(12)],context:nil)     -->
 	let size = body.boundingRect(with: CGSize.init(width: CGFloat(Constants.SCREEN_W - 20), height: CGFloat(10000.0)),options:NSStringDrawingOptions.usesLineFragmentOrigin,  attributes:[NSAttributedStringKey.font : UIFont.systemFont(ofSize: 12)],context:nil)

 7.let myMutableString = NSMutableAttributedString(string: (!body.isEmpty ? body : "") , attributes: [NSFontAttributeName:UIFont(name: "HelveticaNeue", size: 12.0)!])	 -->  
   let myMutableString = NSMutableAttributedString.init(string: (!body.isEmpty ? body : ""), attributes: [NSAttributedStringKey.font : UIFont(name: "HelveticaNeue", size: 12.0)!])
 
 8.let paragraphStyle = NSMutableParagraphStyle()
 	myMutableString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle,range: NSRange(location:0,length:myMutableString.length))   -->
 	myMutableString.addAttribute(NSAttributedStringKey.paragraphStyle, value: paragraphStyle, range: NSRange.init(location: 0, length: myMutableString.length))

 9.SDWebImage的用法改变:
   headImg.sd_setImageWithURL(NSURL(string: self.images[indexPath.row] as! String), placeholderImage: UIImage(named: "default_logo"))     -->   
   headImg.sd_setImage(with: NSURL.init(string: self.images[indexPath.row] as! String)! as URL, placeholderImage: UIImage(named: "default_logo"), options: .retryFailed, completed: nil)

10. UIApplication.sharedApplication().keyWindow?.windowLevel = UIWindowLevelStatusBar    -->   UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelStatusBar

11. NSCalendarUnit.Day  -->   NSCalendar.Unit.day

12. var components = cal.components([NSCalendar.Unit.day, NSCalendar.Unit.month, NSCalendar.Unit.year, NSCalendar.Unit.weekOfYear, NSCalendar.Unit.hour, NSCalendar.Unit.minute, NSCalendar.Unit.second, NSCalendar.Unit.nanosecond], from: NSDate() as Date)
	components.setValue(components.month-3, forComponent: NSCalendar.Unit.month)  -->  components.setValue(components.month! - 3, for: .month)

13. formatter.stringFromDate(cal.dateFromComponents(components)!)  -->  formatter.string(from: cal.date(from: components)!)

14. string.characters.count  -->  string.count

15. string.stringByReplacingOccurrencesOfString(" ", withString: "")	-->	   string.replacingOccurrences(of: " ", with: "")

16. for i in str.characters {
	
	}         -->     
	for i in str {

	}

17. str.removeRange(index!)  -->   str.removeSubrange(index!)

18. let alertInputTelController = UIAlertController(title: title, message: "", preferredStyle: UIAlertControllerStyle.alert)
	alertInputTelController.addTextFieldWithConfigurationHandler {

	}	  -->   
	alertInputTelController.addTextField {

	}

19. addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSRange.init(location: startIndex!, length: 1))   -->  
	attrString.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange.init(location: startIndex!, length: 1))

20. tf.text?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) != ""   --> 
	tf.text?.trimmingCharacters(in: NSCharacterSet.whitespaces) != ""     //检测textField中输入的字符去掉空格后是否为空的方法

21. kNewStoreInfo.setObject(tf.text!, forKey: key)   --> 
	kNewStoreInfo.setObject(tf.text!, forKey: key as NSCopying)

22. textView.text.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())    -->   
	textView.text.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)

23. //将内容同步写到文件中去(Caches文件夹下)
        let cachePath = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: .UserDomainMask)[0]
        let path = cachePath.URLByAppendingPathComponent("log.txt")
        appendText(path, string: "\(datestr) \(consoleStr)")     

        --> 

        let cachePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let path = cachePath.appendingPathComponent("log.txt")
        appendText(fileURL: path as NSURL, string: "\(datestr) \(consoleStr)")

24. FileManager.defaultManager().createFileAtPath(fileURL.path!, contents: nil, attributes: nil)  -->  
	FileManager.default.createFile(atPath: fileURL.path!, contents: nil, attributes: nil)

25. NSFileHandle.init(forWritingAtPath: fileURL.path!)   -->
	FileHandle.init(forWritingAtPath: fileURL.path!)

26. //找到末尾位置并添加
    fileHandle!.seekToEndOfFile()        
    fileHandle?.writeData(stringToWrite.dataUsingEncoding(NSUTF8StringEncoding)!)  -->
    fileHandle?.write(stringToWrite.data(using: String.Encoding.utf8)!)

27. string.componentsSeparatedByString(",")  -->   string.components(separatedBy: ",")

28. NSIndexPath(forRow: i, inSection: 0)  -->   NSIndexPath(row: i, section: 0)

29. NSComparisonResult.OrderedAscending  -->  ComparisonResult.orderedAscending

30. NSNotificationCenter.defaultCenter().postNotificationName("Notification_UploadImage", object: j)  -->
	NotificationCenter.default.post(name: NSNotification.Name(rawValue: "Notification_UploadImage"), object: j)

31. NSNotificationCenter.defaultCenter().postNotificationName("Notification_UploadImage", object: "error", userInfo: nil)  -->
	NotificationCenter.default.post(name: NSNotification.Name(rawValue: "Notification_UploadImage"), object: "error", userInfo: nil)

32. let startIndex =  Int("\(range.startIndex)") -->
	let startIndex =  Int("\(range.upperBound.encodedOffset)") - 1

33. // 单例模式
    class var sharedInstance: TreeNodeHelper {
        struct Static {
            static var instance: TreeNodeHelper?
            static var token: dispatch_once_t = 0
        }
        dispatch_once(&Static.token) { // 该函数意味着代码仅会被运行一次,而且此运行是线程同步
            Static.instance = TreeNodeHelper()
        }
        return Static.instance!
    }
    -->  

    //由于dispatch_once在Swift4.0也已经被废弃   通过给DispatchQueue添加扩展实现
    extension DispatchQueue { 
    private static var _onceTracker = [String]()
    public class func once(token: String, block: () -> ()) {
        objc_sync_enter(self)
        defer {
            objc_sync_exit(self)
        }
        if _onceTracker.contains(token) {
            return
        }
        _onceTracker.append(token)
        block()
    }
    
    func async(block: @escaping ()->()) {
        self.async(execute: block)
    }
    
    func after(time: DispatchTime, block: @escaping ()->()) {
        self.asyncAfter(deadline: time, execute: block)
    }
}
// 单例模式
    class var sharedInstance: TreeNodeHelper {
        
        struct Static {
            static var instance: TreeNodeHelper?
            static let _onceToken = NSUUID().uuidString
        }
        //由于dispatch_once在Swift4.0也已经被废弃  此处通过给DispatchQueue添加扩展实现
        DispatchQueue.once(token: Static._onceToken) { // 该函数意味着代码仅会被运行一次,而且此运行是线程同步
           Static.instance = TreeNodeHelper()
        }
        return Static.instance!
    }

34. let url = NSURL(string:urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)  -->
	let url = NSURL(string: urlString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!)

35. dispatch_async(DispatchQueue.global(0, 0), {
                    
    })    -->   
    DispatchQueue.global().async {

    }
 
36. 在Google Maps SDK中有三个常量来表示每种类型,此属性必须设置成其中一个. 这些常量是:
	kGMSTypeNormal
	kGMSTypeTerrain
	kGMSTypeHybrid
	在使用时候的变化:
 	kGMSTypeNormal  -->  GMSMapViewType.normal

 	kGMSMarkerAnimationPop --> GMSMarkerAnimation.pop

37. let directionsData = NSData(contentsOfURL: directionsURL! as URL)  -->
	let directionsData = NSData(contentsOf: directionsURL as URL)

38. 使用Alamofire解析获取返回的error code值方法:
	res.result.error?.code   -->   res.result.error?._code


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

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 的迁移上已经大大改善了。

参考资料:

2017-10-18 18:23:24 bddzzw 阅读数 10274
/*
Swift 中的闭包有很多优化的地方
1 根据上下文推断参数和返回值的类型
2 从单行表达式闭包中隐式返回 可以省略return
3 可以使用简化的参数如 $0 $1 意为从0或者1开始
4 提供了尾随闭包的语法
*/
//语法   parameters参数 return 隐藏了
//{(parameters) -> return type in
//    parameters
//}
//最简单的闭包//省略in的
let b = {
    print("这也是闭包")
}

//用变量记录函数 (带参数的闭包)
//带有参数的闭包
//参数返回值 实现代码  {形参->返回值 in 代码}
//带参数待返回值的闭包
let countNum = {(num1:Int,num2:Int)->Int in
    return num1+num2;
}
let count1 = countNum(2,3)


//闭包的应用场景

/*异步执行完成回调  控制器之间的回调  自定义视图的回调*/

/*  以下代码需要在项目中的.Swift文件中完成
override func viewDidLoad() {
super.viewDidLoad()
loadData { (result) in
print("获取json信息\(result)")
}
// Do any additional setup after loading the view, typically from a nib.
}
func loadData(completion: @escaping (_ result: [String])->()) -> () {
DispatchQueue.global().async {
print("耗时操作\(Thread.current)")
Thread.sleep(forTimeInterval: 1.0);
let json=["12","23","34"]
DispatchQueue.main.async(execute: {
print("主线程\(Thread.current)")
completion(json)
})
}
}
结果为:耗时操作<NSThread: 0x600000277000>{number = 3, name = (null)}
主线程<NSThread: 0x604000075a80>{number = 1, name = main}
获取json信息["12", "23", "34"]
*/

//闭包表达式
let names = ["s","b","e","h","f"]

func backwards(a1:String,a2:String)->Bool{
    return a1<a2;
}
// public func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
var result = names.sorted(by:backwards)
print(result)

//参数名缩写
///以上代码还可以  Swift自动内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。
var result1 = names.sorted(by:{$0<$1})
print(result1)

//作为一个函数接受两个String类型的参数并返回Bool类型的值Swift可以自动推断出您想使用大于号的字符串函数实现:
//运算符函数
var result2 = names.sorted(by:<)
print(result2)

//尾随闭包
//例子1  尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

var result3 = names.sorted(){$0<$1}
print(result3)



//例子2
//此时点回车函数会变成中括号的形式 也就是说如果函数的最后一个参数是闭包,函数的参数可以提前结束
//        最后一个参数直接使用{}来包装闭包的代码
//        loadData1(completion: <#T##([String]) -> ()#>)
//以下两段代码相同只不过一个是使用尾随闭包的写法
/*func loadData(completion:@escaping (_ result:([String])->())->()){
    DispatchQueue.global().async {
        print("耗时操作\(Thread.current)")
        Thread.sleep(forTimeInterval: 1.0);
        let json=["12","23","34"]
        //以下两段代码相同只不过一个是使用尾随闭包的写法
        DispatchQueue.main.async{
            print("主线程\(Thread.current)")
            completion(json)
        }
        DispatchQueue.main.async(execute: {
            print("主线程\(Thread.current)")
            completion(json)
        })
        
    }
}
*/


良言一句三冬暖,恶语伤人六月寒。无论工作还是生活,都不要拿脾气当做与他人博弈的武器。即使侥幸一时赢了,最终也只是两败俱伤。一言一语间,完成的是沟通,体现的是修养。或许我们不能成为优秀的别人,却完全可以做更好的自己。
2018-11-05 23:00:24 Takioo 阅读数 632

更多原创文章,请访问:https://takioo.cn

随着 Xcode9 Swift 4.0 的到来,一些小问题也接踵而至。

 

许多优秀的 Swift 第三方框架还没有来得及迎接 Swift 4.0 的到来,它们还停留在 Swift3.x 的状态。

这个时候新建一个项目,使用cocoapods 引入所需的第三方,即使 pod install 成功后,一编译工程就是满屏红,

且报错 Swift compiler Error,如下图:

不过所幸,苹果每次升级 Xcode 都会保留上个版本的 swift。

 

解决方案,控制编译时第三方框架所使用的 Swift 版本,有如下两种方法。

方法1. 在 Xcode 设置中手动为每个需要指定 swift 版本的第三方框架设置 swift 版本,如下图:

方法2. 在podfile 文件中,使用 Cocoapods 控制 :

platform :ios, '9.0'
post_install do |installer|
    # 需要指定swift编译版本的第三方的名称
    myTargets = ['Charts','Moya']
    installer.pods_project.targets.each do |target|
        if myTargets.include? target.name
            target.build_configurations.each do |config|
                config.build_settings['SWIFT_VERSION'] = '3.2'
            end
        end
    end
end

use_frameworks!

target 'xxxx' do

pod 'IQKeyboardManager', '~>4.0.6'
pod 'JSONModel'
pod 'KVOController'
pod 'MagicalRecord'
pod 'Mantle'
pod 'MJRefresh'
pod 'Masonry'
pod 'Moya'
pod 'ReactiveSwift'
pod 'SDWebImage'
pod 'SwiftyJSON'
pod 'SDCycleScrollView'
pod 'Charts', '~> 3.0.1'

end

然后再 pod install 一下就可以了。

2017-12-25 14:26:42 fzhlee 阅读数 2408

免费下载地址:https://itunes.apple.com/cn/app/id1320746678

◈ 不看视频不看书,手把手带您学习Swift语言
◈ 利用手指来互动式学习Swift 4.0
◈ 无痛上手,比观看视频、阅读书籍更加有趣、更加有效的学习方式 
◈ 变被动学习为主动学习,真正的Swift语言一点通,是您学习Swift语言的最佳方式


【Swift4互动教程是什么】
Swift4互动教程是一款简单、有趣、互动式的学习Swift的新工具。是第一款互动式学习Swift的工具。此应用将向您展示基于Swift 4.0的绝大部分功能点。
就像有一名专业的教师手把手地教您最新、最实用的Swift编程技巧。您将学会怎么使用Xcode来快速、直接地表达您的想法与创意。
另外,本应用119节课都包含互动模式,同时也支持观赏模式,当您在互动式学习中肢体疲劳时,可以切换至此模式,这样意味着您同时也拥有了119节教学影片。 

【Swift4互动教程主要包括哪些内容】
包含常量与变量、基本数据类型,以及Swift语言中的新数据类型—元组型和可选型的内容。同时还讲解了运算符和表达式、流程控制语句、字符和字符串、集合类型、函数和闭包。此外,Swift面向对象的枚举、结构体、类,以及内存管理、协议与抽象类型、错误处理、链式编程、iOS开发中的常见概念等内容也有所涉及。在第八章我们使用Swift语言实现了栈、队列、二叉树等数据结构,还实现了常见的8种排序算法。最后一章则包含了证书的创建、项目的架构、开发、打包、上传和发布一整套流程,供您学以致用。 部分内容如下:
- 使用Playground学习Swift
- Swift语言中的常量和变量
- Swift的标识符和关键字
- Swift的表达式Expressions
- 给Swift代码添加注释语句
- Swift的几个打印输出语句
- 在控制台输出类的实例及其属性

- Swift的(Boolean)布尔类型
- Swift的(Int)整形类型
- Swift的(Float)和(Double)浮点类型
- 字符串(String)及对字符(Characters)的遍历
- Swift字符串的使用详解
- Swift中的元组(Tuples)
- 基本数据类型之间的互相转换
- 一维数组(Array)的使用详解
- 多维数组的创建和遍历
- 数组的几种遍历方式
- 字典(Dictionary)的使用详解

- Swift的一元、二元和三元运算符
- Swift的位运算符和防溢出运算符
- 比较运算符和区间运算符
- 使用for-in循环遍历数组
- 使用循环语句获得序列中的最小值
- switch-case语句的实例解析
- continue、break和fallthrough的区别
- while和repeat-where循环语句
- if和if-let判断语句的使用解析

- 函数的参数和返回值
- 函数的外部参数名
- 创建拥有任意数量参数的函数
- 函数的输入输出inout参数解析
- 给函数的参数设置默认的取值
- 使用函数作为另一个函数的参数
- 函数类型的解析
- 使用函数类型作为函数的返回类型
- 函数可以拥有多个返回值
- 藏在函数内部的函数
- 递归函数的使用解析
- Swift那些非常实用的内部函数

- Swift的枚举(enum)类型及遍历
- 给枚举类型添加方法
- Swift的结构(struct)类型及其下标(subscript)
- 类(class)的初始化方法、属性和方法
- 类的引用(reference)特征
- 类和结构两个类型的比较
- 类属性的set和get方法解析
- 类属性的willSet和didSet方法解析
- 类析构方法(deinit)的使用
- 给类添加下标(subscript)
- 类的静态方法(class func)
- 将一个类的实例作为另一个类的属性
- 类的继承以及方法的重写(override)
- 父类在实例类型转化时的应用
- 使用is语句检查实例的类型
- 使用Any表示任意值类型

- 使用扩展(extension)扩展类型的属性
- 使用extension对方法进行扩展
- 使用协议(protocol)进行方法的声明
- 问号?和感叹号!的用法解析
- Swift中的闭包(Closure)详解
- Swift语言中的泛型编程
- Swift的do-try-catch错误处理模式
- 实例的引用特征和Swift的内存管理
- 实例的交叉引用和弱引用(weak)
- Swift的懒加载(lazy initialization)使用解析

- 范围Range、ClosedRange和NSRange的使用
- 点CGPoint和变形CGAffineTransform的使用
- 尺寸CGSize的使用详解
- 范围CGRect的使用详解
- 使用NSString对字符串进行各种操作
- 日期Date和DateFormatter日期的格式化
- 日历Calendar和日期组件DateComponents
- 日历Calendar和时区TimeZone
- 使用定时组件Timer执行定时任务
- 使用UserDefaults和归档方式存取用户数据
- 路径URL的使用详解
- 使用DispatchGroup管理线程组
- 使用UIScreen查询设备屏幕信息
- 使用UIColor设置界面组件的颜色属性

- Swift语言中的两种单例模式
- Swift语言中的三种消息传递模式
- 闭包在定时任务、动画和线程中的使用
- 通过protocol在两个对象中进行消息传递
- 通过间接代理进行对象间的消息传递
- 通过属性进行对象间的消息传递
- 使用通知的方法进行对象间的数据传递
- 使用performSegue在故事板页面之间进行数据传递
- Swift中的栈Stack和队列Queue详解
- Swift中的链表LinkedList详解
- 使用Swift创建一个二叉树BinaryTreeNode
- 冒泡排序算法的Swift实现
- 选择排序算法的Swift实现
- 快速排序算法的Swift实现
- 插入排序算法的Swift实现
- 希尔排序算法的Swift实现
- 归并排序算法的Swift实现
- 基数排序算法的Swift实现
- 堆排序算法的Swift实现

- 在iTunesConnect中创建产品ACTHelper
- 在Xcode9中创建新项目ACTHelper
- 创建开发证书和发布证书及其它文件
- 在ACTHelper项目中使用CocoaPod管理插件
- 创建BaseViewController作为控制器的基类
- 创建App的欢迎页面
- 登陆页面:创建自定义视图及相关组件
- 登陆页面:创建自定义表单Row以及基控制器
- 创建用户登陆页面
- 用户注册账号信息的输入和短信验证码
- 用户注册头像和科目信息输入页面
- 用户注册考试成绩输入页面及相关自定义组件
- 试题解析页面及相关自定义组件
- 试题科目列表及答案列表等自定义组件
- 创建试题解析列表页面和试卷选择页面
- 创建试题详情页面和浮动菜单组件
- 试题原文显示页面和数学公式的渲染
- 账号设置列表页面和分享、反馈、评分功能
- 会员个人信息设置页面和登出功能
- App发布前的设置以及打包、上传和提交审核
- 更多内容等待您的探索!!!










【相关互动教程】
Xcode9.0互动教程
https://itunes.apple.com/cn/app/id1063100471

iOS开发中的神兵利器
https://itunes.apple.com/cn/app/id1209739676

Excel2016互动教程
https://itunes.apple.com/cn/app/id1090726089

Photoshop CC互动教程精装版
https://itunes.apple.com/cn/app/id822431555

Illustrator CC互动教程
https://itunes.apple.com/cn/app/id1005557436

Photoshop CS5互动教程完全版:
https://itunes.apple.com/cn/app/id976566883

Photoshop脚本互动教程
https://itunes.apple.com/cn/app/id926846601

Swift4.0 study3 单例

阅读数 1811

Swift4.0 访问权限

阅读数 1606

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