Read only CurrentValueSubject? - swift

Is there a way to create a CurrentValueSubject that is read-only?
So you could sink it publicly, read value publicly, but could only send values to it internally/privately. Want to use it in a library module.

The best pattern is to have it declared private:
private let _status = CurrentValueSubject<ThisStatus?, Never>(nil)
and expose it through a computed property:
public var status: AnyPublisher<ThisStatus?, Never> {
_status
.eraseToAnyPublisher()
}

I ended up creating a Publisher wrapping a CurrentValueSubject.
This way it can be written internally (to the module), but other modules can only read/subscribe to the wrapping publisher.
public class ReadOnlyCurrentValueSubject<Output, Failure>: Publisher where Failure : Error {
internal let currentValueSubject: CurrentValueSubject<Output, Failure>
public internal(set) var value: Output {
get { currentValueSubject.value }
set { currentValueSubject.value = newValue }
}
public init(_ value: Output) {
currentValueSubject = .init(value)
}
public func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
currentValueSubject.receive(subscriber: subscriber)
}
}
With having internal access to the currentValueSubject the module can freely compose it internally, while the outside world can only consume the values.

You can write a custom publisher that wraps a CurrentValueSubject, and that exposes the subject only at initialization time. This way, the code that creates the publisher is the only one having access to the subject, and is able to instruct the publisher to emit events.
The new publisher can look like this:
extension Publishers {
public struct CurrentValue<Value, Failure: Error>: Publisher {
public typealias Output = Value
public typealias Subject = CurrentValueSubject<Value, Failure>
public var value: Value { subject.value }
private var subject: Subject
public static func publisher(_ initialValue: Value) -> (Self, Subject) {
let publisher = Self(initialValue)
return (publisher, publisher.subject)
}
private init(_ initialValue: Value) {
subject = Subject(initialValue)
}
public func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Value == S.Input {
subject.receive(subscriber: subscriber)
}
}
}
, and can be consumed in this fashion:
class MyClass {
// use this to expose the published values in a readonly manner
public let publisher: Publishers.CurrentValue<Int, Never>
// use this to emit values/completion
private var subject: Publishers.CurrentValue<Int, Never>.Subject
init() {
(publisher, subject) = Publishers.CurrentValue.publisher(10)
}
}
This way you have a readonly value publisher, and the only instance that can publish values is the one that instantiates the publisher.
Now, if the internal requirement you specified in the question must be taken ad-literam, then you can change the visibility of the CurrentValue.subject property to be internal, in this case you no longer need the static method.

A good replacement in this case is:
#Published public private(set) var status: ThisStatus? = nil
Use $status to get the underlying publisher.

Related

How to create an instance that satisfies the generic requirements

