iOS 开发交流

Swift 4.2 Release Notes for Xcod

2018-12-03  本文已影响19人  Mr__Peng__
概览

Xcode 10中编辑Swift时使用Swift 4.2

Swift Complier
新功能
struct ConditionalCast<T> {
    func cast(value: Any?) -> T? {
        return value as? T
    }
}

如果valuenil且类型T在运行时是可选类型,则返回值现在为.some(nil)而不是nil。 当使用具体类型而不是泛型类型时,此行为与转换行为一致

class Person: NSObject {
    let name: String
 
    init(name: String) {
        self.name = name
        super.init()
    }
 
    override func isEqual(_ other: Any?) -> Bool {
        guard let other = other as? Person else { return false }
        return other.name == self.name
    }
 
    override var hash: Int {
        var hasher = Hasher()
        hasher.combine(name)
        return hasher.finalize()
    }
}

注意:hashingequality相辅相成,重载hash,就要重载isEqual :,反之亦然

已知问题
protocol SomeProtocol { }
class SomeClass: SomeProtocol { }

extension SomeProtocol {
    var someNonmutatingProperty: CGPoint {
        nonmutating set { _ = self }
        get { return .zero }
    }
}

// Might be miscompiled, deallocating SomeClass() too early.
SomeClass().someNonmutatingProperty.x = 42

解决方案:将操作分解为多个语句,以便getset操作在不同的语句中发生:

let someObject = SomeClass()
// First get the nonmutating property value.
var temp = someObject.someNonmutatingProperty
temp.x = 42
// Then modify it.
someObject.someNonmutatingProperty = temp
class A<B> {
    let function: (B) -> B
}
 
func takeFunction(_: (Int) -> Int) {}
 
func passFunction(from a: A<Int>) {
    // Reference to a.function as an argument here may crash
    // the compiler.
    takeFunction(a.function)
 
    // Workaround: assign to a local variable, and pass the
    // local variable instead.
    let function = a.function
    takeFunction(function)
}
func f(_: [AnyObject])
f([NSObject.self, NSString.self]) // May crash or miscompile.

解决方案:单独将类对象分配给AnyObject类型的变量,然后形成这些变量的数组:

let myNSObject: AnyObject = NSObject.self
let myNSString: AnyObject = NSString.self
 
f([myNSObject, myNSString])
class X: NSObject {
    var f: ((Any?) -> Bool)?
    func setF() {
        f = super.isEqual(to:) // May crash the compiler.
    }
}

解决方案:首先将闭包分配给非可选变量,然后将非可选变量分配给可选变量。

class X: NSObject {
    var f: ((Any?) -> Bool)?
    func setF() {
        let f2: (Any?) -> Bool = super.isEqual(to:)
        // Work around compiler crash.
        f = f2
    }
}
protocol Example {
    mutating func test() -> Self
}

func foo(x: inout Example) {
    _ = x.test() // May crash the compiler.
}

解决方案:如果可能,使方法不变,或让方法返回Void或协议类型而不是Self

protocol Example {
    mutating func test() -> Example // Instead of Self.
}

func foo(x: inout Example) {
    _ = x.test()
}

如果无法做到这一点,请在协议扩展中添加一个包装器方法,该方法调用Self-returning方法并返回协议类型:

extension Example {
    mutating func testWorkaround() -> Example {
        return test()
    }
}

func foo(x: inout Example) {
    _ = x.testWorkaround() // Invoke testWorkaround instead of test to avoid compiler crash.
}
解决问题
protocol Base {}
protocol Sub: Base {}
struct Generic<Param> {}
 
extension Generic: Base where Param: Base {}
extension Generic: Sub where Param: Sub {}

第一个扩展必须存在,因为第二个扩展并不意味着GenericBase的一致性(如果确实如此,它将暗示它具有与Sub一致性相同的条件要求,即Param:Sub)。 这尤其会影响Hashable:EquatableBidirectionalCollection:Collection等层次结构,并有助于在符合它们的类型范围内实现精度

error: value of optional type 'X?' must be unwrapped to a value
of type 'X'
f(x)
  ^
note: coalesce using '??' to provide a default when the optional value
contains 'nil'
  f(x)
    ^
     ?? <#default value#>
note: force-unwrap using '!' to abort execution if the optional value
contains 'nil'
  f(x)
    ^
     !

