[Swift] 如何在 actor 中使用孤立/非孤立关键字

   2023-02-07 学习力0
核心提示:概述Actor 的方法和属性与 Actor 是隔离的。SE-0313 改进了对actor隔离的控制通过使用提案中添加的isolated / nonisolated 关键字,可以将不是参与者成员的进程隔离到参与者,反之亦然,将参与者成员与参与者隔离。本文根据提案总结了如何使用这些关键字。文

概述

Actor 的方法和属性与 Actor 是隔离的。SE-0313 改进了对actor隔离的控制通过使用提案中添加的isolated / nonisolated 关键字,可以将不是参与者成员的进程隔离到参与者,反之亦然,将参与者成员与参与者隔离。本文根据提案总结了如何使用这些关键字。

文中的操作验证是使用 Xcode 14 Beta 5 完成的。

isolated关键词

作为演员的一个例子,请考虑下面的Counter

actor Counter {
    var count: Int = 0
}

假设我们想在外部为这个演员增加count。与参与者隔离的状态不能直接从外部操作以防止数据竞争,因此像以下这样的实现将是错误的。

func incrementCounter(_ counter: Counter) {
    counter.count += 1 // ❗️ Actor-isolated property 'count' can not be mutated from a non-isolated context
}

在这里,我们可以通过将isolated 关键字附加到参数来将incrementCounter 函数与counter 演员隔离开来。这允许我们同步操作actor的属性count

func incrementCounter(_ counter: isolated Counter) {
    counter.count += 1 // ✅ OK
}

这个incrementCounter 不是async 函数,但由于它与actor 隔离,所以在actor 外部调用它时需要await。由于它与actor隔离,因此它具有与actor的方法相同的属性。

@main
public struct Main {
    public static func main() async throws {
        let counter = Counter()
        await incrementCounter(counter)
    }
}

isolated相关错误

isolated 关键字只能附加到演员。由于函数的性质与标记为isolated 的参数隔离,因此用isolated 标记非参与者参数是没有意义的。

// ❗️ 'isolated' parameter has non-actor type 'Int'
func incrementCounter(_ counter: Counter, by value: isolated Int) {}

此外,isolated 不能附加到一个函数中的多个参数。这是因为函数不能被多个参与者隔离。

// ❗️エラー
func incrementCounters(_ counter1: isolated Counter, _ counter2: isolated Counter) {}

出于同样的原因,您也不能在 actor 方法上使用 isolated 关键字。有方法的演员

extension Counter {
    // ❗️エラー
    func transferCount(to other: isolated Counter) {}
}

但是,从 Xcode 14 Beta 5 开始,这两个代码都可以在没有编译错误或警告的情况下运行,并且如果您尝试,实际上可能会引入数据竞争。这个问题将在适当的时候修复,上面的代码应该不再运行。

参考:https://github.com/apple/swift/issues/60474

何时使用isolated

isolated至于关键字,我想不出绝对必须使用它的情况(如果有人知道,请在评论中告诉我)。这是因为如果你想要一个actor中的独立进程,你可以让它成为actor的一个方法。
但是,从代码管理和代码编写风格来看,很可能会出现将isolated关键字作为全局函数而不是actor方法更合适的情况,所以值得记住.

nonisolated 关键字

isolated 我发现你可以使用isolated 关键字将actor 之外的函数与actor 隔离。通过使用nonisolated 关键字,您可以反过来不隔离演员的成员。
例如,如下图在actor方法中添加nonisolated,就不会被隔离,可以从actor外部同步调用。

extension Counter {
    func f1() {}
    nonisolated func f2() {}
}

@main
public struct Main {
    public static func main() async throws {
        let counter = Counter()
        counter.f1() // ❗️ Expression is 'async' but is not marked with 'await'
        counter.f2() // ✅ OK
    }
}

nonisolated 关键字添加到var 属性会导致错误。首先,一个actor是为了保护可变状态,所以我认为var之所以不被隔离,是为了偏离actor的使用。

actor Counter {
    nonisolated let id: String
    nonisolated var count: Int = 0 // ❗️ 'nonisolated' can not be applied to stored properties 
    nonisolated var p1: String { "p1" }
    nonisolated func f1() {}
}