I have created the following code and everything works fine except the definition of TestDBAPI.
When I want to create a type that conforms to the DBAPIProtocol protocol, it is always impossible to generate a type instance that satisfies the generic constraints
Please, how can I define TestNoteFetcher to satisfy the protocol requirement of DBAPIProtocol.
ps: I hope the flexibility of generic definitions can be maintained in DBAPIProtocol
thanks
import Combine
// For Value
public enum WrappedID: Equatable, Identifiable, Sendable, Hashable {
case string(String)
case integer(Int)
public var id: Self {
self
}
}
public protocol BaseValueProtocol: Equatable, Identifiable, Sendable {
var id: WrappedID { get }
}
public struct Note: BaseValueProtocol {
public var id: WrappedID
public var index: Int
public init(id: WrappedID, index: Int) {
self.id = id
self.index = index
}
}
// For Object
public protocol ConvertibleValueObservableObject<Value>: ObservableObject, Equatable, Identifiable where ID == WrappedID {
associatedtype Value: BaseValueProtocol
func convertToValueType() -> Value
}
public final class TestNote: ConvertibleValueObservableObject {
public static func == (lhs: TestNote, rhs: TestNote) -> Bool {
true
}
public var id: WrappedID {
.integer(1)
}
public func convertToValueType() -> Note {
.init(id: .integer(1), index: 0)
}
}
// For Fetcher
public protocol ObjectFetcherProtocol<Object,ConvertValue> {
associatedtype ConvertValue: BaseValueProtocol
associatedtype Object: ConvertibleValueObservableObject<ConvertValue>
var stream: AsyncPublisher<AnyPublisher<[Object], Never>> { get }
}
public final class TestNoteFetcher: ObjectFetcherProtocol {
public typealias ConvertValue = Note
public typealias Object = TestNote
public var stream: AsyncPublisher<AnyPublisher<[TestNote], Never>> {
sender.eraseToAnyPublisher().values
}
public var sender: CurrentValueSubject<[TestNote], Never>
public init(_ notes: [TestNote] = []) {
sender = .init(notes)
}
}
// For API
public protocol DBAPIProtocol {
var notesFetcher: () async -> any ObjectFetcherProtocol<any ConvertibleValueObservableObject<Note>, Note> { get set }
}
// get error in here . Cannot convert value of type 'TestNoteFetcher.Object' (aka 'TestNote') to closure result type 'any ConvertibleValueObservableObject<Note>'
public final class TestDBAPI: DBAPIProtocol {
public var notesFetcher: () async -> any ObjectFetcherProtocol<any ConvertibleValueObservableObject<Note>, Note> = {
TestNoteFetcher([])
}
}
Since your closure returns too many anys, the compiler got confused & is telling you that TestNoteFetcher does not conform any ObjectFetcherProtocol. Generics are your friend, you can use associatedtype to skip all this code & fix the issue:
public protocol DBAPIProtocol {
associatedtype Fetcher: ObjectFetcherProtocol
var notesFetcher: () async -> Fetcher { get set }
}
public final class TestDBAPI: DBAPIProtocol {
public var notesFetcher: () async -> TestNoteFetcher = {
TestNoteFetcher([])
}
}
Sometimes the answer is right in front of me, but I do turn a blind eye to it. follow the Xcode warning, I made a change to this part of the code and it works now.
public protocol ObjectFetcherProtocol<ConvertValue> {
associatedtype ConvertValue: BaseValueProtocol
var stream: AsyncPublisher<AnyPublisher<[any ConvertibleValueObservableObject<ConvertValue>], Never>> { get }
}
public final class TestNoteFetcher: ObjectFetcherProtocol {
public var stream: AsyncPublisher<AnyPublisher<[any ConvertibleValueObservableObject<Note>], Never>> {
sender.eraseToAnyPublisher().values
}
public var sender: CurrentValueSubject<[any ConvertibleValueObservableObject<Note>], Never>
public init(_ notes: [any ConvertibleValueObservableObject<Note>] = []) {
sender = .init(notes)
}
}
This way, I can still maintain the flexibility defined in DBAPIProtocol without having to introduce association types

PropertyWrapper subscript is not called. WHY?

