iOS 11 的一些玩意儿: Swift 4

iOS 11九月份就要正式发布了。拖延症表示陆陆续续还没看完WWDC~?。除了重磅的ARKitCore ML,其他更像是小修小补。连Swift 4都不是一门新语言,算什么WWDC~~~

陆陆续续写一些iOS 11的玩意儿吧~

这是关于Swift 4的~~~~

Access Control

想给Swift跪了~~~

Swift么有Objective-C.h.m这样泾渭分明,哪些该露点的,哪些不该露点的,都很清晰。于是在Swift中有了access control,就是public啊,private啊,fileprivate啊等。

Swift 4中,主要是对private进行重新定义。在Swift 3中,private表明的属性和方法,是无法在extension中使用的,只有fileprivate才可以。但是~~~,Swift 4觉得不太合理,所以,private可以在extension中使用了~~~还是上个?吧。。

struct Foo {
    private let a : Int
    fileprivate let b : Int
    private func testA() {}
    fileprivate func testB() {}
}

extension Foo {
    func testAB() {
        print(a)        // private 标注的 a 属性,在 Swift 3 中是无法使用的
        print(b)
        testA()         // private 标注的 testA 方法,在 Swift 3 中是无法使用的
        testB()
    }
}

dynamic

Swift 3dynamic是自带@objc,但是Swift 4中,dynamic不在包含@objc了。所以有些需要使用到@objc标明的方法,在Swift 4得补回去。之前为了让Swift可以使用JSPatch,于是乎,给一些方法都加了dynamic~~~~累cry~~

Swift & Objective-C

直接上个?。

class Foo {
    let a : Int
    func testA() {}
}

Swift 3中,如果是在Objective-C中使用上述的代码,那么是可以成功调用到foo.a[foo testA]的。在对应的xxx-Swift.h文件中,也可以看到相关的转换成Objective-C的代码。但是!!!在Swift 4,这些都不成立了。如果要使用,那么需要标上@objcMembers或者@objc。也就是——

class Foo {
    @objcMembers let a : Int
       @objcMembers func testA() {}
   // 又或者
   @objc let b : Int
       @objc func testB() {}
}

不过,文档更推荐使用@objcMembers。据悉,@objc相对于@objcMembers而来,可能会增大包的编译大小。而且,最好是在需要使用到的地方才用上@objcMembers,而不是所有都标明~~

NSAttributedString

先前,诸如类似NSFontAttributeName,NSForegroundColorAttributeName等这些,在Swift 4中,统统变成NSAttributedStringKey.font,NSAttributedStringKey.foregroundColor等这些更佳Swift化的形式了。

String

字符串长度

Swift 3Swift 4
“xxx”.characters.count“xxx”.count

换行

// Swift 3 换行需要用到换行符 \n
let str = "xxx\nxxx"

// Swift 4 有了更方便的表示。使用 """ 。不过得保持一致的缩进
let str = """
            xxx
            xxx
            """

split

这方法是进行切割字符串的。看?!

var str = "Hello, playground"
let strArr = str.split(separator: ",") // ["Hello","playground"]

但是切割得到的数组是[SubString]。因此,在赋值的时候,需要对其进行强转,即String(SubString)这样。

Codable

Swift 4中,加入了Codable协议,可以将JSON给转换成对应的Struct或者Class,也可以将其他的格式转成对应的。

Codabel是由DecodableEncodable两个协议组成的。

public typealias Codable = Decodable & Encodable

直接上?。

let jsonStr = """
       {
            "id": 123455,
            "nickname": "Ben",
            "isMale": true,
            "birthday": "2000年3月24日",
            "personalURL": "https://addicechan.github.io"
        }
        """
let jsonData = jsonStr.data(using: .utf8)!
let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY年MM月dd日"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
do {
    let user = try decoder.decode(User.self, from: jsonData)
    print(user)
    print(user.personalURL.scheme ?? "http?https?")
} catch let error {
    print(error.localizedDescription)
}

使用上,并不困难。使用JSONDecoder可以对一些时间格式进行处理,比如上述?就是自定义时间格式。当然还可以用时间戳显示等。dateDecodingStrategy属性是一个DateDecodingStrategy枚举值来着。具体瞅文档吧。

可以解码,当然也可以进行编码。

