Sample project that demonstrates some problems in the Gifu animated UIImage library.
Project created and built in Xcode 7.3 (7D175) and run on iOS 9.3 simulators and devices.
Setup
Requires CocoaPods to include the Gifu dependency
git clone https://github.com/mr-seiler/gifu-bug-demo.git
cd gifu-bug-demo
pod install
open gifu-test.xcworkspace
The two different issues have their own tags, listed below.
Checkout the appropriate tag and run the project to observe the issue.
Issues Demonstrated
precaching failure
Tag: broken-precache
Issue: kaishin/Gifu#56
Method:
git checkout broken-precache
- Use
pod install
and/orpod update
to be sure the Gifu version from commmit 91ba745 is being used - Open the workspace
- Run the project on device or in simulator
- Tap "Go places" in the running application
- The test GIF contains 90 frames (displayed for 1/4 s each), but the GIF will loop after only 50 frames have displayed
Analysis:
If using Gifu from the master branch as of 91ba745, the AnimatableImageView will not display GIFs longer than 50 frames in their entirety. This can be demonstarted with any GIF of sufficient length; the one in this test project counts down from 90, one number per frame.
Changing the framePreloadCount
property on AnimatableImageView
caused more or less of the GIF to be played depending on the new value, and testing quickly verifies that the AnimatableImageView only loads the number of frames specified in framePreloadCount
.
Using git bisect
, I have determined that the issue was introduced in 91ba745, which is changes to accomodate the new Swift 2.2 syntax. The most likely culprit is this section in the Animator
class.
memory leak
Tag: repro-memory-leak
Issue: kaishin/Gifu#12 ?
Pull Request: kaishin/Gifu#55
Method:
git checkout repro-memory-leak
- Open the workspace in Xcode
- Run the project on any simulator or device
- Open the Debug Navigator (⌘+6) and select "Memory" to open the Memory report
- In the running application, tap "Go Places" to push the view controller containing the Gif onto the navigation stack, then tap the back button on the navigation bar.
- Repeat step 5 while observing memory graph
For more detail, use Instruments:
- Edit build scheme > Profile > select Debug build configuration
- Xcode > Open Developer Tool > Instruments
- Select the "Allocations" profiling template
- Start recording
- Mark Generation before pushing the view controller containing the Gifu AnimatableImageView and after going back (can repeat for multiple generations)
- Retained objects can be inspected in the Generations list
retain/release crash
Tag: repro-zombie-bug
Issue: kaishin/Gifu#53
Pull Request: kaishin/Gifu#54
Method:
git checkout repro-zombie-bug
- Edit build scheme > Run > Diagnostics > Enable Zombie Objects (this allows us to see which object is causing the crash)
- Run the project
- In the app, tap "Go Places" to push the view controller containg the AnimatableImageView, then the back button in the navigation bar to pop and deallocate
- Observe crash
Analysis:
The crash occurs in cases when the AnimatableImageView is deallocated without having ever loaded any image data.
The AnimatableImageView
creates a CADisplayLink
as a lazily initialized property, passing itself as the object to be retained by the display link. the AnimatableImageView
s deinit
block calls displayLink.invalidate()
, which instructs the CADisplayLink
to release its retained object.
If prepareForAnimation(imageData:)
is never called, then attachDisplayLink()
is never called, the lazy property is never accessed and the CADisplayLink
instance is not created.
If no other accesses to that property ever occur before the AnimatableImageView
is deallocated, the first access of the lazy property will be inside the deinit
block, so the display link will be created while the Gifu view is being deinitialized. The CADisplay link immediately tries to retain and release an object which is already being deallocated and a crash occurs.