访问可选值的成员时,Fix-it对话框包含可选链接作为潜在解决方案:

error: value of optional type 'X?' must be unwrapped to refer to
member 'f' of wrapped base type 'X'
  let _: Int = x.f()
               ^
note: chain the optional using '?' to access member 'f'
only for non-'nil' base values
  let _: Int = x.f()
               ^
                ?
note: force-unwrap using '!' to abort execution if the optional
value contains 'nil'
  let _: Int = x.f()
               ^
                !
Swift Language
新功能
#warning("This code is incomplete.")

#if BUILD_CONFIG && OTHER_BUILD_CONFIG
      #error("BUILD_CONFIG and OTHER_BUILD_CONFIG can't both be set")
#endif
enum Suit: CaseIterable {
    case spade
    case heart
    case diamond
    case club
}
 
print(Suit.allCases)
// prints [Suit.heart, Suit.club, Suit.diamond, Suit.spade]
解决问题
struct Generic<Param> {
    var property: Param
}
 
extension Generic: Equatable where Param: Equatable {}
// Automatically synthesized inside the extension:
// static func ==(lhs: Generic, rhs: Generic) -> Bool {
//     return lhs.property == rhs.property
// }

想要尽可能精确的代码通常不应该直接有条件地符合Codable,而是它的两个组成协议EncodableDecodable,否则只能(例如)解码Generic <Param>如果ParamEncodable除了Decodable,即使可能不需要Encodable

// Unnecessarily restrictive:
extension Generic: Codable where Param: Codable {}
 
// More precise:
extension Generic: Encodable where Param: Encodable {}
extension Generic: Decodable where Param: Decodable {}

最后,由于Decodable具有初始化程序要求,因此无法在非最终类的扩展中符合Decodable。 这样的类需要从协议中获得任何初始化器,这意味着它们需要在类定义中。

protocol P {}
 
struct DoesntConform {}
struct Conforms: P {}
 
struct Generic<Param> {}
extension Generic: P where Param: P {}
 
print(Generic<DoesntConform>() as? P) // nil
print(Generic<Conforms>() as? P)
// Optional(main.Generic<main.Conforms>())
Swift Migrator
解决问题
Swift Package Manager
新功能
// swift-tools-version:4.2
import PackageDescription
 
let package = Package(
    // ...
    swiftLanguageVersions: [.v3, .v4]
)
已知问题
Root package:
    PackageA@master
PackageA:
    PackageB@master
    PackageC@master
PackageB:
    PackageC@1.0.0..<2.0.0

根据包管理器的依赖性解析规则,这应该解决为:PackageA @ masterPackageB @ masterPackageC @ master。 但是,由于共享依赖项PackageC以1.0.0 .. <2.0.0要求引用,因此可能会导致崩溃
解决方案:对共享包依赖项使用基于版本化或基于修订版的要求。

Swift Standard Library
新功能
let countableRange: CountableRange<Int> = 0..<10
var range: Range = countableRange
 
func f<T: SignedInteger>(_ countableRange: CountableRange<T>) { }
f(range)

ClosedRangeCountableClosedRange分别与RangeCountableRange具有相同的行为。

struct Foo: Hashable {
    var a: String?
    var b: [Int]
    var c: [String: Int]
 
    static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c
    }
 
    func hash(into hasher: inout Hasher) {
        hasher.combine(a)
        hasher.combine(b)
        hasher.combine(c)
    }
}

Hashable的自动合成已更新,以生成散列(into:)实现。例如,上面的==hash(into :)实现等同于编译器合成的实现,并且可以在不改变代码含义的情况下删除。
还扩展了Synthesis以支持从hash(into:)导出hashValue,反之亦然。因此,仅实现hashValue的代码在Swift 4.2中继续有效。这个新的编译器功能适用于所有可以实现Hashable的类型,包括类。
请注意,这些更改不会影响Foundation的散列界面。子类NSObject的类应该覆盖hash属性,就像之前一样。
在某些受控环境中,例如在运行特定测试时,选择性地禁用散列种子随机化可能会有所帮助,这样散列值和集合和字典值中元素的顺序在执行期间保持一致。可以通过定义值为1的环境变量SWIFT_DETERMINISTIC_HASHING来禁用散列种子随机化.Swift运行时在进程启动期间查看此变量,如果已定义,则使用常量值替换随机种子。

解决问题
上一篇 下一篇

猜你喜欢

热点阅读