Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: Memory Leak, Auto-retry download, Shared Resource Propagation #433

Merged
merged 7 commits into from
Apr 23, 2019

Conversation

StevenMasini
Copy link
Contributor

@StevenMasini StevenMasini commented Mar 20, 2019

Changelogs

1- Use weak self reference in progression and completion block to avoid memory leaks

It's recommended to never retain the strong reference to self into blocks.

Blocks maintain strong references to any captured objects, including self, which means that it’s easy to end up with a strong reference cycle

Avoid Strong Reference Cycles when Capturing self

2- Broadcast a notification when different instance share the same source succeed to download an image

As mentioned in #432, when two instance of FFFastImageView share the same source, but one of them fail to download while the second succeed, the result of the second won't be shared with the first one. Which result with instance of FFFastImageView staying empty.

To remedy this issue I used NSNotificationCenter to attach an observer and post a notification whenever an instance of FFFastImageView succeed to download. Works very well on my own project, it's a game changer for me.

3- Implement auto-retry failed download logic

As mentioned in #432, we can't only rely on SDWebImageRetryFailed to auto-retry when a download failed.

Also apparently this feature has been requested on the SDWebImage repository and is still waiting for implementation.
SDWebImage/SDWebImage#2587

So I implemented my own auto-retry mechanism which will retry after 1.5s, 3s and 4.5s before failing. In total it makes the timeout for the image download to 9s.

Let me know if you want to bring more flexibility to this feature.

4- Update Objective-C syntax to highlight instance properties

Being a former Objective-C iOS engineer myself, I updated the code to the modern Objective-C syntax.

Adopting Modern Objective-C
Migrating to Modern Objective-C See slides 47 and 48

Updating the syntax helped me to realized that their were more invocation to strong self in blocks that I initially thought. See #398

5- Use RCTDirectEventBlock instead of RCTBubblingEventBlock

From what I understand from the React Native documentation.

RCTBubblingEventBlock are used for UI related events (e.g: the user pinch the map)
RCTDirectEventBlock are used for more straight events (e.g web page failed to load callbacks).

So I replaced all the RCTBubblingEventBlock types by RCTDirectEventBlock as we don't handle user interaction.

Also found this gist about the subject.

@codecov
Copy link

codecov bot commented Mar 20, 2019

Codecov Report

Merging #433 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #433   +/-   ##
=======================================
  Coverage   94.73%   94.73%           
=======================================
  Files           1        1           
  Lines          19       19           
=======================================
  Hits           18       18           
  Misses          1        1

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4849f2b...1c19f96. Read the comment docs.

@StevenMasini
Copy link
Contributor Author

StevenMasini commented Mar 21, 2019

Just in case someone wants to try this branch on their own project, I made a npm module.

In your package.json use

"@stevenmasini/react-native-fast-image": "^5.2.0",

For iOS, you will need to update your XCode project to use the
./node_modules/@stevenmasini/react-native-fast-image/ios/FastImage.xcodeproj.

For Android, in you settings.gradle

include ':react-native-fast-image'
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/@stevenmasini/react-native-fast-image/android')

@evanjmg
Copy link

evanjmg commented Apr 1, 2019

@DylanVann any feedback on this?

@evanjmg
Copy link

evanjmg commented Apr 2, 2019

I tried this out and I'm getting repeated images in cells. Is there anything changed in this that could cause this issue? @StevenMasini any thoughts? I'm using .map((item=> <FastImage source={{ uri }} />) - UPDATE: ah this happens in 5.2.0 as well. Anyway to set a key?

@StevenMasini
Copy link
Contributor Author

StevenMasini commented Apr 3, 2019

@evanjmg

Do you means the warning that <FlatList /> will trigger if you don't specify a key in the renderItem method ?

Then if that the case you just need to add a prop key to your element.

Something like

.map( (item, index) => {
     <FastImage key={`0x${index}`} source={{ uri: item.uri }} />
})

If not can you give me more detail ?

@StevenMasini
Copy link
Contributor Author

StevenMasini commented Apr 7, 2019

@DylanVann I am asking once again to review this PR 🙏

I am already using it in Production in my own app via @steven/react-native-fast-image module.

Also this PR is a follow up and aim to fix these issues #371 #421 #394

@evanjmg
Copy link

evanjmg commented Apr 7, 2019

I'm using it in production too 👍

}
}

