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, a struct to AnyObject (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 placeholder Title defined by getAssociatedObject(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 the defaultValue: parameter, as it may not need to be evaluated if an associated object is already present.

  • Having the key: parameter take an UnsafeRawPointer, 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 of snake_case, as is Swift convention.

Tags:

Swift