diff --git a/Core/Core.xcodeproj/project.pbxproj b/Core/Core.xcodeproj/project.pbxproj index 10a8b982..5197059a 100644 --- a/Core/Core.xcodeproj/project.pbxproj +++ b/Core/Core.xcodeproj/project.pbxproj @@ -18,7 +18,7 @@ CB95641623AB2E9100ACCD39 /* MMKV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F223AB2E9100ACCD39 /* MMKV.cpp */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; CB95641723AB2E9100ACCD39 /* MiniPBCoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F523AB2E9100ACCD39 /* MiniPBCoder.cpp */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; CB95641823AB2E9100ACCD39 /* ThreadLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F723AB2E9100ACCD39 /* ThreadLock.cpp */; }; - CB95641923AB2E9100ACCD39 /* MMBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F823AB2E9100ACCD39 /* MMBuffer.cpp */; }; + CB95641923AB2E9100ACCD39 /* MMBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F823AB2E9100ACCD39 /* MMBuffer.cpp */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; CB95641A23AB2E9100ACCD39 /* CodedInputData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB9563F923AB2E9100ACCD39 /* CodedInputData.cpp */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; CB95641B23AB2E9100ACCD39 /* openssl_cfb128.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640123AB2E9100ACCD39 /* openssl_cfb128.cpp */; }; CB95641C23AB2E9100ACCD39 /* openssl_aes_core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB95640323AB2E9100ACCD39 /* openssl_aes_core.cpp */; }; diff --git a/Core/MMBuffer.cpp b/Core/MMBuffer.cpp index 47c65106..ca8523d1 100644 --- a/Core/MMBuffer.cpp +++ b/Core/MMBuffer.cpp @@ -23,6 +23,12 @@ #include #include +#ifdef MMKV_IOS_OR_MAC +# if __has_feature(objc_arc) +# error This file must be compiled with MRC. Use -fno-objc-arc flag. +# endif +#endif + namespace mmkv { MMBuffer::MMBuffer(size_t length) : ptr(nullptr), size(length), isNoCopy(MMBufferCopy) { @@ -38,10 +44,26 @@ MMBuffer::MMBuffer(void *source, size_t length, MMBufferCopyFlag noCopy) : ptr(s } } +#ifdef MMKV_IOS_OR_MAC +MMBuffer::MMBuffer(NSData *data, MMBufferCopyFlag noCopy) + : ptr((void *) data.bytes), size(data.length), isNoCopy(noCopy) { + if (isNoCopy == MMBufferCopy) { + m_data = [data retain]; + } else { + m_data = data; + } +} +#endif + MMBuffer::MMBuffer(MMBuffer &&other) noexcept : ptr(other.ptr), size(other.size), isNoCopy(other.isNoCopy) { other.ptr = nullptr; other.size = 0; other.isNoCopy = MMBufferCopy; + +#ifdef MMKV_IOS_OR_MAC + m_data = other.m_data; + other.m_data = nil; +#endif } MMBuffer &MMBuffer::operator=(MMBuffer &&other) noexcept { @@ -49,10 +71,25 @@ MMBuffer &MMBuffer::operator=(MMBuffer &&other) noexcept { std::swap(size, other.size); std::swap(isNoCopy, other.isNoCopy); +#ifdef MMKV_IOS_OR_MAC + std::swap(m_data, other.m_data); +#endif + return *this; } MMBuffer::~MMBuffer() { +#ifdef MMKV_IOS_OR_MAC + if (m_data) { + if (isNoCopy == MMBufferCopy) { + [m_data release]; + } + m_data = nil; + ptr = nullptr; + return; + } +#endif + if (isNoCopy == MMBufferCopy && ptr) { free(ptr); } diff --git a/Core/MMBuffer.h b/Core/MMBuffer.h index 2494f672..8d95a1c6 100644 --- a/Core/MMBuffer.h +++ b/Core/MMBuffer.h @@ -21,6 +21,8 @@ #ifndef MMKV_MMBUFFER_H #define MMKV_MMBUFFER_H +#include "MMKVPredef.h" + #include #include @@ -36,6 +38,9 @@ private: void *ptr; size_t size; MMBufferCopyFlag isNoCopy; +#ifdef MMKV_IOS_OR_MAC + NSData *m_data = nil; +#endif public: void *getPtr() const { return ptr; } @@ -44,6 +49,9 @@ public: explicit MMBuffer(size_t length = 0); MMBuffer(void *source, size_t length, MMBufferCopyFlag noCopy = MMBufferCopy); +#ifdef MMKV_IOS_OR_MAC + explicit MMBuffer(NSData *data, MMBufferCopyFlag noCopy = MMBufferCopy); +#endif MMBuffer(MMBuffer &&other) noexcept; MMBuffer &operator=(MMBuffer &&other) noexcept; diff --git a/Core/MMKV.cpp b/Core/MMKV.cpp index 332a8817..c298608c 100644 --- a/Core/MMKV.cpp +++ b/Core/MMKV.cpp @@ -697,7 +697,7 @@ const MMBuffer &MMKV::getDataForKey(MMKVKey_t key) { if (itr != m_dic.end()) { return itr->second; } - static MMBuffer nan(0); + static MMBuffer nan((size_t) 0); return nan; } @@ -733,7 +733,7 @@ bool MMKV::removeDataForKey(MMKVKey_t key) { m_dic.erase(itr); m_hasFullWriteback = false; - static MMBuffer nan(0); + static MMBuffer nan((size_t) 0); return appendDataWithKey(nan, key); } diff --git a/Core/MMKV_OSX.cpp b/Core/MMKV_OSX.cpp index 9857a5ef..50a2bb29 100644 --- a/Core/MMKV_OSX.cpp +++ b/Core/MMKV_OSX.cpp @@ -70,9 +70,9 @@ void MMKV::setIsInBackground(bool isInBackground) { // @finally in C++ stype template -struct FinalAction { - FinalAction(F f) : m_func{f} {} - ~FinalAction() { m_func(); } +struct AtExit { + AtExit(F f) : m_func{f} {} + ~AtExit() { m_func(); } private: F m_func; @@ -90,7 +90,7 @@ bool MMKV::protectFromBackgroundWriting(size_t size, WriteBlock block) { //block(m_output); return false; } else { - FinalAction cleanup([=] { munlock((void *) ptr, lockDownSize); }); + AtExit cleanup([=] { munlock((void *) ptr, lockDownSize); }); try { block(m_output); } catch (std::exception &exception) { @@ -121,7 +121,9 @@ bool MMKV::set(NSObject *__unsafe_unretained obj, MMKVKey_t key) { } else { /*if ([object conformsToProtocol:@protocol(NSCoding)])*/ { auto tmp = [NSKeyedArchiver archivedDataWithRootObject:obj]; - data = MMBuffer((void *) tmp.bytes, tmp.length); + if (tmp.length > 0) { + data = MMBuffer(tmp); + } } } diff --git a/Core/MiniPBCoder.cpp b/Core/MiniPBCoder.cpp index c1f4cd3e..f6586b05 100644 --- a/Core/MiniPBCoder.cpp +++ b/Core/MiniPBCoder.cpp @@ -83,7 +83,7 @@ void MiniPBCoder::writeRootObject() { m_outputData->writeRawVarint32(encodeItem->valueSize); if (encodeItem->valueSize > 0 && encodeItem->value.tmpObjectValue != nullptr) { auto obj = (__bridge NSData *) encodeItem->value.tmpObjectValue; - MMBuffer buffer((void *) obj.bytes, obj.length, MMBufferNoCopy); + MMBuffer buffer(obj, MMBufferNoCopy); m_outputData->writeRawData(buffer); } break; @@ -92,7 +92,7 @@ void MiniPBCoder::writeRootObject() { m_outputData->writeRawVarint32(encodeItem->valueSize); if (encodeItem->valueSize > 0 && encodeItem->value.objectValue != nullptr) { auto obj = (__bridge NSData *) encodeItem->value.objectValue; - MMBuffer buffer((void *) obj.bytes, obj.length, MMBufferNoCopy); + MMBuffer buffer(obj, MMBufferNoCopy); m_outputData->writeRawData(buffer); } break; diff --git a/Core/MiniPBCoder.h b/Core/MiniPBCoder.h index 76dc8331..b53e74cb 100644 --- a/Core/MiniPBCoder.h +++ b/Core/MiniPBCoder.h @@ -83,7 +83,7 @@ public: return pbcoder.getEncodeData(obj); } catch (const std::exception &exception) { MMKVError("%s", exception.what()); - return MMBuffer(0); + return MMBuffer((size_t) 0); } } diff --git a/iOS/MMKV/MMKV/libMMKV.mm b/iOS/MMKV/MMKV/libMMKV.mm index 614e7e39..a3593953 100644 --- a/iOS/MMKV/MMKV/libMMKV.mm +++ b/iOS/MMKV/MMKV/libMMKV.mm @@ -511,7 +511,7 @@ static BOOL g_hasCalledInitializeMMKV = NO; } else { string = mmapID; } - MMKVInfo("mmapKey: %@", string); + MMKVDebug("mmapKey: %@", string); return string; } diff --git a/iOS/MMKVDemo/MMKVDemo/ViewController.mm b/iOS/MMKVDemo/MMKVDemo/ViewController.mm index e30a0b43..4cebc117 100644 --- a/iOS/MMKVDemo/MMKVDemo/ViewController.mm +++ b/iOS/MMKVDemo/MMKVDemo/ViewController.mm @@ -22,6 +22,35 @@ #import "MMKVDemo-Swift.h" #import +@interface TestNSArchive : NSObject +@property(nonatomic, strong) NSString *m_username; +@property(nonatomic, assign) int32_t m_age; +@property(nonatomic, assign) float m_score; +@end + +@implementation TestNSArchive + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super init]; + if (!self) { + return nil; + } + + self.m_username = [decoder decodeObjectForKey:@"m_username"]; + self.m_age = [decoder decodeInt32ForKey:@"m_age"]; + self.m_score = [decoder decodeFloatForKey:@"m_score"]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder { + [encoder encodeObject:self.m_username forKey:@"m_username"]; + [encoder encodeInteger:self.m_age forKey:@"m_age"]; + [encoder encodeFloat:self.m_score forKey:@"m_score"]; +} + +@end + @interface ViewController () @end @@ -29,6 +58,8 @@ NSMutableArray *m_arrStrings; NSMutableArray *m_arrStrKeys; NSMutableArray *m_arrIntKeys; + NSMutableArray *m_arrObjKeys; + NSMutableArray *m_arrNSCodingObjs; int m_loops; } @@ -69,6 +100,8 @@ m_arrStrings = [NSMutableArray arrayWithCapacity:m_loops]; m_arrStrKeys = [NSMutableArray arrayWithCapacity:m_loops]; m_arrIntKeys = [NSMutableArray arrayWithCapacity:m_loops]; + m_arrObjKeys = [NSMutableArray arrayWithCapacity:m_loops]; + m_arrNSCodingObjs = [NSMutableArray arrayWithCapacity:m_loops]; for (size_t index = 0; index < m_loops; index++) { NSString *str = [NSString stringWithFormat:@"%s-%d", __FILE__, rand()]; [m_arrStrings addObject:str]; @@ -78,6 +111,15 @@ NSString *intKey = [NSString stringWithFormat:@"int-%zu", index]; [m_arrIntKeys addObject:intKey]; + /* + NSString *objKey = [NSString stringWithFormat:@"obj-%zu", index]; + [m_arrObjKeys addObject:objKey]; + + TestNSArchive *obj = [[TestNSArchive alloc] init]; + obj.m_username = str; + obj.m_age = rand(); + obj.m_age = rand() * rand() * 0.5; + [m_arrNSCodingObjs addObject:obj];*/ } } @@ -347,6 +389,9 @@ for (size_t index = 0; index < self->m_loops; index++) { NSString *str = [NSString stringWithFormat:@"%s-%d", __FILE__, rand()]; [self->m_arrStrings addObject:str]; + + //TestNSArchive *obj = self->m_arrNSCodingObjs[index]; + //obj.m_username = str; } [self mmkvBaselineTest:self->m_loops]; @@ -365,6 +410,8 @@ [self mmkvBatchReadInt:loops]; [self mmkvBatchWriteString:loops]; [self mmkvBatchReadString:loops]; + //[self mmkvBatchWriteObject:loops]; + //[self mmkvBatchReadObject:loops]; //[self mmkvBatchDeleteString:loops]; //[[MMKV defaultMMKV] trim]; @@ -447,6 +494,37 @@ } } +- (void)mmkvBatchWriteObject:(int)loops { + @autoreleasepool { + NSDate *startDate = [NSDate date]; + + MMKV *mmkv = [MMKV defaultMMKV]; + for (int index = 0; index < loops; index++) { + TestNSArchive *obj = m_arrNSCodingObjs[index]; + NSString *objKey = m_arrObjKeys[index]; + [mmkv setObject:obj forKey:objKey]; + } + NSDate *endDate = [NSDate date]; + int cost = [endDate timeIntervalSinceDate:startDate] * 1000; + NSLog(@"mmkv write object %d times, cost:%d ms", loops, cost); + } +} + +- (void)mmkvBatchReadObject:(int)loops { + @autoreleasepool { + NSDate *startDate = [NSDate date]; + + MMKV *mmkv = [MMKV defaultMMKV]; + for (int index = 0; index < loops; index++) { + NSString *objKey = m_arrObjKeys[index]; + [mmkv getObjectOfClass:TestNSArchive.class forKey:objKey]; + } + NSDate *endDate = [NSDate date]; + int cost = [endDate timeIntervalSinceDate:startDate] * 1000; + NSLog(@"mmkv read object %d times, cost:%d ms", loops, cost); + } +} + #pragma mark - NSUserDefault baseline test - (void)userDefaultBaselineTest:(int)loops { @@ -454,6 +532,8 @@ [self userDefaultBatchReadInt:loops]; [self userDefaultBatchWriteString:loops]; [self userDefaultBatchReadString:loops]; + //[self userDefaultBatchWriteObject:loops]; + //[self userDefaultBatchReadObject:loops]; } - (void)userDefaultBatchWriteInt:(int)loops { @@ -465,8 +545,8 @@ NSInteger tmp = rand(); NSString *intKey = m_arrIntKeys[index]; [userdefault setInteger:tmp forKey:intKey]; - [userdefault synchronize]; } + [userdefault synchronize]; NSDate *endDate = [NSDate date]; int cost = [endDate timeIntervalSinceDate:startDate] * 1000; NSLog(@"NSUserDefaults write int %d times, cost:%d ms", loops, cost); @@ -497,8 +577,8 @@ NSString *str = m_arrStrings[index]; NSString *strKey = m_arrStrKeys[index]; [userdefault setObject:str forKey:strKey]; - [userdefault synchronize]; } + [userdefault synchronize]; NSDate *endDate = [NSDate date]; int cost = [endDate timeIntervalSinceDate:startDate] * 1000; NSLog(@"NSUserDefaults write string %d times, cost:%d ms", loops, cost); @@ -520,6 +600,40 @@ } } +- (void)userDefaultBatchWriteObject:(int)loops { + @autoreleasepool { + NSDate *startDate = [NSDate date]; + + NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults]; + for (int index = 0; index < loops; index++) { + TestNSArchive *obj = m_arrNSCodingObjs[index]; + NSString *objKey = m_arrObjKeys[index]; + auto tmp = [NSKeyedArchiver archivedDataWithRootObject:obj]; + [userdefault setObject:tmp forKey:objKey]; + } + [userdefault synchronize]; + NSDate *endDate = [NSDate date]; + int cost = [endDate timeIntervalSinceDate:startDate] * 1000; + NSLog(@"NSUserDefaults write object %d times, cost:%d ms", loops, cost); + } +} + +- (void)userDefaultBatchReadObject:(int)loops { + @autoreleasepool { + NSDate *startDate = [NSDate date]; + + NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults]; + for (int index = 0; index < loops; index++) { + NSString *objKey = m_arrObjKeys[index]; + NSData *tmp = [userdefault objectForKey:objKey]; + [NSKeyedUnarchiver unarchiveObjectWithData:tmp]; + } + NSDate *endDate = [NSDate date]; + int cost = [endDate timeIntervalSinceDate:startDate] * 1000; + NSLog(@"NSUserDefaults read object %d times, cost:%d ms", loops, cost); + } +} + #pragma mark - brutle test - (void)brutleTest {