I am implementing my own AtomicDictionary property wrapper as follows:
#propertyWrapper
public class AtomicDictionary<Key: Hashable, Value>: CustomDebugStringConvertible {
public var wrappedValue = [Key: Value]()
private let queue = DispatchQueue(label: "atomicDictionary.\(UUID().uuidString)",
attributes: .concurrent)
public init() {}
public subscript(key: Key) -> Value? {
get {
queue.sync {
wrappedValue[key]
}
}
set {
queue.async(flags: .barrier) { [weak self] in
self?.wrappedValue[key] = newValue
}
}
}
public var debugDescription: String {
return wrappedValue.debugDescription
}
}
now, when I use it as follows:
class ViewController: UIViewController {
#AtomicDictionary var a: [String: Int]
override func viewDidLoad() {
super.viewDidLoad()
self.a["key"] = 5
}
}
The subscript function of the AtomicDicationary is not called!!
Does anybody have any explanation as to why that is?
Property wrappers merely provide an interface for the basic accessor methods, but that’s it. It’s not going to intercept subscripts or other methods.
The original property wrapper proposal SE-0258 shows us what is going on behind the scenes. It contemplates a hypothetical property wrapper, Lazy, in which:
The property declaration
#Lazy var foo = 1738
translates to:
private var _foo: Lazy<Int> = Lazy<Int>(wrappedValue: 1738)
var foo: Int {
get { return _foo.wrappedValue }
set { _foo.wrappedValue = newValue }
}
Note that foo is just an Int computed property. The _foo is the Lazy<Int>.
So, in your a["key"] = 5 example, it will not use your property wrapper’s subscript operator. It will get the value associated with a, use the dictionary’s own subscript operator to update that value (not the property wrapper’s subscript operator), and then it will set the value associated with a.
That’s all the property wrapper is doing, providing the get and set accessors. E.g., the declaration:
#AtomicDictionary var a: [String: Int]
translates to:
private var _a: AtomicDictionary<String, Int> = AtomicDictionary<String, Int>(wrappedValue: [:])
var a: [String: Int] {
get { return _a.wrappedValue }
set { _a.wrappedValue = newValue }
}
Any other methods you define are only accessible through _a in this example, not a (which is just a computed property that gets and sets the wrappedValue of _a).
So, you’re better off just defining a proper type for your “atomic dictionary”:
public class AtomicDictionary<Key: Hashable, Value> {
private var wrappedValue: [Key: Value]
private let queue = DispatchQueue(label: "atomicDictionary.\(UUID().uuidString)", attributes: .concurrent)
init(_ wrappedValue: [Key: Value] = [:]) {
self.wrappedValue = wrappedValue
}
public subscript(key: Key) -> Value? {
get {
queue.sync {
wrappedValue[key]
}
}
set {
queue.async(flags: .barrier) {
self.wrappedValue[key] = newValue
}
}
}
}
And
let a = AtomicDictionary<String, Int>()
That gives you the behavior you want.
And if you are going to supply CustomDebugStringConvertible conformance, make sure to use your synchronization mechanism there, too:
extension AtomicDictionary: CustomDebugStringConvertible {
public var debugDescription: String {
queue.sync { wrappedValue.debugDescription }
}
}
All interaction with the wrapped value must be synchronized.
Obviously you can use this general pattern with whatever synchronization mechanism you want, e.g., the above reader-writer pattern, GCD serial queue, locks, actors, etc. (The reader-writer pattern has a natural appeal, but, in practice, there are generally better mechanisms.)
Needless to say, the above presumes that subscript-level atomicity is sufficient. One should always be wary about general purpose thread-safe collections as often the correctness of our code relies on a higher-level of synchronization.

Swift Combine: Waiting until subscribed to generate values

