alexiscreuzot / SwiftyGif

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

Fix objc_getAssociatedObject crashing on weak reference #160

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

Solution

Introduce a objc_setAssociatedWeakObject and objc_getAssociatedWeakObject to avoid the crash. Instead of using the OBJC_ASSOCIATION_ASSIGN with the pointer directly, We can 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 accessing the nil delegate.