Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: Trying to put the stack in unreadable memory at:

Tags:

swift

I am trying to add additional properties to UIViewController.

Code:

protocol AdditionalStoredProperties
   {
        associatedtype Title
        func getAssociatedObject<Title>(key: UnsafePointer<Title> , 
defValue : Title)->Title
    }

extension AdditionalStoredProperties
{
    func getAssociatedObject<Title>( key: UnsafePointer<Title> , defValue : Title)->Title
    {
        guard let actual_value = objc_getAssociatedObject(self as! AnyObject, key) as? Title else
        {
            return defValue
        }
        return actual_value
    }

 }

extension UIViewController:AdditionalStoredProperties
{
    typealias Title = String
    var previousPage : String
     {
        get { return getAssociatedObject(&self.previousPage, defValue: self.previousPage) }
        set { objc_setAssociatedObject(self, &self.previousPage, newValue, .OBJC_ASSOCIATION_RETAIN)}
    }
}

But I am getting the following error:

Error: Trying to put the stack in unreadable memory at:

I know that we cannot directly add stored properties to extensions so I am trying it add using objc_setAssociatedObject()

like image 449
Kiah_Dev Avatar asked Aug 07 '17 07:08

Kiah_Dev


Video Answer


2 Answers

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.

like image 148
Hamish Avatar answered Oct 14 '22 04:10

Hamish


If someone has the below scenario

If your method is getting called recursively, you may get this error.

like image 31
Saranjith Avatar answered Oct 14 '22 04:10

Saranjith