I'm trying to write a custom publisher that generates some values. Something like this:
class MyPublisher: Publisher {
typealias Output = Int
typealias Failure = Never
private let subject = PassThroughSubject<Int, Never>()
func receive<S>(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output {
subject.receive(subscriber: subscriber)
startSending()
}
func startSending() {
subject.send(1)
subject.send(2)
subject.send(3)
subject.send(completion: .finished)
}
}
I'm trying to figure out how to call startSending() automatically after a subscribing attaches, but I'm not sure if I'm doing it right.
I've just been reading about ConnectablePublisher and was wondering if that might help, but I'm not sure how.
Has anyone tried something like this? How did you do it?
As a continuation of this. One of the changes I've been experimenting with is to locally declared the subject instead of having it as a class variable. I think that solves the issue some raised about multiple subscribers.
public func receive<S>(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output {
let subject = PassthroughSubject<Int, Error>()
defaultValueSubject.receive(subscriber: subscriber)
startSending(to: subject)
}
func startSending(to subject: PassThroughSubject<Int>) {
subject.send(1)
subject.send(2)
subject.send(3)
subject.send(completion: .finished)
}

How to implement a Thread Safe HashTable (PhoneBook) Data Structure in Swift?

I am trying to implement a Thread-Safe PhoneBook object. The phone book should be able to add a person, and look up a person based on their name and phoneNumber. From an implementation perspective this simply involves two hash tables, one associating name -> Person and another associating phone# -> Person.
The caveat is I want this object to be threadSafe. This means I would like to be able to support concurrent lookups in the PhoneBook while ensuring only one thread can add a Person to the PhoneBook at a time. This is the basic reader-writers problem, and I am trying to solve this using GrandCentralDispatch and dispatch barriers. I am struggling to solve this though as I am running into issues.. Below is my Swift playground code:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
public class Person: CustomStringConvertible {
public var description: String {
get {
return "Person: \(name), \(phoneNumber)"
}
}
public var name: String
public var phoneNumber: String
private var readLock = ReaderWriterLock()
public init(name: String, phoneNumber: String) {
self.name = name
self.phoneNumber = phoneNumber
}
public func uniquePerson() -> Person {
let randomID = UUID().uuidString
return Person(name: randomID, phoneNumber: randomID)
}
}
public enum Qos {
case threadSafe, none
}
public class PhoneBook {
private var qualityOfService: Qos = .none
public var nameToPersonMap = [String: Person]()
public var phoneNumberToPersonMap = [String: Person]()
private var readWriteLock = ReaderWriterLock()
public init(_ qos: Qos) {
self.qualityOfService = qos
}
public func personByName(_ name: String) -> Person? {
var person: Person? = nil
if qualityOfService == .threadSafe {
readWriteLock.concurrentlyRead { [weak self] in
guard let strongSelf = self else { return }
person = strongSelf.nameToPersonMap[name]
}
} else {
person = nameToPersonMap[name]
}
return person
}
public func personByPhoneNumber( _ phoneNumber: String) -> Person? {
var person: Person? = nil
if qualityOfService == .threadSafe {
readWriteLock.concurrentlyRead { [weak self] in
guard let strongSelf = self else { return }
person = strongSelf.phoneNumberToPersonMap[phoneNumber]
}
} else {
person = phoneNumberToPersonMap[phoneNumber]
}
return person
}
public func addPerson(_ person: Person) {
if qualityOfService == .threadSafe {
readWriteLock.exclusivelyWrite { [weak self] in
guard let strongSelf = self else { return }
strongSelf.nameToPersonMap[person.name] = person
strongSelf.phoneNumberToPersonMap[person.phoneNumber] = person
}
} else {
nameToPersonMap[person.name] = person
phoneNumberToPersonMap[person.phoneNumber] = person
}
}
}
// A ReaderWriterLock implemented using GCD and OS Barriers.
public class ReaderWriterLock {
private let concurrentQueue = DispatchQueue(label: "com.ReaderWriterLock.Queue", attributes: DispatchQueue.Attributes.concurrent)
private var writeClosure: (() -> Void)!
public func concurrentlyRead(_ readClosure: (() -> Void)) {
concurrentQueue.sync {
readClosure()
}
}
public func exclusivelyWrite(_ writeClosure: #escaping (() -> Void)) {
self.writeClosure = writeClosure
concurrentQueue.async(flags: .barrier) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.writeClosure()
}
}
}
// MARK: Testing the synchronization and thread-safety
for _ in 0..<5 {
let iterations = 1000
let phoneBook = PhoneBook(.none)
let concurrentTestQueue = DispatchQueue(label: "com.PhoneBookTest.Queue", attributes: DispatchQueue.Attributes.concurrent)
for _ in 0..<iterations {
let person = Person(name: "", phoneNumber: "").uniquePerson()
concurrentTestQueue.async {
phoneBook.addPerson(person)
}
}
sleep(10)
print(phoneBook.nameToPersonMap.count)
}
To test my code I run 1000 concurrent threads that simply add a new Person to the PhoneBook. Each Person is unique so after the 1000 threads complete I am expecting the PhoneBook to contain a count of 1000. Everytime I perform a write I perform a dispatch_barrier call, update the hash tables, and return. To my knowledge this is all we need to do; however, after repeated runs of the 1000 threads I get the number of entries in the PhoneBook to be inconsistent and all over the place:
Phone Book Entries: 856
Phone Book Entries: 901
Phone Book Entries: 876
Phone Book Entries: 902
Phone Book Entries: 912
Can anyone please help me figure out what is going on? Is there something wrong with my locking code or even worse something wrong with how my test is constructed? I am very new to this multi-threaded problem space, thanks!
The problem is your ReaderWriterLock. You are saving the writeClosure as a property, and then asynchronously dispatching a closure that calls that saved property. But if another exclusiveWrite came in during the intervening period of time, your writeClosure property would be replaced with the new closure.
In this case, it means that you can be adding the same Person multiple times. And because you're using a dictionary, those duplicates have the same key, and therefore don't result in you're seeing all 1000 entries.
You can actually simplify ReaderWriterLock, completely eliminating that property. I’d also make concurrentRead a generic, returning the value (just like sync does), and rethrowing any errors (if any).
public class ReaderWriterLock {
private let queue = DispatchQueue(label: "com.domain.app.rwLock", attributes: .concurrent)
public func concurrentlyRead<T>(_ block: (() throws -> T)) rethrows -> T {
return try queue.sync {
try block()
}
}
public func exclusivelyWrite(_ block: #escaping (() -> Void)) {
queue.async(flags: .barrier) {
block()
}
}
}
A couple of other, unrelated observations:
By the way, this simplified ReaderWriterLock happens to solves another concern. That writeClosure property, which we've now removed, could have easily introduced a strong reference cycle.
Yes, you were scrupulous about using [weak self], so there wasn't any strong reference cycle, but it was possible. I would advise that wherever you employ a closure property, that you set that closure property to nil when you're done with it, so any strong references that closure may have accidentally entailed will be resolved. That way a persistent strong reference cycle is never possible. (Plus, the closure itself and any local variables or other external references it has will be resolved.)
You're sleeping for 10 seconds. That should be more than enough, but I'd advise against just adding random sleep calls (because you never can be 100% sure). Fortunately, you have a concurrent queue, so you can use that:
concurrentTestQueue.async(flags: .barrier) {
print(phoneBook.count)
}
Because of that barrier, it will wait until everything else you put on that queue is done.
Note, I did not just print nameToPersonMap.count. This array has been carefully synchronized within PhoneBook, so you can't just let random, external classes access it directly without synchronization.
Whenever you have some property which you're synchronizing internally, it should be private and then create a thread-safe function/variable to retrieve whatever you need:
public class PhoneBook {
private var nameToPersonMap = [String: Person]()
private var phoneNumberToPersonMap = [String: Person]()
...
var count: Int {
return readWriteLock.concurrentlyRead {
nameToPersonMap.count
}
}
}
You say you're testing thread safety, but then created PhoneBook with .none option (achieving no thread-safety). In that scenario, I'd expect problems. You have to create your PhoneBook with the .threadSafe option.
You have a number of strongSelf patterns. That's rather unswifty. It is generally not needed in Swift as you can use [weak self] and then just do optional chaining.
Pulling all of this together, here is my final playground:
PlaygroundPage.current.needsIndefiniteExecution = true
public class Person {
public let name: String
public let phoneNumber: String
public init(name: String, phoneNumber: String) {
self.name = name
self.phoneNumber = phoneNumber
}
public static func uniquePerson() -> Person {
let randomID = UUID().uuidString
return Person(name: randomID, phoneNumber: randomID)
}
}
extension Person: CustomStringConvertible {
public var description: String {
return "Person: \(name), \(phoneNumber)"
}
}
public enum ThreadSafety { // Changed the name from Qos, because this has nothing to do with quality of service, but is just a question of thread safety
case threadSafe, none
}
public class PhoneBook {
private var threadSafety: ThreadSafety
private var nameToPersonMap = [String: Person]() // if you're synchronizing these, you really shouldn't expose them to the public
private var phoneNumberToPersonMap = [String: Person]() // if you're synchronizing these, you really shouldn't expose them to the public
private var readWriteLock = ReaderWriterLock()
public init(_ threadSafety: ThreadSafety) {
self.threadSafety = threadSafety
}
public func personByName(_ name: String) -> Person? {
if threadSafety == .threadSafe {
return readWriteLock.concurrentlyRead { [weak self] in
self?.nameToPersonMap[name]
}
} else {
return nameToPersonMap[name]
}
}
public func personByPhoneNumber(_ phoneNumber: String) -> Person? {
if threadSafety == .threadSafe {
return readWriteLock.concurrentlyRead { [weak self] in
self?.phoneNumberToPersonMap[phoneNumber]
}
} else {
return phoneNumberToPersonMap[phoneNumber]
}
}
public func addPerson(_ person: Person) {
if threadSafety == .threadSafe {
readWriteLock.exclusivelyWrite { [weak self] in
self?.nameToPersonMap[person.name] = person
self?.phoneNumberToPersonMap[person.phoneNumber] = person
}
} else {
nameToPersonMap[person.name] = person
phoneNumberToPersonMap[person.phoneNumber] = person
}
}
var count: Int {
return readWriteLock.concurrentlyRead {
nameToPersonMap.count
}
}
}
// A ReaderWriterLock implemented using GCD concurrent queue and barriers.
public class ReaderWriterLock {
private let queue = DispatchQueue(label: "com.domain.app.rwLock", attributes: .concurrent)
public func concurrentlyRead<T>(_ block: (() throws -> T)) rethrows -> T {
return try queue.sync {
try block()
}
}
public func exclusivelyWrite(_ block: #escaping (() -> Void)) {
queue.async(flags: .barrier) {
block()
}
}
}
for _ in 0 ..< 5 {
let iterations = 1000
let phoneBook = PhoneBook(.threadSafe)
let concurrentTestQueue = DispatchQueue(label: "com.PhoneBookTest.Queue", attributes: .concurrent)
for _ in 0..<iterations {
let person = Person.uniquePerson()
concurrentTestQueue.async {
phoneBook.addPerson(person)
}
}
concurrentTestQueue.async(flags: .barrier) {
print(phoneBook.count)
}
}
Personally, I'd be inclined to take it a step further and
move the synchronization into a generic class; and
change the model to be an array of Person object, so that:
The model supports multiple people with the same or phone number; and
You can use value types if you want.
For example:
public struct Person {
public let name: String
public let phoneNumber: String
public static func uniquePerson() -> Person {
return Person(name: UUID().uuidString, phoneNumber: UUID().uuidString)
}
}
public struct PhoneBook {
private var synchronizedPeople = Synchronized([Person]())
public func people(name: String? = nil, phone: String? = nil) -> [Person]? {
return synchronizedPeople.value.filter {
(name == nil || $0.name == name) && (phone == nil || $0.phoneNumber == phone)
}
}
public func append(_ person: Person) {
synchronizedPeople.writer { people in
people.append(person)
}
}
public var count: Int {
return synchronizedPeople.reader { $0.count }
}
}
/// A structure to provide thread-safe access to some underlying object using reader-writer pattern.
public class Synchronized<T> {
/// Private value. Use `public` `value` computed property (or `reader` and `writer` methods)
/// for safe, thread-safe access to this underlying value.
private var _value: T
/// Private reader-write synchronization queue
private let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".synchronized", qos: .default, attributes: .concurrent)
/// Create `Synchronized` object
///
/// - Parameter value: The initial value to be synchronized.
public init(_ value: T) {
_value = value
}
/// A threadsafe variable to set and get the underlying object, as a convenience when higher level synchronization is not needed
public var value: T {
get { reader { $0 } }
set { writer { $0 = newValue } }
}
/// A "reader" method to allow thread-safe, read-only concurrent access to the underlying object.
///
/// - Warning: If the underlying object is a reference type, you are responsible for making sure you
/// do not mutating anything. If you stick with value types (`struct` or primitive types),
/// this will be enforced for you.
public func reader<U>(_ block: (T) throws -> U) rethrows -> U {
return try queue.sync { try block(_value) }
}
/// A "writer" method to allow thread-safe write with barrier to the underlying object
func writer(_ block: #escaping (inout T) -> Void) {
queue.async(flags: .barrier) {
block(&self._value)
}
}
}
In some cases you use might NSCache class. The documentation claims that it's thread safe:
You can add, remove, and query items in the cache from different threads without having to lock the cache yourself.
Here is an article that describes quite useful tricks related to NSCache
I don’t think you are using it wrong :).
The original (on macos) generates:
0 swift 0x000000010c9c536a PrintStackTraceSignalHandler(void*) + 42
1 swift 0x000000010c9c47a6 SignalHandler(int) + 662
2 libsystem_platform.dylib 0x00007fffbbdadb3a _sigtramp + 26
3 libsystem_platform.dylib 000000000000000000 _sigtramp + 1143284960
4 libswiftCore.dylib 0x0000000112696944 _T0SSwcp + 36
5 libswiftCore.dylib 0x000000011245fa92 _T0s24_VariantDictionaryBufferO018ensureUniqueNativeC0Sb11reallocated_Sb15capacityChangedtSiF + 1634
6 libswiftCore.dylib 0x0000000112461fd2 _T0s24_VariantDictionaryBufferO17nativeUpdateValueq_Sgq__x6forKeytF + 1074
If you remove the ‘.concurrent’ from your ReaderWriter queue, "the problem disappears”.©
If you restore the .concurrent, but change the async invocation in the writer side to be sync:
swift(10504,0x70000896f000) malloc: *** error for object 0x7fcaa440cee8: incorrect checksum for freed object - object was probably modified after being freed.
Which would be a bit astonishing if it weren’t swift?
I dug in, replaced your ‘string’ based array with an Int one by interposing a hash function, replaced the sleep(10) with a barrier dispatch to flush any laggardly blocks through, and that made it more reproducibly crash with the somewhat more helpful:
x(10534,0x700000f01000) malloc: *** error for object 0x7f8c9ee00008: incorrect checksum for freed object - object was probably modified after being freed.
But when a search of the source revealed no malloc or free, perhaps the stack dump is more useful.
Anyways, best way to solve your problem: use go instead; it actually makes sense.

