Skip to content
This repository was archived by the owner on Feb 6, 2023. It is now read-only.

添加基于引用计数的缓存方式;修复SVGAVideoEntity文件中weakCache的线程安全问题 #123

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions SVGAPlayer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
90D7CA1C1F7FB114006E74F0 /* rose_1.5.0.svga in Resources */ = {isa = PBXBuildFile; fileRef = 90D7CA1A1F7FB114006E74F0 /* rose_1.5.0.svga */; };
90D7CA1E1F7FB34E006E74F0 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 90D7CA1D1F7FB34E006E74F0 /* libz.tbd */; };
90DB59B51F96026E00894727 /* SVGAImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 90DB59B41F96026E00894727 /* SVGAImageView.m */; };
FA44BB1A244B1394001B5D75 /* SVGAVideoEntityMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = FA44BB19244B1394001B5D75 /* SVGAVideoEntityMemoryCache.m */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -119,6 +120,8 @@
90DB59B41F96026E00894727 /* SVGAImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SVGAImageView.m; sourceTree = "<group>"; };
92332F7A897BF4379D765B05 /* libPods-SVGAPlayer React.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SVGAPlayer React.a"; sourceTree = BUILT_PRODUCTS_DIR; };
E02B8713B25C0283C736EE03 /* Pods-SVGAPlayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SVGAPlayer.release.xcconfig"; path = "Pods/Target Support Files/Pods-SVGAPlayer/Pods-SVGAPlayer.release.xcconfig"; sourceTree = "<group>"; };
FA44BB18244B1394001B5D75 /* SVGAVideoEntityMemoryCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGAVideoEntityMemoryCache.h; sourceTree = "<group>"; };
FA44BB19244B1394001B5D75 /* SVGAVideoEntityMemoryCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SVGAVideoEntityMemoryCache.m; sourceTree = "<group>"; };
FF89C40C3E9839DA5DE71191 /* Pods-SVGAPlayer React.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SVGAPlayer React.release.xcconfig"; path = "Pods/Target Support Files/Pods-SVGAPlayer React/Pods-SVGAPlayer React.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -233,6 +236,8 @@
9052FC621E6EB8D4007BC925 /* SVGAExporter.m */,
904D41F61D223DD20085A21A /* SVGABezierPath.h */,
904D41F71D223DD20085A21A /* SVGABezierPath.m */,
FA44BB18244B1394001B5D75 /* SVGAVideoEntityMemoryCache.h */,
FA44BB19244B1394001B5D75 /* SVGAVideoEntityMemoryCache.m */,
);
path = Source;
sourceTree = "<group>";
Expand Down Expand Up @@ -443,6 +448,7 @@
904D41F81D223DD20085A21A /* SVGABezierPath.m in Sources */,
90A364D71E5AECBD009347F1 /* SVGAVideoSpriteEntity.m in Sources */,
90A676E51D13A6DF008A69F3 /* AppDelegate.m in Sources */,
FA44BB1A244B1394001B5D75 /* SVGAVideoEntityMemoryCache.m in Sources */,
90A364DD1E5D33F8009347F1 /* SVGAContentLayer.m in Sources */,
90DB59B51F96026E00894727 /* SVGAImageView.m in Sources */,
90A676FA1D13A81F008A69F3 /* SVGA.m in Sources */,
Expand Down
4 changes: 3 additions & 1 deletion Source/SVGAVideoEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
@property (nonatomic, readonly) CGSize videoSize;
@property (nonatomic, readonly) int FPS;
@property (nonatomic, readonly) int frames;
// 所有图片的总像素数(像素数决定了占用内存的大小)
@property (nonatomic, readonly) NSUInteger totalPixelCount;
@property (nonatomic, readonly) NSDictionary<NSString *, UIImage *> *images;
@property (nonatomic, readonly) NSDictionary<NSString *, NSData *> *audiosData;
@property (nonatomic, readonly) NSArray<SVGAVideoSpriteEntity *> *sprites;
Expand All @@ -32,7 +34,7 @@
- (void)resetAudiosWithProtoObject:(SVGAProtoMovieEntity *)protoObject;

+ (SVGAVideoEntity *)readCache:(NSString *)cacheKey;
// NSCache缓存

- (void)saveCache:(NSString *)cacheKey;
// NSMapTable弱缓存
- (void)saveWeakCache:(NSString *)cacheKey;
Expand Down
30 changes: 23 additions & 7 deletions Source/SVGAVideoEntity.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "SVGABezierPath.h"
#import "SVGAVideoSpriteEntity.h"
#import "SVGAAudioEntity.h"
#import "SVGAVideoEntityMemoryCache.h"
#import "Svga.pbobjc.h"

