Error: Trying to put the stack in unreadable memory at:
If someone has the below scenario
If your method is getting called recursively, you may get this error.
There are a number of things wrong with what you're doing:
Attempting to access
self.previousPage
within its own getter will call itself recursively.You cannot use
&self.previousPage
as a stable or unique pointer value, as it'll be a pointer to a temporary variable (because you're dealing a computed property). You cannot therefore use it as the key for an associated object. Swift only guarantees stable and unique pointer values for static and global stored variables (see this Q&A for more info).You should make
AdditionalStoredProperties
a class-bound protocol (with: class
), as you can only add associated objects to Objective-C classes (which, on Apple platforms, Swift classes are built on top of). While you can bridge, for example, astruct
toAnyObject
(it'll get boxed in an opaque Obj-C compatible wrapper), it is merely that; a bridge. There's no guarantee you'll get the same instance back, therefore no guarantee the associated objects will persist.You probably didn't mean for
Title
to be an associated type of your protocol; you're not using it for anything (the generic placeholderTitle
defined bygetAssociatedObject(key:defValue:)
is completely unrelated).
Bearing those points in mind, here's a fixed version of your code:
protocol AdditionalStoredProperties : class {
func getAssociatedObject<T>(ofType: T.Type, key: UnsafeRawPointer,
defaultValue: @autoclosure () -> T) -> T
}
extension AdditionalStoredProperties {
func getAssociatedObject<T>(ofType: T.Type, key: UnsafeRawPointer,
defaultValue: @autoclosure () -> T) -> T {
// or: return objc_getAssociatedObject(self, key) as? T ?? defaultValue()
guard let actualValue = objc_getAssociatedObject(self, key) as? T else {
return defaultValue()
}
return actualValue
}
}
extension UIViewController : AdditionalStoredProperties {
private enum AssociatedObjectKeys {
static var previousPage: Never?
}
var previousPage: String {
get {
// return the associated object with a default of "" (feel free to change)
return getAssociatedObject(ofType: String.self,
key: &AssociatedObjectKeys.previousPage,
defaultValue: "")
}
set {
objc_setAssociatedObject(self, &AssociatedObjectKeys.previousPage,
newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
}
Note that we're:
Using a
static
stored property in order to get a pointer value to use as the key for our associated object. Again, this works because Swift guarantees stable and unique pointer values for static and global stored variables.Using
@autoclosure
for thedefaultValue:
parameter, as it may not need to be evaluated if an associated object is already present.Having the
key:
parameter take anUnsafeRawPointer
, as the type of the pointee is irrelevant; it's merely the location in memory that's used as the key.Explicitly satisfying the generic placeholder with an
ofType:
parameter. This is mainly a matter of preference, but I prefer to spell these things out explicitly rather than relying on type inference.Using
camelCase
instead ofsnake_case
, as is Swift convention.