From Apple docs:
“Each access-level modifier above optionally accepts a single argument, which consists of the keyword set enclosed in parentheses (for instance, private(set)). Use this form of an access-level modifier when you want to specify an access level for the setter of a variable or subscript that’s less than or equal to the access level of the variable or subscript itself, as discussed in Getters and Setters.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/ru/jEUH0.l
An example which I try to test in Playground:
import UIKit
class A {
private(set) var name: String {
get { return "Hello, \(self.name)" }
set { self.name = "Unknown" }
}
init(_ name:String) {
self.name = name
}
}
let a = A("Andrew")
a.name = "Hello"
An error which I get in Console Output:
Playground execution failed: error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=2, address=0x7fff5056eff8).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
* thread #1: tid = 0xea721, 0x00000001104f308c libsystem_pthread.dylib`__mtx_droplock + 222, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7fff5056eff8)
* frame #0: 0x00000001104f308c libsystem_pthread.dylib`__mtx_droplock + 222
frame #1: 0x00000001104f2f07 libsystem_pthread.dylib`pthread_mutex_unlock + 68
frame #2: 0x000000010ffbd2b5 libc++.1.dylib`std::__1::mutex::unlock() + 9
frame #3: 0x000000010f040b94 libswift_stdlib_core.dylib`swift_getGenericMetadata + 260
frame #4: 0x000000010ef28a24 libswift_stdlib_core.dylib`Swift.IndexingGenerator.init <A : Swift._Collection>(Swift.IndexingGenerator<A>.Type)(A) -> Swift.IndexingGenerator<A> + 164
frame #5: 0x000000010ef55f1a libswift_stdlib_core.dylib`protocol witness for Swift._Sequence_.generate <A : Swift._Sequence_>(@inout Swift._Sequence_.Self)() -> Swift._Sequence_.Self.GeneratorType in conformance Swift._ContiguousArrayBuffer : Swift._Sequence_ + 154
frame #6: 0x000000010ef284d5 libswift_stdlib_core.dylib`Swift._copyCollectionToNativeArrayBuffer <A : protocol<Swift._Collection, Swift._Sequence_>>(A) -> Swift._ContiguousArrayBuffer<A.GeneratorType.Element> + 1061
frame #7: 0x000000010ef40281 libswift_stdlib_core.dylib`Swift.Array.convertFromArrayLiteral <A>(Swift.Array<A>.Type)(Swift.Array<A>...) -> Swift.Array<A> + 641
frame #8: 0x000000010f1eaae4 PlaygroundLogger`Swift.UInt64.toBytes (Swift.UInt64)() -> Swift.Array<Swift.UInt8> + 292
frame #9: 0x000000010f1eb6a4 PlaygroundLogger`protocol witness for PlaygroundLogger.ToBytes.toBytes <A : PlaygroundLogger.ToBytes>(@inout PlaygroundLogger.ToBytes.Self)() -> Swift.Array<Swift.UInt8> in conformance Swift.UInt64 : PlaygroundLogger.ToBytes + 20
frame #10: 0x000000010f1dbe3d PlaygroundLogger`PlaygroundLogger.BytesStream.write (PlaygroundLogger.BytesStream)(PlaygroundLogger.ToBytes) -> PlaygroundLogger.BytesStream + 77
frame #11: 0x000000010f1dbd74 PlaygroundLogger`PlaygroundLogger.BytesStream.write (PlaygroundLogger.BytesStream)(Swift.String) -> PlaygroundLogger.BytesStream + 164
frame #12: 0x000000010f20f04b PlaygroundLogger`PlaygroundLogger.PlaygroundWriter.encode_config_info (PlaygroundLogger.PlaygroundWriter)() -> () + 203
frame #13: 0x000000010f20f2bf PlaygroundLogger`PlaygroundLogger.PlaygroundWriter.encode_header (PlaygroundLogger.PlaygroundWriter)() -> () + 127
frame #14: 0x000000010f20ecda PlaygroundLogger`PlaygroundLogger.PlaygroundScopeWriter.encode_scope_event (PlaygroundLogger.PlaygroundScopeWriter)(PlaygroundLogger.ScopeEvent) -> () + 58
frame #15: 0x000000010f1eb997 PlaygroundLogger`playground_log_scope_entry + 87
frame #16: 0x000000011ae20771
What am I doing wrong? Am I missing something?
PS1
This example works fine:
struct TrackedString {
private(set) var numberOfEdits = 0
var value: String = "" {
didSet {
numberOfEdits++
}
}
}
var stringToEdit = TrackedString()
stringToEdit.value = "Hello"
stringToEdit
stringToEdit.numberOfEdits += 10
stringToEdit
More from docs:
“the access level for the numberOfEdits property is marked with a private(set) modifier to indicate that the property should be settable only from within the same source file as the TrackedString structure’s definition.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/ru/jEUH0.l
But thats not what I need. Is it possible to disallow to set variable numberOfEdits outside the struct/class?
However, the access level for the numberOfEdits property is marked with a private(set) modifier to indicate that the property's getter still has the default access level of internal, but the property is settable only from within code that's part of the TrackedString structure.
public(get) private(set) var foo:String. to be doubly explicit. The goal is that foo should have a getter which is accessible from outside the module, but a private setter. Using only private(set) means that the getter is internal - so not accessible outside the module.
Private setters allow you to create read-only public or protected properties. That's it.
Your problem lies here:
set { self.name = "Unknown" }
You're setting the value of a computed property within its own setter. This causes infinite recursion. Don't forget that this is a computed property: it doesn't actually have storage. You don't have a variable "self.name" to put anything in; you only have a couple of functions to calculate it. Computed properties like this should use other, non-computed variables for storage. (That's why your structure example works, by the way: you're using a real property with storage.)
You're not being helped in your debugging by fact of running in a Playground. Don't get me wrong: Playgrounds are great. However, in this case, it's taking many seconds to crash, so the crash probably isn't showing up when you expect after an edit. It's also not showing you a full stack trace (which is massive for the problem you're getting, having reproduced it in a "real" app, and might have made it rather more obvious that you'd blown the stack.) When I built and ran the above as a console app, it finally blew up with a stack trace 104,832 calls deep, all but two of which were ...private_cmd.A.name.setter...
. Bit of a clue :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With