Question

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

enter image description here

But thats not what I need. Is it possible to disallow to set variable numberOfEdits outside the struct/class?

Was it helpful?

Solution

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 :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top