#define MP3_MAGIC_NUMBER "ID3"
Expand All @@ -20,6 +21,7 @@ @interface SVGAVideoEntity ()
@property (nonatomic, assign) CGSize videoSize;
@property (nonatomic, assign) int FPS;
@property (nonatomic, assign) int frames;
@property (nonatomic, readwrite) NSUInteger totalPixelCount;
@property (nonatomic, copy) NSDictionary<NSString *, UIImage *> *images;
@property (nonatomic, copy) NSDictionary<NSString *, NSData *> *audiosData;
@property (nonatomic, copy) NSArray<SVGAVideoSpriteEntity *> *sprites;
Expand All @@ -30,16 +32,16 @@ @interface SVGAVideoEntity ()

@implementation SVGAVideoEntity

static NSCache *videoCache;
static NSMapTable * weakCache;
static dispatch_semaphore_t cacheLock;

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
videoCache = [[NSCache alloc] init];
weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
valueOptions:NSPointerFunctionsWeakMemory
capacity:64];
capacity:128];
cacheLock = dispatch_semaphore_create(1);
});
}

Expand All @@ -50,6 +52,7 @@ - (instancetype)initWithJSONObject:(NSDictionary *)JSONObject cacheDir:(NSString
_FPS = 20;
_images = @{};
_cacheDir = cacheDir;
_totalPixelCount = 0;
[self resetMovieWithJSONObject:JSONObject];
}
return self;
Expand Down Expand Up @@ -80,20 +83,21 @@ - (void)resetMovieWithJSONObject:(NSDictionary *)JSONObject {
}

- (void)resetImagesWithJSONObject:(NSDictionary *)JSONObject {
_totalPixelCount = 0;
if ([JSONObject isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary<NSString *, UIImage *> *images = [[NSMutableDictionary alloc] init];
NSDictionary<NSString *, NSString *> *JSONImages = JSONObject[@"images"];
if ([JSONImages isKindOfClass:[NSDictionary class]]) {
[JSONImages enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSString class]]) {
NSString *filePath = [self.cacheDir stringByAppendingFormat:@"/%@.png", obj];
// NSData *imageData = [NSData dataWithContentsOfFile:filePath];
NSData *imageData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:NULL];
if (imageData != nil) {
UIImage *image = [[UIImage alloc] initWithData:imageData scale:2.0];
if (image != nil) {
[images setObject:image forKey:[key stringByDeletingPathExtension]];
}
[self increasePixelCount:image];
}
}
}];
Expand Down Expand Up @@ -147,6 +151,7 @@ + (BOOL)isMP3Data:(NSData *)data {
}

- (void)resetImagesWithProtoObject:(SVGAProtoMovieEntity *)protoObject {
_totalPixelCount = 0;
NSMutableDictionary<NSString *, UIImage *> *images = [[NSMutableDictionary alloc] init];
NSMutableDictionary<NSString *, NSData *> *audiosData = [[NSMutableDictionary alloc] init];
NSDictionary *protoImages = [protoObject.images copy];
Expand All @@ -158,13 +163,13 @@ - (void)resetImagesWithProtoObject:(SVGAProtoMovieEntity *)protoObject {
filePath = [self.cacheDir stringByAppendingFormat:@"/%@", fileName];
}
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// NSData *imageData = [NSData dataWithContentsOfFile:filePath];
NSData *imageData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:NULL];
if (imageData != nil) {
UIImage *image = [[UIImage alloc] initWithData:imageData scale:2.0];
if (image != nil) {
[images setObject:image forKey:key];
}
[self increasePixelCount:image];
}
}
}
Expand All @@ -177,6 +182,7 @@ - (void)resetImagesWithProtoObject:(SVGAProtoMovieEntity *)protoObject {
if (image != nil) {
[images setObject:image forKey:key];
}
[self increasePixelCount:image];
}
}
}
Expand Down Expand Up @@ -208,20 +214,30 @@ - (void)resetAudiosWithProtoObject:(SVGAProtoMovieEntity *)protoObject {
self.audios = audios;
}

- (void)increasePixelCount:(UIImage *)image {
if (image) {
_totalPixelCount += image.size.width * image.size.height * image.scale * image.scale;
}
}

+ (SVGAVideoEntity *)readCache:(NSString *)cacheKey {
SVGAVideoEntity * object = [videoCache objectForKey:cacheKey];
SVGAVideoEntity * object = [SVGAVideoEntityMemoryCache videoEntityForKey:cacheKey];
if (!object) {
dispatch_semaphore_wait(cacheLock, DISPATCH_TIME_FOREVER);
object = [weakCache objectForKey:cacheKey];
dispatch_semaphore_signal(cacheLock);
}
return object;
}

- (void)saveCache:(NSString *)cacheKey {
[videoCache setObject:self forKey:cacheKey];
[SVGAVideoEntityMemoryCache setVideoEntity:self forKey:cacheKey];
}

- (void)saveWeakCache:(NSString *)cacheKey {
dispatch_semaphore_wait(cacheLock, DISPATCH_TIME_FOREVER);
[weakCache setObject:self forKey:cacheKey];
dispatch_semaphore_signal(cacheLock)
}