- (void)imageDidLoadObserver:(NSNotification *)notification {
Copy link

@evanjmg evanjmg Apr 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the line that is crashing for me. @StevenMasini ? any ideas?

Fatal Exception: NSInvalidArgumentException
0  CoreFoundation                 0x208d19eb8 __exceptionPreprocess
1  libobjc.A.dylib                0x207f13f18 objc_exception_throw
2  CoreFoundation                 0x208c2ef10 -[NSOrderedSet initWithSet:copyItems:]
3  CoreFoundation                 0x208d1f924 ___forwarding___
4  CoreFoundation                 0x208d216f0 _CF_forwarding_prep_0
5  App                      0x102ddae0c -[FFFastImageView imageDidLoadObserver:] (FFFastImageView.m:79)
6  Foundation                     0x2097b7618 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_2
7  CoreFoundation                 0x208c85fcc __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__
8  CoreFoundation                 0x208c85f8c ___CFXRegistrationPost_block_invoke
9  CoreFoundation                 0x208c85434 _CFXRegistrationPost
10 CoreFoundation                 0x208c850c0 ___CFXNotificationPost_block_invoke
11 CoreFoundation                 0x208bfaae4 -[_CFXNotificationRegistrar find:object:observer:enumerator:]
12 CoreFoundation                 0x208c84b44 _CFXNotificationPost
13 Foundation                     0x2096a4b24 -[NSNotificationCenter postNotificationName:object:userInfo:]
14 UIKitCore                      0x236e74ca4 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:]
15 UIKitCore                      0x236e75024 -[UIViewAnimationState animationDidStop:finished:]
16 QuartzCore                     0x20d3d2338 CA::Layer::run_animation_callbacks(void*)
``` - -[UIViewAnimationState url]: unrecognized selector sent to instance 0x1120ad720

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps check for source.url?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps check for source.url?

Any way to reproduce the crash ? What was the source.url ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this crash is link with the observer that I attached to the event.

[FFFastImageView imageDidLoadObserver:

I will investigate this. Would be helpful if you could tell me how do you call <FastImage>, which props to you give to it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still I’m having trouble reproducing as my app has hundreds of images in it and I need to add more metadata to crashalytics. The only props I use are onLoad, source.headers, source.uri and priority. Perhaps it’s an image resp error?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now I've reverted my production build to prevent crashes; hopefully I won't get memory leaks with 5.2.0. I'll try to reproduce on staging as I develop new features.

Copy link
Contributor Author

@StevenMasini StevenMasini Apr 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evanjmg Could you use XCode to intercept the crash and then print the value of source in the console.

I don't know if you are familiar with XCode debugging tools.

Debugging with XCode

In the breakpoint panel, enable All Exceptions to be caught.
Then once the crash occur, in the debug console type po (stand for print object) then the value you want to prospect.

po source

breakpoint-panel

@pribeh
Copy link

pribeh commented Apr 19, 2019

This issue is severe. Can we get this reviewed.

@DylanVann
Copy link
Owner

@StevenMasini This looks good. Turning on some warning checks confirms that you are right about the usage of self inside blocks.

I would merge these changes except I'm unsure about the retry handling code. Whatever we provide for retrying should be consistent for Android and iOS.

If you remove the retry related changes I can merge this and we could handle them in another PR.

@StevenMasini
Copy link
Contributor Author

StevenMasini commented Apr 22, 2019

@DylanVann I will extract the auto-retry logic from this PR and submit a new PR specific to it.

I also need to fix the issue found by @evanjmg.

@StevenMasini
Copy link
Contributor Author

@DylanVann I removed the auto-retry logic from this branch.

If @evanjmg come back to me about his crash, I can fix it in another PR.

Also I am willing to contribute more on the native iOS part if you need me 👍

@evanjmg
Copy link

evanjmg commented Apr 22, 2019 via email

@StevenMasini
Copy link
Contributor Author

@evanjmg I haven't updated @steven/react-native-fast-image as I need the auto-retry logic in my own project.

Where did you put your try/catch ?

@evanjmg
Copy link

evanjmg commented Apr 22, 2019

- (void)imageDidLoadObserver:(NSNotification *)notification {
    FFFastImageSource *source = notification.object;
    if (source != nil) {
        BOOL responds = [self respondsToSelector:@selector(sd_setImageWithURL:)];
        if (responds == YES) {
            @try {
                [self sd_setImageWithURL:source.url];
            } @catch (NSException * e) {
                NSLog(@"Exception: %@", e);
            }
        }
    }
}

Copy link
Owner

@DylanVann DylanVann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good!

I may end up also removing the notification center changes before publishing though. I'm trying to keep things between Android and iOS in sync, and trying to primarily leverage the native libraries SDWebImage and Glide. So I think a more correct place to add this functionality might be in SDWebImage

Alternatively there's this issue if you know of another iOS library that supports this:

Consider switching to a different iOS image loading library.

@DylanVann DylanVann merged commit 70be744 into DylanVann:master Apr 23, 2019
DylanVann pushed a commit that referenced this pull request Apr 23, 2019
# [5.3.0](v5.2.1...v5.3.0) (2019-04-23)

### Bug Fixes

* Fix memory leak on iOS. ([#433](#433)) ([70be744](70be744))

### Features

* Upgrade example apps. ([#453](#453)) ([25f8f0d](25f8f0d))

### Reverts

* Remove functionality for notifying other images on load. ([#452](#452)) ([292223d](292223d))
@StevenMasini
Copy link
Contributor Author

@DylanVann Yeah I was thinking about it yesterday while removing the auto-retry logic. I will submit a PR directly to SDWebImage for this feature.

But for the notification center, I would still advise you to keep this change as it's mostly RN specific issue. I can't suggest something like this directly to SDWebImage.

The problem with RN is that the component will re-render only if the props have changed. So if a similar image has finish loading in another instance of FastImageView, then previously loaded FastImageView won't be refreshed.

I checked Android don't have this problem for some reason.

@StevenMasini StevenMasini deleted the issue/432 branch April 25, 2019 03:29
luffy1701 pushed a commit to luffy1701/react-native-fast-image-v2 that referenced this pull request May 9, 2019
It's recommended to never retain the strong reference to self into blocks.

Blocks maintain strong references to any captured objects, including self, which means that it’s easy to end up with a strong reference cycle.

[skip release]
luffy1701 pushed a commit to luffy1701/react-native-fast-image-v2 that referenced this pull request May 9, 2019
# [5.3.0](DylanVann/react-native-fast-image@v5.2.1...v5.3.0) (2019-04-23)

### Bug Fixes

* Fix memory leak on iOS. ([DylanVann#433](DylanVann#433)) ([70be744](DylanVann@70be744))

### Features

* Upgrade example apps. ([DylanVann#453](DylanVann#453)) ([25f8f0d](DylanVann@25f8f0d))

### Reverts

* Remove functionality for notifying other images on load. ([DylanVann#452](DylanVann#452)) ([292223d](DylanVann@292223d))
github-actions bot pushed a commit to perrystreetsoftware/react-native-fast-image that referenced this pull request Aug 3, 2021
# [6.0.0](v5.0.11...v6.0.0) (2021-08-03)

### Bug Fixes

* add react@17 as peer dependency ([DylanVann#790](https://github.com/perrystreetsoftware/react-native-fast-image/issues/790)) ([27bd586](27bd586))
* xcode 12 compatibility ([DylanVann#732](https://github.com/perrystreetsoftware/react-native-fast-image/issues/732)) ([23c3955](23c3955))
* **android:** make center ResizeMode work correctly ([d648ef8](d648ef8))
* **android:** remove explicit use of UI thread ([DylanVann#698](https://github.com/perrystreetsoftware/react-native-fast-image/issues/698)) ([5d2894e](5d2894e))
* accessibilityIgnoresInvertColors prop not recognised when using TypeScript ([DylanVann#666](https://github.com/perrystreetsoftware/react-native-fast-image/issues/666)) ([22f89e4](22f89e4)), closes [/github.com/DylanVann/react-native-fast-image/blob/master/src/index.tsx#L150-L160](https://github.com//github.com/DylanVann/react-native-fast-image/blob/master/src/index.tsx/issues/L150-L160)
* Add git tag to CocoaPods source property ([DylanVann#601](https://github.com/perrystreetsoftware/react-native-fast-image/issues/601)) ([2d706ad](2d706ad))
* Add tintColor type definition. ([4adf42f](4adf42f))
* Bump Glide version number to v4.11.0. ([DylanVann#649](https://github.com/perrystreetsoftware/react-native-fast-image/issues/649)) ([c4e4306](c4e4306)), closes [DylanVann#536](https://github.com/perrystreetsoftware/react-native-fast-image/issues/536)
* Fix dependency versions not specified in podfile. ([89f3379](89f3379)), closes [DylanVann#456](https://github.com/perrystreetsoftware/react-native-fast-image/issues/456)
* Fix fallback prop not working. ([DylanVann#420](https://github.com/perrystreetsoftware/react-native-fast-image/issues/420)) ([487d410](487d410))
* Fix IllegalArgumentException crash (Android). ([DylanVann#511](https://github.com/perrystreetsoftware/react-native-fast-image/issues/511)) ([b6c4677](b6c4677))
* Fix incorrect syntax. ([11f6047](11f6047))
* Fix local resource cache issue on Android. ([DylanVann#472](https://github.com/perrystreetsoftware/react-native-fast-image/issues/472)) ([5f65383](5f65383)), closes [DylanVann#402](https://github.com/perrystreetsoftware/react-native-fast-image/issues/402)
* Fix memory leak on iOS. ([DylanVann#433](https://github.com/perrystreetsoftware/react-native-fast-image/issues/433)) ([70be744](70be744))
* Fix peer dependency and remove prop-types. ([44a4c8b](44a4c8b))
* Fix setting props order issue for iOS. ([DylanVann#303](https://github.com/perrystreetsoftware/react-native-fast-image/issues/303)) ([5597ed0](5597ed0)), closes [DylanVann#304](https://github.com/perrystreetsoftware/react-native-fast-image/issues/304)
* Fix wildcard peer dependencies. ([7149420](7149420)), closes [DylanVann#440](https://github.com/perrystreetsoftware/react-native-fast-image/issues/440)
* Fixes cacheControl types. ([DylanVann#382](https://github.com/perrystreetsoftware/react-native-fast-image/issues/382)) ([e13db7d](e13db7d)), closes [DylanVann#325](https://github.com/perrystreetsoftware/react-native-fast-image/issues/325)
* Fixes podspec syntax. ([b627646](b627646))
* Fixes WebP rendering on iOS 12. ([DylanVann#412](https://github.com/perrystreetsoftware/react-native-fast-image/issues/412)) ([97630c8](97630c8)), closes [DylanVann#298](https://github.com/perrystreetsoftware/react-native-fast-image/issues/298) [DylanVann#385](https://github.com/perrystreetsoftware/react-native-fast-image/issues/385)
* Loading images by reverting "bug: Use device scale when loading images.". ([0326c3e](0326c3e)), closes [DylanVann#509](https://github.com/perrystreetsoftware/react-native-fast-image/issues/509)
* peer dependency warning ([DylanVann#653](https://github.com/perrystreetsoftware/react-native-fast-image/issues/653)) ([cd81b1b](cd81b1b))
* remove cache property if using fallback ([ba0f238](ba0f238))
* Replace 'Component' with 'ComponentType' ([DylanVann#647](https://github.com/perrystreetsoftware/react-native-fast-image/issues/647)) ([6abb273](6abb273))
* update SDWebImage and SDWebImageWebPCoder ([DylanVann#689](https://github.com/perrystreetsoftware/react-native-fast-image/issues/689)) ([9646456](9646456))
* Updates SDWebImageWebPCoder. ([DylanVann#628](https://github.com/perrystreetsoftware/react-native-fast-image/issues/628)) ([325d77f](325d77f))
* Upgrade vendored SDWebImage to v5.0.5. ([5016172](5016172)), closes [DylanVann#489](https://github.com/perrystreetsoftware/react-native-fast-image/issues/489)
* wrong cache type ([DylanVann#688](https://github.com/perrystreetsoftware/react-native-fast-image/issues/688)) [skip ci] ([94e2256](94e2256))

### Features

* **ios:** allow for for per-image-request-headers ([DylanVann#691](https://github.com/perrystreetsoftware/react-native-fast-image/issues/691)) ([4a7cd64](4a7cd64))
* Add cookie support for iOS. ([DylanVann#284](https://github.com/perrystreetsoftware/react-native-fast-image/issues/284)) ([ae47bff](ae47bff))
* Add tint color support. ([03c50f0](03c50f0)), closes [DylanVann#124](https://github.com/perrystreetsoftware/react-native-fast-image/issues/124)
* Add tvOS target. ([DylanVann#486](https://github.com/perrystreetsoftware/react-native-fast-image/issues/486)) ([6805972](6805972))
* converts to TypeScript ([DylanVann#642](https://github.com/perrystreetsoftware/react-native-fast-image/issues/642)) ([ac11706](ac11706))
* export ResizeMode and Priority types ([DylanVann#678](https://github.com/perrystreetsoftware/react-native-fast-image/issues/678)) ([e33664f](e33664f))
* Upgrade example apps. ([DylanVann#453](https://github.com/perrystreetsoftware/react-native-fast-image/issues/453)) ([25f8f0d](25f8f0d))
* Upgrade to React Native 0.60.0 / CocoaPods / Android X. ([DylanVann#513](https://github.com/perrystreetsoftware/react-native-fast-image/issues/513)) ([5489f9e](5489f9e))
* Upgrade to SDWebImage 5.0. ([DylanVann#454](https://github.com/perrystreetsoftware/react-native-fast-image/issues/454)) ([8a216e2](8a216e2)), closes [DylanVann#447](https://github.com/perrystreetsoftware/react-native-fast-image/issues/447)
* Use forwardRef to allow access to ref.measure and others. ([DylanVann#419](https://github.com/perrystreetsoftware/react-native-fast-image/issues/419)) ([2b4fba3](2b4fba3)), closes [DylanVann#69](https://github.com/perrystreetsoftware/react-native-fast-image/issues/69)

### Performance Improvements

* Use React.memo for FastImage. ([DylanVann#449](https://github.com/perrystreetsoftware/react-native-fast-image/issues/449)) ([5c2b4af](5c2b4af))

### Reverts

* Remove functionality for notifying other images on load. ([DylanVann#452](https://github.com/perrystreetsoftware/react-native-fast-image/issues/452)) ([292223d](292223d))

### BREAKING CHANGES

* This changes how network requests are handled on iOS. Make sure they still work for you.
* You should upgrade React Native. See https://facebook.github.io/react-native/blog/2019/07/03/version-60
* Upgrade SDWebImage, may affect some projects and CocoaPods users.

Fix the bug of `cacheOnly` behavior
alicayan008 pushed a commit to alicayan008/ReactNative-fast-image that referenced this pull request Jul 4, 2023
# [5.3.0](DylanVann/react-native-fast-image@v5.2.1...v5.3.0) (2019-04-23)

### Bug Fixes

* Fix memory leak on iOS. ([#433](DylanVann/react-native-fast-image#433)) ([70be744](DylanVann/react-native-fast-image@70be744))

### Features

* Upgrade example apps. ([#453](DylanVann/react-native-fast-image#453)) ([25f8f0d](DylanVann/react-native-fast-image@25f8f0d))

### Reverts

* Remove functionality for notifying other images on load. ([#452](DylanVann/react-native-fast-image#452)) ([292223d](DylanVann/react-native-fast-image@292223d))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants