结合对服务器的访问在游乐场内而不是在函数内检索数据。为什么?

Combine access to server retrieves data within a playground but not within a function. Why?

以下代码产生了我期望的输出:

import UIKit
import Combine


    let myURL = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")

    // MARK: - Region
    struct Region: Codable {
        let country: String
        let subregions: [String]
    }


    let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: myURL!)
        .map { [=10=].data }
        .print("Hello Data")
        .decode(type: Region.self, decoder: JSONDecoder())

    let cancellableSink = remoteDataPublisher
        .sink(receiveCompletion: { completion in
                print(".sink() received the completion", String(describing: completion))
                switch completion {
                    case .finished:
                        break
                    case .failure(let anError):
                        print("received error: ", anError)
                }
        }, receiveValue: { someValue in
            print(".sink() received \(someValue)")
        })


// =====================================================================================================
 
print("The End.")

这是控制台中显示的输出。
可以看到,没有'cancel':


但是,当我将代码包装到函数 () 中时,'combine' 取消了输出。
getRegionList() 中的以下代码不会产生输出。 相反,它会收到 'cancel',如以下代码后的控制台所示:

import UIKit
import Combine

var regionList = [String]()
let simplePublisher = PassthroughSubject<String, Never>()


func getRegionList() {
let myURL = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")

// MARK: - Region
struct Region: Codable {
    let country: String
    let subregions: [String]
}


let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: myURL!)
    .map { [=11=].data }
    .print("Hello Data")
    .decode(type: Region.self, decoder: JSONDecoder())

    let cancellableSink = remoteDataPublisher
    .sink(receiveCompletion: { completion in
            print(".sink() received the completion", String(describing: completion))
            switch completion {
                case .finished:
                    break
                case .failure(let anError):
                    print("received error: ", anError)
            }
    }, receiveValue: { someValue in
        print(".sink() received \(someValue)")
    })
}
getRegionList()
print("The End.")

函数取消,如控制台所示:

为什么?
唯一的代码差异是一个 被包装在一个简单的函数 中。
我怀疑某些 'lifespan' 一定已经过期了。

解决方案: 我应该使 concellableSink 全局化吗?
什么是正确的solution/syntax?

您需要保留对 AnyCancellable 的引用,否则当它被释放时,它会取消订阅,这就是您的函数 returns.

时发生的情况

通常,这是通过将引用存储在实例的 AnyCancellable(或 Set<AnyCancellable>)属性 中来处理的:

class Foo {
   var cancellables: Set<AnyCancellable> = []

   //...

   func doSomething() {
      somePublisher
        .sink(...)
        .store(in: &cancellables)
   }
}

或者,sink 闭包可以保持对它自己的 AnyCancellable 的引用,并在收到值时释放它,但是如果发布者从不发布任何东西(尽管你可以使用 .timeout 运算符来缓解它):

func doSomething() {
   let cancellable: AnyCancellable? = nil
   cancellable = somePublisher
      .sink(receiveCompletion: { completion in
         // ...
         cancellable = nil
      }, receiveValue: { value in
         //...
      })
}