// 接着???的?
let user1 = User(id: 1000,
                 nickname: "ADDICE",
                 isMale: true,
                 birthday: Date(),
                 personalURL: URL(string: "https://addicechan.github.io")!)
                 
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .formatted(dateFormatter)
do {
    let data = try encoder.encode(user1)
    let str = String.init(data: data, encoding: .utf8)!
    print(str)
} catch let encodeError {
    print(encodeError.localizedDescription)
}

有了Codable,对JSON格式的转换也是方便很多。不过实际在项目中使用还是有待验证吧。不过这倒是解决了之前Swift原生代码对JSON数据的解析,不用各种嵌套。反正,使用这个协议还可以支持其他数据格式,并不是单单JSON一种。

KVC

class Foo: NSObject {
    @objc var age : Int
    var name : String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}
let foo = Foo(age: 10, name: "foo")
#if swift(>=4.0)
    let key = \Foo.age
    foo[keyPath: key] = 50
    print(foo[keyPath: \ Foo.name])
#else
    let key = #keyPath(Foo.age)
    foo.setValue(20, forKey: key)
#endif
print(foo.age)

较之Swift 3#keyPath(T.property)这种方式,Swift 4用一个\去表示一个keyPath,而且具备各种推断,并且还可以用于structSwift是一门强类型的安全语言,所以使用改版后的KeyPath可以推断到原来的类型,而不是再是Any。不过,先前用#keyPath这种生成字符串的方式,居然没有被废除~?

KeyPath的基类是AnyPathAnyPath拥有rootTypevalueType。根据上述的?,那么key的类型其实应该是KeyPath<Foo, Int>。也就是,如果你要取到Foo.age,其实也可以这样去写——

let key : KeyPath<Foo, Int> = \Foo.age

但是很多时候,我们会选择使用推导类型,所以还是懒一些吧。

其实按照文档来说,KeyPath的层级式这样的。

说明
AnyPath基类,用RootValue属性,以及appending(path: AnyKeyPath)可以进行拼接keyPath
PartialKeyPath<Root>继承自AnyPath,只声明了Root类型,Value 均为Any
KeyPath<Root,Value>继承自PartialKeyPath,携带了RootValue,都有明确的类型
WritableKeyPath<Root,Value>继承自KeyPath,可读可写,用于struct这种值类型。
ReferenceWritableKeyPath<Root,Value>继承自WritableKeyPath,可读可写,用于class这种引用类型。

另外,发现一个bug。当用KeyPath去更改struct里面的值时,只能读,不能写。但是WWDCkeynote显示又是可读可写。。寂寞。不知道是不是Debug版本的锅。反正我用的是Xcode 9 beta 6

上个WWDC上面的那个?。

@objcMembers
class Kid : NSObject {
    dynamic var nickname: String = ""
    dynamic var age: Double = 0.0
    dynamic var bestFriend: Kid? = nil
    dynamic var friends: [Kid] = []
    init(nickname: String, age: Double) {
        super.init()
        self.nickname = nickname
        self.age = age
    }
}

struct BirthdayParty {
    let celebrant: Kid
    var theme: String
    var attending: [Kid]
}
let ben = Kid(nickname: "Benji", age: 5.5)

var bensParty = BirthdayParty(celebrant: ben, theme: "Construction", attending: [])

let birthdayKid = bensParty[keyPath: \BirthdayParty.celebrant]

bensParty[keyPath: \BirthdayParty.theme] = "Pirate"

KVO

当需要监听对象某个属性的时候,通常我们会使用KVO的方式,利用addObserver来添加观察者,之后在

func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)

进行处理。但是,Swift 4有了更加简单的观察形式。

// 方式一
// observer 为 被观察的对象,keyValueChange 为被观察的属性的变化
foo.observe(\FooClass.age, options: .new, changeHandler: { (observer, keyValueChange) in 
})

// 方式二
foo.observe(\FooClass.age, changeHandler: { (observer, keyValueChange) in
    
})

Swift 4大致上整理到这些。还有其他的一些更新。推荐还是看官方文档。上述的内容,估计也会有遗漏,或者错误。请多包涵~?

Demo在此~~~

    原文作者:六叔
    原文地址: https://segmentfault.com/a/1190000010878701
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