@end
Expand Down
33 changes: 33 additions & 0 deletions Source/SVGAVideoEntityMemoryCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// SVGAVideoEntityMemoryCache.h
// SVGAPlayer
//
// Created by song.meng on 2020/4/18.
// Copyright © 2020 UED Center. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "SVGAVideoEntity.h"
NS_ASSUME_NONNULL_BEGIN

@interface SVGAVideoEntityMemoryCache : NSObject

// 内存缓存最大像素数限制,默认为:50 * 1024 * 1024 / 4,大约在内存中占用50M
@property (nonatomic, assign) NSUInteger maxPixelLimit;
//自动清理周期,默认0,即不清理,最小限制10s
@property (nonatomic, assign) NSUInteger autoClearInterval;
// 是否使用强引用缓存,默认YES
@property (nonatomic, assign) BOOL useStrongCache;

+ (instancetype)defaultCache;

+ (void)clearCache;

+ (void)setVideoEntity:(SVGAVideoEntity *)object forKey:(id)key;
+ (void)removeVideoEntityWithKey:(id)key;
+ (SVGAVideoEntity *)videoEntityForKey:(id)key;


@end

NS_ASSUME_NONNULL_END
154 changes: 154 additions & 0 deletions Source/SVGAVideoEntityMemoryCache.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//
// SVGAVideoEntityMemoryCache.m
// SVGAPlayer
//
// Created by song.meng on 2020/4/18.
// Copyright © 2020 UED Center. All rights reserved.
//

#import "SVGAVideoEntityMemoryCache.h"


#define MemoryLock(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define MemoryUnLock(lock) dispatch_semaphore_signal(lock);


@interface SVGAVideoEntityMemoryCache()

@property (nonatomic, strong) NSCache *strongCache;
@property (nonatomic, strong) NSMapTable *weakCache;
@property (nonatomic, strong) dispatch_semaphore_t lock;
@property (nonatomic, assign) BOOL startedAutoClear;

@end

@implementation SVGAVideoEntityMemoryCache

static SVGAVideoEntityMemoryCache * instance = nil;
+ (instancetype)defaultCache
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}

+ (void)clearCache
{
[[self defaultCache] clearCache];
}

+ (void)setVideoEntity:(SVGAVideoEntity *)object forKey:(id)key
{
[[self defaultCache] setVideoEntity:object forKey:key];
}

+ (void)removeVideoEntityWithKey:(id)key
{
[[self defaultCache] removeVideoEntityWithKey:key];
}

+ (id)videoEntityForKey:(id)key
{
return [[self defaultCache] videoEntityForKey:key];
}

#pragma mark - instance

- (instancetype)init
{
if (self = [super init]) {
_strongCache = [[NSCache alloc] init];
_weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
valueOptions:NSPointerFunctionsWeakMemory
capacity:128];
_lock = dispatch_semaphore_create(1);
_useStrongCache = YES;
_maxPixelLimit = 50 * 1024 * 1024 / 4;
_autoClearInterval = 0;
}
return self;
}

- (void)clearCache
{
[_strongCache removeAllObjects];
}

- (void)setVideoEntity:(SVGAVideoEntity *)object forKey:(id)key
{
if (!key) {
return;
}

if ([object isKindOfClass:[SVGAVideoEntity class]]) {
if (_useStrongCache && (_maxPixelLimit == 0 || object.totalPixelCount < _maxPixelLimit)) {
[_strongCache setObject:object forKey:key];
}

MemoryLock(_lock);
[_weakCache setObject:object forKey:key];
MemoryUnLock(_lock);
} else {
[self removeVideoEntityWithKey:key];
}
}

- (void)removeVideoEntityWithKey:(id)key
{
if (key) {
[self.strongCache removeObjectForKey:key];
}
}

- (SVGAVideoEntity *)videoEntityForKey:(id)key
{
if (!key) {
return nil;
}

SVGAVideoEntity * object = [_strongCache objectForKey:key];
if (!object) {
MemoryLock(_lock);
object = [_weakCache objectForKey:key];
MemoryUnLock(_lock);
if (object && _useStrongCache && (_maxPixelLimit == 0 || object.totalPixelCount < _maxPixelLimit)) {
[_strongCache setObject:object forKey:key];
}
}
return object;
}

#pragma mark - auto clear
- (void)setAutoClearInterval:(NSUInteger)autoClearInterval
{
if (autoClearInterval < 10) {
autoClearInterval = 10;
}

_autoClearInterval = autoClearInterval;
[self clearCache];

if (!_startedAutoClear) {
[self _autoClear];
}
}

- (void)_autoClear
{
_startedAutoClear = YES;
__weak typeof(self) weak_self = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoClearInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
__strong typeof(weak_self) s = weak_self;
if (!s) {
return;
}

[s clearCache];
[s _autoClear];
});

}

@end