此外,let 属性可能是 nonisolated ,但恐怕实际上这样做并没有太大的好处。这是因为let 默认情况下可以在actor外部同步访问。这是因为为了发生数据竞争,必须同时访问相同的数据,其中一个必须是写入。我认为。

actor Counter {
    let id: String
    
    init(id: String) {
        self.id = id
    }
}

@main
public struct Main {
    public static func main() async throws {
        let counter = Counter(id: "counter")
        print(counter.id) // ✅ OK。 nonisolated がついていなくても同期的にアクセスできる
    }
}

何时使用nonisolated

nonisolated 有用的一个例子是允许同步访问actor属性,这些属性可以安全地从多个线程同时访问。
考虑以下description 属性,它将Counter 的摘要作为字符串返回。

actor Counter {
    let id: String
    var count: Int = 0
    
    var description: String {
        "Counter with ID \(id)"
    }
    
    init(id: String) {
        self.id = id
    }
}

description 该属性与actor 隔离,因此必须从actor 外部使用await 调用它。这很不方便,因为它不能直接从同步函数中调用。

@main
public struct Main {
    public static func main() async throws {
        let counter = Counter(id: "counter")
        print(counter.description) // ❗️ Expression is 'async' but is not marked with 'await'
    }
}

但是,当我冷静地回顾description 的实现时,它只依赖于id,即let 属性。如前所述,let属性不会引起数据竞争,可以从多个线程同时访问,所以description也可以从多个线程同时访问,不需要actor保护。就是这个意思.也就是说description不需要隔离到某个actor,但是因为是隔离的,所以只能异步访问,不方便。在这种情况下,将nonisolated添加到description以防止actor被隔离,使得同步访问成为可能。

actor Counter {
    let id: String
    var count: Int = 0
    
    nonisolated var description: String {
        "Counter with ID \(id)"
    }
}

@main
public struct Main {
    public static func main() async throws {
        let counter = Counter(id: "counter")
        print(counter.description) // ✅ OK
    }
}

当然,在这里,如果nonisolated 进程可以访问一个应该与参与者隔离的属性,则会导致数据竞争。我们可以安全地使用nonisolated,因为编译器会对这种危险的访问发出错误。

actor Counter {
    let id: String
    var count: Int = 0

    nonisolated var description: String {
        // ❗️ Actor-isolated property 'count' can not be referenced from a non-isolated context
        "Counter with ID \(id) with count \(count)"
    }
}

nonisolated 有用的另一个地方是当您希望参与者符合特定协议时。例如,CounterCustomStringConvertible我希望它符合CustomStringConvertible的请求是

public protocol CustomStringConvertible {
    var description: String { get }
}

但是,像下面这样的Counter 的实现并不能很好地符合并导致错误。

actor Counter: CustomStringConvertible {
    let id: String

	// ❗️ Actor-isolated property 'description' cannot be used to satisfy nonisolated protocol requirement
    var description: String {
        "Counter with ID \(id)"
    }
}

这是因为description是隔离的,必须从actor外部异步调用,所以接口有async属性。由于假设description可以作为CustomStringConvertible的请求被同步访问,所以只能异步访问这个请求是无法满足的。
nonisolated 在这种情况下很有效。将nonisolated 添加到description 可以从actor 外部进行同步访问,因此可以满足对CustomStringConvertible 的请求。

// ✅ OK
actor Counter: CustomStringConvertible {
    let id: String

    nonisolated var description: String {
        "Counter with ID \(id)"
    }
}

除了CustomStringConvertible,Swift 的大部分内置协议如IdentifiableHashable 都需要同步属性和方法,所以nonisolated 经常用在actor 符合某种协议的情况下,会有很多。

概括

  • isolated 您可以通过将isolated 关键字附加到函数参数中的参与者来将函数隔离到参与者。
  • nonisolated 关键字添加到参与者的成员中使其非隔离
    • 当您想从参与者外部同步访问,或者当您希望参与者遵守特定协议时很有用

