alexiscreuzot / SwiftyGif

High performance GIF engine
MIT License
1.99k stars 210 forks source link

Fix: objc_getAssociatedObject crashing on weak reference #161

Closed nuno-vieira closed 2 years ago

nuno-vieira commented 2 years ago

Description

It looks that the previous fix: https://github.com/kirualex/SwiftyGif/pull/158 didn't actually fix the root of the problem. After more investigation, it seems the issue is related to objc_associatedObject API. The OBJC_ASSOCIATION_ASSIGN does not weakify references. More details about the issue can be found here: https://stackoverflow.com/questions/16569840/using-objc-setassociatedobject-with-weak-references

How to reproduce

This can actually be easily reproducible on a Playground:

import UIKit

private let _delegateKey = malloc(4)

@objc protocol SwiftyGifDelegate: AnyObject { }
class SomeObject: SwiftyGifDelegate { }

extension UIImageView {
    var delegate: SwiftyGifDelegate? {
        get { return objc_getAssociatedObject(self, _delegateKey!) as? SwiftyGifDelegate }
        set { objc_setAssociatedObject(self, _delegateKey!, newValue, .OBJC_ASSOCIATION_ASSIGN) }
    }
}

let imageView = UIImageView()

autoreleasepool {
    let object = SomeObject()
    imageView.delegate = object
}

debugPrint(imageView.delegate)

Calling the debugPrint(imageView.delegate) will crash.

Solution

Introduce a objc_setAssociatedWeakObject and objc_getAssociatedWeakObject to avoid the crash. Instead of using the OBJC_ASSOCIATION_ASSIGN with the pointer directly, we use a block that stores a weak reference, and then use OBJC_ASSOCIATION_COPY for the block.

On the playground, if you replace the previous methods with the new ones, you will see that it doesn't crash anymore when accessing the nil delegate.

alexiscreuzot commented 2 years ago

thanks for your PR