Swift 3 generic function ambiguous return

I need a single function to resolve different dependencies in a class.
But there is a compilation error appears.
Is it possible to create that generic function or there are some compiler constraints in Swift?
import Foundation
protocol Client: class {
var description: String { get }
}
final class ImportantPerson : Client {
var description: String {
return "Important person"
}
}
protocol Order: class {
var description: String { get }
}
final class LastOrder : Order {
var description: String {
return "Last order"
}
}
final class A {
fileprivate func resolveDependency<T>() -> T {
return resolve() as T
}
private func resolve() -> Client {
return ImportantPerson()
}
private func resolve() -> Order {
return LastOrder()
}
}
let a = A()
let client: Client = a.resolveDependency()
let order: Order = a.resolveDependency()
print("Client: \(client.description)")
print("Order: \(order.description)")
EDIT: This question is not about if Swift allows to create two functions that differs only by return type. I know it's possible. I think there are some artificial constraints in the compiler but not in the fundamental logic that should allow to infer needed type from a context.
Let's put yourself into the compiler's shoes. Imagine that this was not causing an error and you had one signature with different outputs.
Whenever you call resolveDependency<T>() -> T, the compiler will return you a type T which is an instance conforming to a protocol in your case.
In your code you call this method with different instances conforming to the same protocol. At that stage the compiler has no idea about this. All it knows is that you have passed an instance of T and it needs to give you a result in shape of T
There is no problem until this point. As soon as you execute
return resolve() as! T
The compiler will be confused. I have a T but I don't know which resolve() I will call... All I know is that I have a T. How would I know if this is an Order or a Client ?
In order to prevent such confusions we have compiler-time errors. At least this is the case for Swift. (I don't know how this works in other languages)
You need to define different methods with different signatures and cast your type accordingly to get a similar result
fileprivate func resolveDependency<T>() -> T {
// check if this is an Order
resolveForOrder()
// check if this is a Client
resolveForClient()
}
private func resolveForOrder() -> Order {
return LastOrder()
}
private func resolveForClient() -> Client {
return ImportantPerson()
}
This is like trying to fix a space shuttle engine with a car mechanic. Yes, they both have an engine, they both run on fuel but the mechanic only knows how to fix your car's engine he is not a rocket scientist(!)
This code works fine:
import Foundation
protocol Client: class {
var description: String { get }
}
final class ImportantPerson : Client {
var description: String {
return "Important person"
}
}
protocol Order: class {
var description: String { get }
}
final class LastOrder : Order {
var description: String {
return "Last order"
}
}
final class A {
fileprivate func resolveDependency<T>() -> T {
if T.self == Client.self {
return resolve() as Client as! T
} else {
return resolve() as Order as! T
}
}
private func resolve() -> Client {
return ImportantPerson()
}
private func resolve() -> Order {
return LastOrder()
}
}
let a = A()
let client: Client = a.resolveDependency()
let order: Order = a.resolveDependency()
print("Client: \(client.description)")
print("Order: \(order.description)")
But I believe that compiler should resolve the if else clause himself, it's not so hard as I suppose.
Also there is some bug in the compiler when it tries to match types like that:
switch T.self {
case is Client:
return resolve() as Client as! T
default:
return resolve() as Order as! T
}