参考


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308623646.html

 
标签: iOS Swift
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • iOS扩展——Objective-C开发编程规范 简单io扩展实验代码
    iOS扩展——Objective-C开发编程规范 简单io扩
      最近准备开始系统学习一个完整项目的开发流程和思路,在此之前,我们需要对iOS的开发变成规范进行更系统和详尽的学习,随意对编程规范进行了整理和学习。本文内容主要转载自:Objective-C-Coding-Guidelines-In-Chinese  此外,这篇文章所说的一些常见
    03-08
  • iOS打包framework - Swift完整项目打包Framework,嵌入OC项目使用
    iOS打包framework - Swift完整项目打包Framewor
    场景说明:-之前做的App,使用Swift框架语言,混合编程,内含少部分OC代码。-需要App整体功能打包成静态库,完整移植到另一个App使用,该App使用OC。-所以涉及到一个语言互转的处理,以及一些AppDelegate的代码减除变化。 --------------------------------
    02-09
  • iOS8 蓝牙设备的重连接(retrieve) Swift实现
      今天App写到了蓝牙重连的阶段,以前针对sdk 6.0写的代码,蓝牙设备的回复是通过- (void)retrievePeripherals:(NSArray *)peripheralUUIDs然后回调 centralManager:didRetrievePeripherals:函数来得到可以回复设备的Array。在SDK7之后,- (void)retrievePe
    02-09
  • iOS高效开发必备的10款Objective-C类库 object c ios
    iOS高效开发必备的10款Objective-C类库 object
    因为iOS SDK相对比较底层,所以开发者就得受累多做一些体力活。不过幸运的是,有很多第三方的类库可以用来简化很多不必要的工作.经过作者团队的慎重讨论,他们评选出了10款能够极大提高iOS开发效率的类库,根据原文作者的评价来看,基本上有了这10款工具,做i
    02-09
  • IOS基础:Objective-C 字符串处理
    //一、NSString/*----------------创建字符串的方法----------------*/ //1、创建常量字符串。NSString *astring = @"This is a String!";//2、创建空字符串,给予赋值。NSString *astring = [[NSString alloc] init];astring = @"This is a String!";[astri
    02-09
  • 微信小程序ios下禁用默认滚动事件 微信小程序ios下禁用默认滚动事件提醒
    微信小程序ios下禁用默认滚动事件 微信小程序io
    在微信小程序ios下,如果有一个scroll-view滚动列表,开始触摸点点在滚动列表外,向列表里拖拽列表是不会滚动的,这很正常,因为开始触摸点点在了滚动列表外。可是不正常的是ios下会出现“反应不过来”的现象,这很难表述,看下图图中view:A用了fixed主要是
    02-09
  • 小程序camera组件ios上出现授权的问题
    camera需要调用手机系统的摄像头,正常进入页面就会询问用户权限。但是ios上面,前提是已经打开了摄像头的权限,加载之后隐藏掉camera,再次显示的时候,就会进入binderror事件,该事件是当用户未打开摄像头权限才会进入的监听事件,尝试了用wx.openSetting再
    02-09
  • 【小程序】使用uni-app搭建小程序环境---时间格式问题 new Date(str) IOS系统跟Android系统不兼容
    【小程序】使用uni-app搭建小程序环境---时间格
    今天做了一个需求,要在列表中把后台返回来的时间给显示出来,使用 new Date(str)  在微信开发者工具上显示是没有问题的,然后在IOS系统上显示是NAN。原因是 IOS系统只识别 " / " 不识别 " - ". 后台返回来的时间类型一般有三种 时间、时间搓、字符串。我
    02-09
  • Delphi xe7 FireMonkey / Mobile (Android, iOS)生成 QR Code完整实例
    Delphi xe7 FireMonkey / Mobile (Android, iOS
    这个实例在windows、OS X、IOS和Android等平台运行正常。本文参考这个网站提供的方法:http://zarko-gajic.iz.hr/firemonkey-mobile-android-ios-qr-code-generation-using-delphi-xe-5-delphizxingqrcode/代码中用到的DelphiZXingQRCode.Pas点这下载1 unit U
    02-09
  • 预热篇- 总结Delphi Xe4 做App的的可行性分析.
    首先澄清一个问题, 很多同学其实是误会了, 以为只要搞定了Delphi 就能很快写快餐程序了.  ios 本身的知识还是需要一些的, 并没有什么捷径可以走. 但如果一个团队有分工协作的话, DelphiXe4 也可以考虑作为一种技术方向.  用对了地方, 就可以发挥Delphi的长
    02-09
点击排行