├── README.md
├── mmkv.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── mmkv.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Podfile
├── mmkv
├── protobuf
│ ├── ALMMKV.proto
│ ├── Almmkv.pbobjc.h
│ └── Almmkv.pbobjc.m
├── Info.plist
├── rwlock.hpp
├── almmkv.h
├── rwlock.cpp
└── almmkv.mm
├── Podfile.lock
├── mmkvTests
├── Info.plist
└── mmkvTests.m
├── almmkv.podspec
├── .gitignore
└── LICENSE
/README.md:
--------------------------------------------------------------------------------
1 | # mmkv
2 | High performance KV cache component for iOS & OS X developers, Inspired by Tencent's blog post:https://cloud.tencent.com/developer/article/1066229
3 |
--------------------------------------------------------------------------------
/mmkv.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/mmkv.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/mmkv.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/mmkv.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 |
3 | target 'mmkv' do
4 | use_frameworks!
5 | project 'mmkv'
6 | platform :ios, '8.0'
7 |
8 | pod 'Protobuf', :git => 'https://github.com/google/protobuf.git'
9 | end
10 |
11 | target 'mmkvTests' do
12 | use_frameworks!
13 | project 'mmkv'
14 | platform :ios, '8.0'
15 |
16 | pod 'Protobuf', :git => 'https://github.com/google/protobuf.git'
17 | end
18 |
--------------------------------------------------------------------------------
/mmkv/protobuf/ALMMKV.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3" ;
2 |
3 | message ALKVPair {
4 | string name = 1;
5 | string objcType = 2;
6 | oneof value {
7 | bool boolVal = 3;
8 | sint32 sint32Val = 4;
9 | string strVal = 5;
10 | float floatVal = 6;
11 | bytes binaryVal = 7;
12 | double doubleVal = 8;
13 | sint64 sint64Val = 9;
14 | }
15 | }
16 |
17 | message ALKVList {
18 | repeated ALKVPair item = 1;
19 | }
20 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Protobuf (3.6.0)
3 |
4 | DEPENDENCIES:
5 | - Protobuf (from `https://github.com/google/protobuf.git`)
6 |
7 | EXTERNAL SOURCES:
8 | Protobuf:
9 | :git: https://github.com/google/protobuf.git
10 |
11 | CHECKOUT OPTIONS:
12 | Protobuf:
13 | :commit: 237938ac6ae07d732e1147073ab3569379c313a5
14 | :git: https://github.com/google/protobuf.git
15 |
16 | SPEC CHECKSUMS:
17 | Protobuf: 0fc0ad8bec688b2a3017a139953e01374fedbd5f
18 |
19 | PODFILE CHECKSUM: 5862e7cce1cf3d16112043819068f4622ebe63ab
20 |
21 | COCOAPODS: 1.5.3
22 |
--------------------------------------------------------------------------------
/mmkvTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/mmkv/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/mmkv/rwlock.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // rwlock.hpp
3 | // alloy
4 | //
5 | // Created by Alex Lee on 24/07/2017.
6 | // Copyright © 2017 Alex Lee. All rights reserved.
7 | //
8 |
9 | #ifndef rwlock_hpp
10 | #define rwlock_hpp
11 |
12 | #include
13 | #include
14 | #include
15 |
16 | namespace almmkv {
17 |
18 | //@link:
19 | //https://stackoverflow.com/questions/27860685/how-to-make-a-multiple-read-single-write-lock-from-more-basic-synchronization-pr
20 | class RWLock {
21 | public:
22 | RWLock();
23 | // virtual ~RWLock();
24 |
25 | void lock_read();
26 | void unlock_read();
27 | bool reading() const;
28 |
29 | void lock_write();
30 | void unlock_write();
31 | bool writing() const;
32 |
33 | private:
34 | mutable std::mutex _shared_mutex;
35 | std::condition_variable _rcond;
36 | std::condition_variable _wcond;
37 |
38 | size_t _active_readers;
39 | size_t _waiting_writers;
40 | size_t _active_writers;
41 | };
42 | }
43 |
44 | #endif /* rwlock_hpp */
45 |
--------------------------------------------------------------------------------
/almmkv.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "almmkv"
3 |
4 | # ver = `/usr/libexec/PlistBuddy -c 'print CFBundleShortVersionString' Info.plist`
5 | # abort("No version detected") if ver.nil?
6 | s.version = "1.0"
7 |
8 | s.summary = "A high performance and thread-safe KV cache component for iOS & OS X apps."
9 | s.description = <<-DESC
10 | A high performance and thread-safe KV cache component for iOS & OS X apps.
11 | Inspired by Tencent's blog post: https://cloud.tencent.com/developer/article/1066229 .
12 | DESC
13 |
14 | s.homepage = "https://github.com/alexlee002/mmkv"
15 | s.license = { :type => 'Apache License 2.0', :file => 'LICENSE' }
16 | s.author = { "Alex Lee" => "alexlee002@hotmail.com" }
17 |
18 | s.ios.deployment_target = "8.0"
19 | s.osx.deployment_target = "10.10"
20 |
21 | #s.source = { :git => "https://github.com/alexlee002/mmkv.git", :tag => "#{s.version}" }
22 | s.source = { :git => "https://github.com/alexlee002/mmkv.git", :branch => "master" }
23 |
24 | s.source_files = "mmkv", "mmkv/**/*.{h,m,mm,hpp,cpp}"
25 | s.public_header_files = "mmkv/**/*.h"
26 |
27 | s.dependency "Protobuf" #, "~> 3.6.0",
28 | # Cocoapods only support 3.5, so in your podfile you should specified the protobuf like this:
29 | # pod 'Protobuf', :git => 'https://github.com/google/protobuf.git'
30 |
31 | end
32 |
--------------------------------------------------------------------------------
/mmkv/almmkv.h:
--------------------------------------------------------------------------------
1 | //
2 | // mmkv.h
3 | // mmkv
4 | //
5 | // Created by Alex Lee on 2018/7/11.
6 | // Copyright © 2018 alexlee002. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for mmkv.
12 | FOUNDATION_EXPORT double mmkvVersionNumber;
13 |
14 | //! Project version string for mmkv.
15 | FOUNDATION_EXPORT const unsigned char mmkvVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 | NS_ASSUME_NONNULL_BEGIN
21 | @interface ALMMKV : NSObject
22 |
23 | + (nullable instancetype)defaultMMKV;
24 | + (nullable instancetype)mmkvWithPath:(NSString *)path;
25 |
26 | - (BOOL)boolForKey:(NSString *)key;
27 | - (NSInteger)integerForKey:(NSString *)key;
28 | - (float)floatForKey:(NSString *)key;
29 | - (double)doubleForKey:(NSString *)key;
30 | - (nullable id)objectOfClass:(Class)cls forKey:(NSString *)key;
31 |
32 | - (void)setBool:(BOOL)val forKey:(NSString *)key;
33 | - (void)setInteger:(NSInteger)intval forKey:(NSString *)key;
34 | - (void)setFloat:(float)val forKey:(NSString *)key;
35 | - (void)setDouble:(double)val forKey:(NSString *)key;
36 | - (void)setObject:(nullable id)obj forKey:(NSString *)key;
37 | - (void)removeObjectForKey:(NSString *)key;
38 | - (void)reset;
39 |
40 | #if DEBUG
41 | + (void)dump;
42 | #endif
43 | @end
44 |
45 | NS_ASSUME_NONNULL_END
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | # CocoaPods
32 | #
33 | # We recommend against adding the Pods directory to your .gitignore. However
34 | # you should judge for yourself, the pros and cons are mentioned at:
35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
36 | #
37 | Pods/
38 |
39 | # Carthage
40 | #
41 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
42 | Carthage/Checkouts
43 |
44 | Carthage/Build
45 |
46 | # fastlane
47 | #
48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
49 | # screenshots whenever they are needed.
50 | # For more information about the recommended setup visit:
51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
52 |
53 | fastlane/report.xml
54 | fastlane/Preview.html
55 | fastlane/screenshots/**/*.png
56 | fastlane/test_output
57 |
58 | # Code Injection
59 | #
60 | # After new code Injection tools there's a generated folder /iOSInjectionProject
61 | # https://github.com/johnno1962/injectionforxcode
62 |
63 | iOSInjectionProject/
64 |
--------------------------------------------------------------------------------
/mmkv/rwlock.cpp:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // rwlock.cpp
4 | // alloy
5 | //
6 | // Created by Alex Lee on 24/07/2017.
7 | // Copyright © 2017 Alex Lee. All rights reserved.
8 | //
9 |
10 | #include "rwlock.hpp"
11 |
12 | namespace almmkv {
13 |
14 | RWLock::RWLock() : _shared_mutex(), _rcond(), _wcond(), _active_readers(0), _waiting_writers(0), _active_writers(0) {}
15 | // virtual ~RWLock();
16 |
17 | void RWLock::lock_read() {
18 | std::unique_lock lock(_shared_mutex);
19 | while (_waiting_writers != 0) {
20 | _rcond.wait(lock);
21 | }
22 | ++_active_readers;
23 | lock.unlock();
24 | }
25 |
26 | void RWLock::unlock_read() {
27 | std::unique_lock lock(_shared_mutex);
28 | --_active_readers;
29 | lock.unlock();
30 | _wcond.notify_one();
31 | }
32 |
33 | void RWLock::lock_write() {
34 | std::unique_lock lock(_shared_mutex);
35 | ++_waiting_writers;
36 | while (_active_readers != 0 || _active_writers != 0) {
37 | _wcond.wait(lock);
38 | }
39 | ++_active_writers;
40 | lock.unlock();
41 | }
42 |
43 | void RWLock::unlock_write() {
44 | std::unique_lock lock(_shared_mutex);
45 | --_waiting_writers;
46 | --_active_writers;
47 | if (_waiting_writers > 0) {
48 | _wcond.notify_one();
49 | } else {
50 | _rcond.notify_all();
51 | }
52 | lock.unlock();
53 | }
54 |
55 | bool RWLock::reading() const {
56 | std::unique_lock lock(_shared_mutex);
57 | return _active_readers > 0;
58 | }
59 |
60 | bool RWLock::writing() const {
61 | std::unique_lock lock(_shared_mutex);
62 | return _active_writers > 0;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/mmkv/protobuf/Almmkv.pbobjc.h:
--------------------------------------------------------------------------------
1 | // Generated by the protocol buffer compiler. DO NOT EDIT!
2 | // source: ALMMKV.proto
3 |
4 | // This CPP symbol can be defined to use imports that match up to the framework
5 | // imports needed when using CocoaPods.
6 | #if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
7 | #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
8 | #endif
9 |
10 | #if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
11 | #import
12 | #else
13 | #import "GPBProtocolBuffers.h"
14 | #endif
15 |
16 | #if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
17 | #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
18 | #endif
19 | #if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
20 | #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
21 | #endif
22 |
23 | // @@protoc_insertion_point(imports)
24 |
25 | #pragma clang diagnostic push
26 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
27 |
28 | CF_EXTERN_C_BEGIN
29 |
30 | @class ALKVPair;
31 |
32 | NS_ASSUME_NONNULL_BEGIN
33 |
34 | #pragma mark - AlmmkvRoot
35 |
36 | /**
37 | * Exposes the extension registry for this file.
38 | *
39 | * The base class provides:
40 | * @code
41 | * + (GPBExtensionRegistry *)extensionRegistry;
42 | * @endcode
43 | * which is a @c GPBExtensionRegistry that includes all the extensions defined by
44 | * this file and all files that it depends on.
45 | **/
46 | @interface AlmmkvRoot : GPBRootObject
47 | @end
48 |
49 | #pragma mark - ALKVPair
50 |
51 | typedef GPB_ENUM(ALKVPair_FieldNumber) {
52 | ALKVPair_FieldNumber_Name = 1,
53 | ALKVPair_FieldNumber_ObjcType = 2,
54 | ALKVPair_FieldNumber_BoolVal = 3,
55 | ALKVPair_FieldNumber_Sint32Val = 4,
56 | ALKVPair_FieldNumber_StrVal = 5,
57 | ALKVPair_FieldNumber_FloatVal = 6,
58 | ALKVPair_FieldNumber_BinaryVal = 7,
59 | ALKVPair_FieldNumber_DoubleVal = 8,
60 | ALKVPair_FieldNumber_Sint64Val = 9,
61 | };
62 |
63 | typedef GPB_ENUM(ALKVPair_Value_OneOfCase) {
64 | ALKVPair_Value_OneOfCase_GPBUnsetOneOfCase = 0,
65 | ALKVPair_Value_OneOfCase_BoolVal = 3,
66 | ALKVPair_Value_OneOfCase_Sint32Val = 4,
67 | ALKVPair_Value_OneOfCase_StrVal = 5,
68 | ALKVPair_Value_OneOfCase_FloatVal = 6,
69 | ALKVPair_Value_OneOfCase_BinaryVal = 7,
70 | ALKVPair_Value_OneOfCase_DoubleVal = 8,
71 | ALKVPair_Value_OneOfCase_Sint64Val = 9,
72 | };
73 |
74 | @interface ALKVPair : GPBMessage
75 |
76 | @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
77 |
78 | @property(nonatomic, readwrite, copy, null_resettable) NSString *objcType;
79 |
80 | @property(nonatomic, readonly) ALKVPair_Value_OneOfCase valueOneOfCase;
81 |
82 | @property(nonatomic, readwrite) BOOL boolVal;
83 |
84 | @property(nonatomic, readwrite) int32_t sint32Val;
85 |
86 | @property(nonatomic, readwrite, copy, null_resettable) NSString *strVal;
87 |
88 | @property(nonatomic, readwrite) float floatVal;
89 |
90 | @property(nonatomic, readwrite, copy, null_resettable) NSData *binaryVal;
91 |
92 | @property(nonatomic, readwrite) double doubleVal;
93 |
94 | @property(nonatomic, readwrite) int64_t sint64Val;
95 |
96 | @end
97 |
98 | /**
99 | * Clears whatever value was set for the oneof 'value'.
100 | **/
101 | void ALKVPair_ClearValueOneOfCase(ALKVPair *message);
102 |
103 | #pragma mark - ALKVList
104 |
105 | typedef GPB_ENUM(ALKVList_FieldNumber) {
106 | ALKVList_FieldNumber_ItemArray = 1,
107 | };
108 |
109 | @interface ALKVList : GPBMessage
110 |
111 | @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *itemArray;
112 | /** The number of items in @c itemArray without causing the array to be created. */
113 | @property(nonatomic, readonly) NSUInteger itemArray_Count;
114 |
115 | @end
116 |
117 | NS_ASSUME_NONNULL_END
118 |
119 | CF_EXTERN_C_END
120 |
121 | #pragma clang diagnostic pop
122 |
123 | // @@protoc_insertion_point(global_scope)
124 |
--------------------------------------------------------------------------------
/mmkv/protobuf/Almmkv.pbobjc.m:
--------------------------------------------------------------------------------
1 | // Generated by the protocol buffer compiler. DO NOT EDIT!
2 | // source: ALMMKV.proto
3 |
4 | // This CPP symbol can be defined to use imports that match up to the framework
5 | // imports needed when using CocoaPods.
6 | #if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
7 | #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
8 | #endif
9 |
10 | #if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
11 | #import
12 | #else
13 | #import "GPBProtocolBuffers_RuntimeSupport.h"
14 | #endif
15 |
16 | #import "Almmkv.pbobjc.h"
17 | // @@protoc_insertion_point(imports)
18 |
19 | #pragma clang diagnostic push
20 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
21 | #pragma clang diagnostic ignored "-Wdirect-ivar-access"
22 |
23 | #pragma mark - AlmmkvRoot
24 |
25 | @implementation AlmmkvRoot
26 |
27 | // No extensions in the file and no imports, so no need to generate
28 | // +extensionRegistry.
29 |
30 | @end
31 |
32 | #pragma mark - AlmmkvRoot_FileDescriptor
33 |
34 | static GPBFileDescriptor *AlmmkvRoot_FileDescriptor(void) {
35 | // This is called by +initialize so there is no need to worry
36 | // about thread safety of the singleton.
37 | static GPBFileDescriptor *descriptor = NULL;
38 | if (!descriptor) {
39 | GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
40 | descriptor = [[GPBFileDescriptor alloc] initWithPackage:@""
41 | syntax:GPBFileSyntaxProto3];
42 | }
43 | return descriptor;
44 | }
45 |
46 | #pragma mark - ALKVPair
47 |
48 | @implementation ALKVPair
49 |
50 | @dynamic valueOneOfCase;
51 | @dynamic name;
52 | @dynamic objcType;
53 | @dynamic boolVal;
54 | @dynamic sint32Val;
55 | @dynamic strVal;
56 | @dynamic floatVal;
57 | @dynamic binaryVal;
58 | @dynamic doubleVal;
59 | @dynamic sint64Val;
60 |
61 | typedef struct ALKVPair__storage_ {
62 | uint32_t _has_storage_[2];
63 | int32_t sint32Val;
64 | float floatVal;
65 | NSString *name;
66 | NSString *objcType;
67 | NSString *strVal;
68 | NSData *binaryVal;
69 | double doubleVal;
70 | int64_t sint64Val;
71 | } ALKVPair__storage_;
72 |
73 | // This method is threadsafe because it is initially called
74 | // in +initialize for each subclass.
75 | + (GPBDescriptor *)descriptor {
76 | static GPBDescriptor *descriptor = nil;
77 | if (!descriptor) {
78 | static GPBMessageFieldDescription fields[] = {
79 | {
80 | .name = "name",
81 | .dataTypeSpecific.className = NULL,
82 | .number = ALKVPair_FieldNumber_Name,
83 | .hasIndex = 0,
84 | .offset = (uint32_t)offsetof(ALKVPair__storage_, name),
85 | .flags = GPBFieldOptional,
86 | .dataType = GPBDataTypeString,
87 | },
88 | {
89 | .name = "objcType",
90 | .dataTypeSpecific.className = NULL,
91 | .number = ALKVPair_FieldNumber_ObjcType,
92 | .hasIndex = 1,
93 | .offset = (uint32_t)offsetof(ALKVPair__storage_, objcType),
94 | .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
95 | .dataType = GPBDataTypeString,
96 | },
97 | {
98 | .name = "boolVal",
99 | .dataTypeSpecific.className = NULL,
100 | .number = ALKVPair_FieldNumber_BoolVal,
101 | .hasIndex = -1,
102 | .offset = 2, // Stored in _has_storage_ to save space.
103 | .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
104 | .dataType = GPBDataTypeBool,
105 | },
106 | {
107 | .name = "sint32Val",
108 | .dataTypeSpecific.className = NULL,
109 | .number = ALKVPair_FieldNumber_Sint32Val,
110 | .hasIndex = -1,
111 | .offset = (uint32_t)offsetof(ALKVPair__storage_, sint32Val),
112 | .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
113 | .dataType = GPBDataTypeSInt32,
114 | },
115 | {
116 | .name = "strVal",
117 | .dataTypeSpecific.className = NULL,
118 | .number = ALKVPair_FieldNumber_StrVal,
119 | .hasIndex = -1,
120 | .offset = (uint32_t)offsetof(ALKVPair__storage_, strVal),
121 | .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
122 | .dataType = GPBDataTypeString,
123 | },
124 | {
125 | .name = "floatVal",
126 | .dataTypeSpecific.className = NULL,
127 | .number = ALKVPair_FieldNumber_FloatVal,
128 | .hasIndex = -1,
129 | .offset = (uint32_t)offsetof(ALKVPair__storage_, floatVal),
130 | .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
131 | .dataType = GPBDataTypeFloat,
132 | },
133 | {
134 | .name = "binaryVal",
135 | .dataTypeSpecific.className = NULL,
136 | .number = ALKVPair_FieldNumber_BinaryVal,
137 | .hasIndex = -1,
138 | .offset = (uint32_t)offsetof(ALKVPair__storage_, binaryVal),
139 | .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
140 | .dataType = GPBDataTypeBytes,
141 | },
142 | {
143 | .name = "doubleVal",
144 | .dataTypeSpecific.className = NULL,
145 | .number = ALKVPair_FieldNumber_DoubleVal,
146 | .hasIndex = -1,
147 | .offset = (uint32_t)offsetof(ALKVPair__storage_, doubleVal),
148 | .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
149 | .dataType = GPBDataTypeDouble,
150 | },
151 | {
152 | .name = "sint64Val",
153 | .dataTypeSpecific.className = NULL,
154 | .number = ALKVPair_FieldNumber_Sint64Val,
155 | .hasIndex = -1,
156 | .offset = (uint32_t)offsetof(ALKVPair__storage_, sint64Val),
157 | .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
158 | .dataType = GPBDataTypeSInt64,
159 | },
160 | };
161 | GPBDescriptor *localDescriptor =
162 | [GPBDescriptor allocDescriptorForClass:[ALKVPair class]
163 | rootClass:[AlmmkvRoot class]
164 | file:AlmmkvRoot_FileDescriptor()
165 | fields:fields
166 | fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
167 | storageSize:sizeof(ALKVPair__storage_)
168 | flags:GPBDescriptorInitializationFlag_None];
169 | static const char *oneofs[] = {
170 | "value",
171 | };
172 | [localDescriptor setupOneofs:oneofs
173 | count:(uint32_t)(sizeof(oneofs) / sizeof(char*))
174 | firstHasIndex:-1];
175 | #if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
176 | static const char *extraTextFormatInfo =
177 | "\010\002\010\000\003\007\000\004\t\000\005\006\000\006\010\000\007\t\000\010\t\000\t\t\000";
178 | [localDescriptor setupExtraTextInfo:extraTextFormatInfo];
179 | #endif // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
180 | NSAssert(descriptor == nil, @"Startup recursed!");
181 | descriptor = localDescriptor;
182 | }
183 | return descriptor;
184 | }
185 |
186 | @end
187 |
188 | void ALKVPair_ClearValueOneOfCase(ALKVPair *message) {
189 | GPBDescriptor *descriptor = [message descriptor];
190 | GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0];
191 | GPBMaybeClearOneof(message, oneof, -1, 0);
192 | }
193 | #pragma mark - ALKVList
194 |
195 | @implementation ALKVList
196 |
197 | @dynamic itemArray, itemArray_Count;
198 |
199 | typedef struct ALKVList__storage_ {
200 | uint32_t _has_storage_[1];
201 | NSMutableArray *itemArray;
202 | } ALKVList__storage_;
203 |
204 | // This method is threadsafe because it is initially called
205 | // in +initialize for each subclass.
206 | + (GPBDescriptor *)descriptor {
207 | static GPBDescriptor *descriptor = nil;
208 | if (!descriptor) {
209 | static GPBMessageFieldDescription fields[] = {
210 | {
211 | .name = "itemArray",
212 | .dataTypeSpecific.className = GPBStringifySymbol(ALKVPair),
213 | .number = ALKVList_FieldNumber_ItemArray,
214 | .hasIndex = GPBNoHasBit,
215 | .offset = (uint32_t)offsetof(ALKVList__storage_, itemArray),
216 | .flags = GPBFieldRepeated,
217 | .dataType = GPBDataTypeMessage,
218 | },
219 | };
220 | GPBDescriptor *localDescriptor =
221 | [GPBDescriptor allocDescriptorForClass:[ALKVList class]
222 | rootClass:[AlmmkvRoot class]
223 | file:AlmmkvRoot_FileDescriptor()
224 | fields:fields
225 | fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
226 | storageSize:sizeof(ALKVList__storage_)
227 | flags:GPBDescriptorInitializationFlag_None];
228 | NSAssert(descriptor == nil, @"Startup recursed!");
229 | descriptor = localDescriptor;
230 | }
231 | return descriptor;
232 | }
233 |
234 | @end
235 |
236 |
237 | #pragma clang diagnostic pop
238 |
239 | // @@protoc_insertion_point(global_scope)
240 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/mmkvTests/mmkvTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // mmkvTests.m
3 | // mmkvTests
4 | //
5 | // Created by Alex Lee on 2018/7/11.
6 | // Copyright © 2018 alexlee002. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Almmkv.pbobjc.h"
11 | #import "almmkv.h"
12 |
13 | @interface mmkvTests : XCTestCase
14 |
15 | @end
16 |
17 | @implementation mmkvTests
18 |
19 | - (void)testProtobufMessage {
20 |
21 | ALKVPair *kv1 = [ALKVPair message];
22 | kv1.name = @"setting_1";
23 | kv1.sint32Val = 1024;
24 |
25 | ALKVPair *kv2 = [ALKVPair message];
26 | kv2.name = @"setting_1";
27 | kv2.floatVal = 10.24;
28 |
29 | ALKVPair *kv3 = [ALKVPair message];
30 | kv3.name = @"setting_1";
31 | // kv3.strVal = @"hello";
32 |
33 | ALKVList *store = [ALKVList message];
34 | store.itemArray = @[kv1].mutableCopy;
35 | NSMutableData *data = [NSMutableData dataWithBytes:"\n" length:1];
36 | [data appendData:kv1.delimitedData];
37 | NSLog(@">> %@", data);
38 | NSLog(@"== %@", store.data);
39 |
40 | [store.itemArray addObject:kv2];
41 | [data appendBytes:"\n" length:1];
42 | [data appendData:kv2.delimitedData];
43 | NSLog(@">> %@", data);
44 | NSLog(@"== %@", store.data);
45 |
46 | [store.itemArray addObject:kv3];
47 | [data appendBytes:"\n" length:1];
48 | [data appendData:kv3.delimitedData];
49 | NSLog(@">> %@", data);
50 | NSLog(@"== %@", store.data);
51 |
52 | // store.itemArray = @[kv1].mutableCopy;
53 | // NSLog(@"store: %@", store.data);
54 | // NSLog(@"kv1(d): %@", kv1.data);
55 | // NSLog(@"kv1(dd): %@", kv1.delimitedData);
56 | //
57 | // store.itemArray = @[kv1, kv2].mutableCopy;
58 | // NSLog(@">> %@", store.data);
59 | // NSMutableData *d = kv1.delimitedData.mutableCopy;
60 | // [d appendData:kv2.delimitedData];
61 | // NSLog(@"== %@", d);
62 | // NSLog(@"++ %@", kv2.delimitedData);
63 | //
64 | // store.itemArray = @[kv1, kv2, kv3].mutableCopy;
65 | // NSLog(@">> %@", store.data);
66 | // [d appendData:kv3.data];
67 | // NSLog(@"== %@", d);
68 | // NSLog(@"++ %@", kv3.data);
69 | //
70 |
71 | //-------
72 | // ALKVList *store1 = [ALKVList parseFromData:store.data error:nil];
73 | // ALKVPair *kv = store1.itemArray[0];
74 | // NSLog(@"%@ = %d", kv.name, kv.sint32Val);
75 | // XCTAssertEqual(kv.sint32Val, 1024);
76 | //
77 | // kv = store1.itemArray[1];
78 | // NSLog(@"%@ = %f", kv.name, kv.floatVal);
79 | // XCTAssert(ABS(kv.floatVal - 10.24) < 0.00001);
80 | //
81 | // kv = store1.itemArray[2];
82 | // NSLog(@"%@ = %@", kv.name, kv.strVal);
83 | // XCTAssertEqualObjects(kv.strVal, @"hello");
84 | // NSLog(@"%@ = %f", kv.name, kv.floatVal);
85 |
86 | }
87 |
88 | - (void)testALMMKVWrite {
89 | NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
90 | path = [path stringByAppendingPathComponent:@"test.mmkv"];
91 | ALMMKV *mmkv = [ALMMKV mmkvWithPath:path];
92 |
93 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
94 |
95 |
96 | NSMutableDictionary *dict = [@{} mutableCopy];
97 | for (int i = 0; i < 10000; ++i) {
98 | dict[[NSString stringWithFormat:@"int_%d", i]] = @(i);
99 | }
100 |
101 | CFTimeInterval t = CFAbsoluteTimeGetCurrent();
102 | for (NSString *key in dict.allKeys) {
103 | [mmkv setInteger:[dict[key] intValue] forKey:key];
104 | }
105 | t = (CFAbsoluteTimeGetCurrent() - t) * 1000;
106 | NSLog(@"== mmkv write int; time: %fms", t);
107 |
108 | t = CFAbsoluteTimeGetCurrent();
109 | for (NSString *key in dict.allKeys) {
110 | [defaults setInteger:[dict[key] intValue] forKey:key];
111 | [defaults synchronize];
112 | }
113 | t = (CFAbsoluteTimeGetCurrent() - t) * 1000;
114 | NSLog(@"== userdefaults write int; time: %fms", t);
115 |
116 | ///////////////////////////
117 | [dict removeAllObjects];
118 | for (int i = 0; i < 10000; ++i) {
119 | dict[[NSString stringWithFormat:@"string_%d", i]] = [NSString stringWithFormat:@"string value: %d", i];
120 | }
121 | t = CFAbsoluteTimeGetCurrent();
122 | for (NSString *key in dict.allKeys) {
123 | [mmkv setObject:dict[key] forKey:key];
124 | }
125 | t = (CFAbsoluteTimeGetCurrent() - t) * 1000;
126 | NSLog(@"== mmkv write string; time: %fms", t);
127 |
128 | t = CFAbsoluteTimeGetCurrent();
129 | for (NSString *key in dict.allKeys) {
130 | [defaults setObject:dict[key] forKey:key];
131 | [defaults synchronize];
132 | }
133 | t = (CFAbsoluteTimeGetCurrent() - t) * 1000;
134 | NSLog(@"== userdefaults write string; time: %fms", t);
135 | }
136 |
137 | - (void)testMMKVRead {
138 | NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
139 | path = [path stringByAppendingPathComponent:@"test.mmkv"];
140 | {
141 | ALMMKV *mmkv = [ALMMKV mmkvWithPath:path];
142 | [mmkv reset];
143 |
144 | NSMutableDictionary *dict = [@{} mutableCopy];
145 | for (int i = 0; i < 10000; ++i) {
146 | dict[[NSString stringWithFormat:@"settings_key_string_%d", i]] = @(i);
147 | }
148 | for (NSString *key in dict.allKeys) {
149 | [mmkv setInteger:[dict[key] intValue] forKey:key];
150 | }
151 |
152 | [dict removeAllObjects];
153 | for (int i = 0; i < 10000; ++i) {
154 | dict[[NSString stringWithFormat:@"settings_key_string_%d", i]] = [NSString stringWithFormat:@"string value: %d", i];
155 | }
156 | for (NSString *key in dict.allKeys) {
157 | [mmkv setObject:dict[key] forKey:key];
158 | }
159 | }
160 |
161 | {
162 | ALMMKV *mmkv = [ALMMKV mmkvWithPath:path];
163 | NSMutableDictionary *dict = [@{} mutableCopy];
164 | for (int i = 0; i < 10000; ++i) {
165 | dict[[NSString stringWithFormat:@"settings_key_string_%d", i]] = @(i);
166 | }
167 | CFTimeInterval t = CFAbsoluteTimeGetCurrent();
168 | for (NSString *key in dict.allKeys) {
169 | [mmkv integerForKey:key];
170 | }
171 | t = (CFAbsoluteTimeGetCurrent() - t) * 1000;
172 | NSLog(@"== time: %fms", t);
173 |
174 | [mmkv reset];
175 | [dict removeAllObjects];
176 | for (int i = 0; i < 10000; ++i) {
177 | dict[[NSString stringWithFormat:@"settings_key_string_%d", i]] = [NSString stringWithFormat:@"string value: %d", i];
178 | }
179 | t = CFAbsoluteTimeGetCurrent();
180 | for (NSString *key in dict.allKeys) {
181 | [mmkv objectOfClass:NSString.class forKey:key];
182 | }
183 | t = (CFAbsoluteTimeGetCurrent() - t) * 1000;
184 | NSLog(@"== time: %fms", t);
185 | }
186 | }
187 |
188 | - (void)testUserDefault {
189 | NSUserDefaults *dft = [NSUserDefaults standardUserDefaults];
190 | NSString *intKey = @"int_key";
191 | [dft setInteger:1234 forKey:intKey];
192 | NSLog(@"--- int ---");
193 | NSLog(@"int: %ld", [dft integerForKey:intKey]);
194 | NSLog(@"float: %f", [dft floatForKey:intKey]);
195 | NSLog(@"NSString: %@", [dft stringForKey:intKey]);
196 | NSLog(@"NSData: %@", [dft dataForKey:intKey]);
197 | NSLog(@"NSObject: %@", [dft objectForKey:intKey]);
198 |
199 | NSString *strKey = @"str_key";
200 | [dft setObject:@"1234" forKey:strKey];
201 | NSLog(@"--- str ---");
202 | NSLog(@"int: %ld", [dft integerForKey:strKey]);
203 | NSLog(@"float: %f", [dft floatForKey:strKey]);
204 | NSLog(@"NSString: %@", [dft stringForKey:strKey]);
205 | NSLog(@"NSData: %@", [dft dataForKey:strKey]);
206 | NSLog(@"NSObject: %@", [dft objectForKey:strKey]);
207 |
208 | NSString *dataKey = @"data_key";
209 | [dft setObject:[@"hello" dataUsingEncoding:NSUTF8StringEncoding] forKey:dataKey];
210 | NSLog(@"--- data ---");
211 | NSLog(@"int: %ld", [dft integerForKey:dataKey]);
212 | NSLog(@"float: %f", [dft floatForKey:dataKey]);
213 | NSLog(@"NSString: %@", [dft stringForKey:dataKey]);
214 | NSLog(@"NSData: %@", [dft dataForKey:dataKey]);
215 | NSLog(@"NSObject: %@", [dft objectForKey:dataKey]);
216 |
217 | NSString *dateKey = @"date_key";
218 | [dft setObject:[NSDate date] forKey:dateKey];
219 | NSLog(@"--- data ---");
220 | NSLog(@"int: %ld", [dft integerForKey:dateKey]);
221 | NSLog(@"float: %f", [dft floatForKey:dateKey]);
222 | NSLog(@"NSString: %@", [dft stringForKey:dateKey]);
223 | NSLog(@"NSData: %@", [dft dataForKey:dateKey]);
224 | NSLog(@"NSObject: %@", [dft objectForKey:dateKey]);
225 |
226 | NSString *numKey = @"nsnumber_key";
227 | [dft setObject:@12.34 forKey:numKey];
228 | NSLog(@"--- nsnumber ---");
229 | NSLog(@"int: %ld", [dft integerForKey:numKey]);
230 | NSLog(@"float: %f", [dft floatForKey:numKey]);
231 | NSLog(@"NSString: %@", [dft stringForKey:numKey]);
232 | NSLog(@"NSData: %@", [dft dataForKey:numKey]);
233 | NSLog(@"NSObject: %@", [dft objectForKey:numKey]);
234 |
235 | NSString *numDataKey1 = @"num_data_key1";
236 | [dft setObject:[NSKeyedArchiver archivedDataWithRootObject:@1234] forKey:numDataKey1];
237 | NSLog(@"--- num data1 ---");
238 | NSLog(@"int: %ld", [dft integerForKey:numDataKey1]);
239 | NSLog(@"float: %f", [dft floatForKey:numDataKey1]);
240 | NSLog(@"NSString: %@", [dft stringForKey:numDataKey1]);
241 | NSLog(@"NSData: %@", [dft dataForKey:numDataKey1]);
242 | NSLog(@"NSObject: %@", [dft objectForKey:numDataKey1]);
243 |
244 | // NSString *numDataKey2 = @"num_data_key2";
245 | // int iv = 1234;
246 | // [dft setObject:[NSValue valueWithPointer:&iv] forKey:numDataKey2];
247 | // NSLog(@"--- num data1 ---");
248 | // NSLog(@"int: %ld", [dft integerForKey:numDataKey2]);
249 | // NSLog(@"float: %f", [dft floatForKey:numDataKey2]);
250 | // NSLog(@"NSString: %@", [dft stringForKey:numDataKey2]);
251 | // NSLog(@"NSData: %@", [dft dataForKey:numDataKey2]);
252 | // NSLog(@"NSObject: %@", [dft objectForKey:numDataKey2]);
253 | }
254 |
255 | - (void)testProtobuf {
256 | ALKVPair *kv = [ALKVPair message];
257 | kv.name = @"test_int_key";
258 | kv.sint32Val = 1234;
259 | CFTimeInterval t = CFAbsoluteTimeGetCurrent();
260 | for (int i = 0; i < 10000; ++i) {
261 | [kv delimitedData];
262 | }
263 | NSLog(@"format int: %fms", (CFAbsoluteTimeGetCurrent() - t) * 1000);
264 |
265 | kv.name = @"test_str_key";
266 | // kv.strVal = @"NSUserDefaults *dft = [NSUserDefaults standardUserDefaults];";
267 | t = CFAbsoluteTimeGetCurrent();
268 | for (int i = 0; i < 10000; ++i) {
269 | [kv delimitedData];
270 | }
271 | NSLog(@"format str: %fms", (CFAbsoluteTimeGetCurrent() - t) * 1000);
272 | }
273 |
274 | - (void)testMultiThread {
275 | NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
276 | path = [path stringByAppendingPathComponent:@"test.mmkv"];
277 | void (^mmkv_job)(NSString *) = ^(NSString *name) {
278 | {
279 | ALMMKV *mmkv = [ALMMKV mmkvWithPath:path];
280 | XCTAssertNotNil(mmkv);
281 |
282 | [mmkv setBool:YES forKey:[name stringByAppendingString:@"_bool"]];
283 | NSLog(@"[JOB: %@; %@] write bool", name, mmkv);
284 |
285 | [mmkv setObject:[NSDate date] forKey:[name stringByAppendingString:@"_date"]];
286 | NSLog(@"[JOB: %@; %@] write date", name, mmkv);
287 |
288 | [mmkv setObject:name forKey:[name stringByAppendingString:@"_str"]];
289 | NSLog(@"[JOB: %@; %@] write string", name, mmkv);
290 | }
291 |
292 | {
293 | ALMMKV *mmkv = [ALMMKV mmkvWithPath:path];
294 | XCTAssertNotNil(mmkv);
295 | id obj = [mmkv objectOfClass:NSDate.class forKey:[name stringByAppendingString:@"_date"]];
296 | XCTAssertNotNil(obj);
297 | NSLog(@"[JOB: %@; %@] read date: %@", name, mmkv, obj);
298 |
299 | obj = [mmkv objectOfClass:NSString.class forKey:[name stringByAppendingString:@"_str"]];
300 | XCTAssertNotNil(obj);
301 | NSLog(@"[JOB: %@; %@] read str: %@", name, mmkv, obj);
302 |
303 | obj = [mmkv objectOfClass:NSNumber.class forKey:[name stringByAppendingString:@"_bool"]];
304 | XCTAssertNotNil(obj);
305 | NSLog(@"[JOB: %@; %@] read bool: %@", name, mmkv, obj);
306 | }
307 | };
308 |
309 |
310 | dispatch_group_t group = dispatch_group_create();
311 | dispatch_queue_t queue = dispatch_queue_create("mmkv-test", DISPATCH_QUEUE_CONCURRENT);
312 |
313 | for (int i = 0; i < 20; ++i) {
314 | dispatch_group_async(group, queue, ^{
315 | mmkv_job([NSString stringWithFormat:@"MMKV-Test-%02d", i]);
316 | });
317 | }
318 | dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
319 |
320 | #if DEBUG
321 | [ALMMKV dump];
322 | #endif
323 | NSLog(@"== DONE ==");
324 | }
325 |
326 | - (void)testDefaultMMKV {
327 | [[ALMMKV defaultMMKV] reset];
328 | [[ALMMKV defaultMMKV] setObject:@"hello mmkv" forKey:@"string"];
329 | NSLog(@"read string: %@", [[ALMMKV defaultMMKV] objectOfClass:NSString.class forKey:@"string"]);
330 |
331 | #if DEBUG
332 | [ALMMKV dump];
333 | #endif
334 | }
335 |
336 | - (void)testRemoveKey {
337 | NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
338 | path = [path stringByAppendingPathComponent:@"test.mmkv"];
339 | {
340 | ALMMKV *mmkv = [ALMMKV mmkvWithPath:path];
341 | [mmkv reset];
342 | [mmkv setObject:[NSDate date] forKey:@"date"];
343 |
344 | [mmkv setObject:@"hello mmkv" forKey:@"string"];
345 | NSLog(@"read string: %@", [mmkv objectOfClass:NSString.class forKey:@"string"]);
346 | NSLog(@"read data: %@", [mmkv objectOfClass:NSDate.class forKey:@"date"]);
347 |
348 | NSLog(@"remove key: string");
349 | [mmkv removeObjectForKey:@"string"];
350 | NSLog(@"read string: %@", [mmkv objectOfClass:NSString.class forKey:@"string"]);
351 | NSLog(@"read data: %@", [mmkv objectOfClass:NSDate.class forKey:@"date"]);
352 | }
353 | #if DEBUG
354 | [ALMMKV dump];
355 | #endif
356 | NSLog(@"------------");
357 | NSLog(@"read string: %@", [[ALMMKV mmkvWithPath:path] objectOfClass:NSString.class forKey:@"string"]);
358 | NSLog(@"read data: %@", [[ALMMKV mmkvWithPath:path] objectOfClass:NSDate.class forKey:@"date"]);
359 | }
360 |
361 | @end
362 |
--------------------------------------------------------------------------------
/mmkv/almmkv.mm:
--------------------------------------------------------------------------------
1 | //
2 | // almmkv.m
3 | // mmkv
4 | //
5 | // Created by Alex Lee on 2018/7/12.
6 | // Copyright © 2018 alexlee002. All rights reserved.
7 | //
8 |
9 | #import "almmkv.h"
10 | #import "Almmkv.pbobjc.h"
11 | #import "rwlock.hpp"
12 | #import
13 | #import
14 |
15 | #ifndef ALMMKV_defer
16 | #define ALMMKV_defer \
17 | __strong almmkv_cleanup_block_t almmkv_exit_block_ ## __LINE__ \
18 | __attribute__((cleanup(almmkv_cleanup_block), unused)) = ^
19 |
20 | typedef void (^almmkv_cleanup_block_t)(void);
21 | static __inline__ __attribute__((always_inline)) void almmkv_cleanup_block (__strong almmkv_cleanup_block_t *block) {
22 | (*block)();
23 | }
24 | #endif
25 |
26 |
27 | static size_t kPageSize = 512 * 1024;
28 |
29 | static NSString *const kMagicString = @"ALMMKV"; // won't change!
30 | static uint32_t kVersion = 1; // mmkv file format version
31 | static size_t kHeaderSize = 18; // sizeof("ALMMKV") + sizeof(version) + sizeof(content_size)
32 | /**
33 | * file format:
34 | * magic_string: "ALMMKV"
35 | * version: uint32_t
36 | * content_size: uint64_t
37 | * data: bytes
38 | */
39 |
40 | @implementation ALMMKV {
41 | int _fd;
42 | void *_mmptr;
43 | size_t _mmsize;
44 | size_t _cursize;
45 |
46 | NSMutableDictionary *_dict;
47 | almmkv::RWLock _lock;
48 | }
49 |
50 | + (instancetype)defaultMMKV {
51 | #if TARGET_OS_IPHONE || TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV || TARGET_OS_SIMULATOR
52 | NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
53 | #else
54 | NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES).firstObject;
55 | path = [path stringByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier];
56 | #endif
57 | path = [path stringByAppendingPathComponent:@"default.mmkv"];
58 | return [self mmkvWithPath:path];
59 | }
60 |
61 | static NSMapTable *kInstances;
62 | + (instancetype)mmkvWithPath:(NSString *)path {
63 | static dispatch_semaphore_t kLocalLock;
64 | static dispatch_once_t onceToken;
65 | dispatch_once(&onceToken, ^{
66 | kInstances = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory
67 | valueOptions:NSPointerFunctionsWeakMemory];
68 | kLocalLock = dispatch_semaphore_create(1);
69 | });
70 |
71 | path = path.stringByStandardizingPath;
72 | {
73 | dispatch_semaphore_wait(kLocalLock, DISPATCH_TIME_FOREVER);
74 | ALMMKV_defer {dispatch_semaphore_signal(kLocalLock);};
75 |
76 | ALMMKV *mmkv = [kInstances objectForKey:path];
77 | if (mmkv == nil) {
78 | mmkv = [[ALMMKV alloc] initWithFile:path];
79 | [kInstances setObject:mmkv forKey:path];
80 | }
81 | return mmkv;
82 | }
83 | }
84 |
85 | - (instancetype)init {
86 | [NSException raise:@"ALMMKVForbiddenException" format:@"Can initialize mmkv via -init"];
87 | return nil;
88 | }
89 |
90 | - (instancetype)initWithFile:(NSString *)path {
91 | self = [super init];
92 | if (self) {
93 | NSError *error = nil;
94 | if (![[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent]
95 | withIntermediateDirectories:YES
96 | attributes:nil
97 | error:&error]) {
98 | NSAssert(NO, @"Create path error: %@", error);
99 | return nil;
100 | }
101 |
102 | if(![self open:path]) {
103 | return nil;
104 | }
105 | }
106 | return self;
107 | }
108 |
109 | - (void)dealloc {
110 | [self cleanup];
111 | }
112 |
113 | - (BOOL)open:(NSString *)file {
114 | _fd = open([file fileSystemRepresentation], O_RDWR | O_CREAT, 0666);
115 | if (_fd == 0) {
116 | NSAssert(NO, @"Can not open file: %@", file);
117 | return NO;
118 | }
119 |
120 | struct stat statInfo;
121 | if( fstat( _fd, &statInfo ) != 0 ) {
122 | NSAssert(NO, @"Can not read file: %@", file);
123 | return NO;
124 | }
125 |
126 |
127 | if (![self mapWithSize:((statInfo.st_size / kPageSize) + 1) * kPageSize]) {
128 | return NO;
129 | }
130 |
131 | _dict = [NSMutableDictionary dictionary];
132 | if (statInfo.st_size == 0) {
133 | [self resetHeaderWithContentSize:0];
134 | _cursize = kHeaderSize;
135 | return YES;
136 | }
137 |
138 | char *ptr = (char *)_mmptr;
139 | // read file magic code
140 | NSData *data = [NSData dataWithBytes:ptr length:6];
141 | if (![kMagicString isEqualToString:[NSString stringWithUTF8String:(const char *)data.bytes]]) {
142 | NSAssert(NO, @"Not a mmkv file: %@", file);
143 | return NO;
144 | }
145 | // read version
146 | ptr += 6;
147 | // data = [NSData dataWithBytes:ptr length:4];
148 | // uint32_t ver = 0;
149 | // [data getBytes:&ver length:4];
150 |
151 | // read content-length
152 | ptr += 4;
153 | data = [NSData dataWithBytes:ptr length:8];
154 | uint64_t dataLength = 0;
155 | [data getBytes:&dataLength length:8];
156 | // if (dataLength + kHeaderSize > statInfo.st_size) {
157 | // NSAssert(NO, @"illegal file size");
158 | // return NO;
159 | // }
160 |
161 | // read contents
162 | ptr += 8;
163 | data = [NSData dataWithBytes:ptr length:MIN(dataLength, statInfo.st_size - kHeaderSize)];
164 | NSError *error;
165 | ALKVList *kvlist = [ALKVList parseFromData:data error:&error];
166 | for (ALKVPair *item in kvlist.itemArray) {
167 | if (item.name != nil && ![item.objcType isEqualToString:@"NSNull"]) {
168 | _dict[item.name] = item;
169 | }
170 | }
171 | [self reallocWithExtraSize:0];
172 | return YES;
173 | }
174 |
175 | - (void)cleanup {
176 | if (_mmptr) {
177 | munmap(_mmptr, _cursize);
178 | }
179 | if (_fd) {
180 | ftruncate(_fd, _cursize);
181 | close(_fd);
182 | }
183 | }
184 |
185 | #pragma mark - primitive types
186 | - (BOOL)boolForKey:(NSString *)key {
187 | ALKVPair *kv = [self _itemForKey:key];
188 | id val = [self _numberValue:kv];
189 | if (val == nil) {
190 | if (kv.valueOneOfCase == ALKVPair_Value_OneOfCase_StrVal) {
191 | val = kv.strVal;
192 | }
193 | }
194 | return [val boolValue];
195 | }
196 |
197 | - (NSInteger)integerForKey:(NSString *)key {
198 | ALKVPair *kv = [self _itemForKey:key];
199 | id val = [self _numberValue:kv];
200 | if (val == nil) {
201 | if (kv.valueOneOfCase == ALKVPair_Value_OneOfCase_StrVal) {
202 | val = kv.strVal;
203 | }
204 | }
205 | return [val integerValue];
206 | }
207 |
208 | - (float)floatForKey:(NSString *)key {
209 | ALKVPair *kv = [self _itemForKey:key];
210 | id val = [self _numberValue:kv];
211 | if (val == nil) {
212 | if (kv.valueOneOfCase == ALKVPair_Value_OneOfCase_StrVal) {
213 | val = kv.strVal;
214 | }
215 | }
216 | return [val floatValue];
217 | }
218 |
219 | - (double)doubleForKey:(NSString *)key {
220 | ALKVPair *kv = [self _itemForKey:key];
221 | id val = [self _numberValue:kv];
222 | if (val == nil) {
223 | if (kv.valueOneOfCase == ALKVPair_Value_OneOfCase_StrVal) {
224 | val = kv.strVal;
225 | }
226 | }
227 | return [val doubleValue];
228 | }
229 |
230 | #pragma mark - read: ObjC objects
231 | - (id)objectOfClass:(Class)cls forKey:(NSString *)key {
232 | ALKVPair *kv = [self _itemForKey:key];
233 |
234 | #ifndef CASE_CLASS
235 | #define CASE_CLASS(cls, type) if (cls == type.class || [cls isSubclassOfClass:type.class])
236 | #endif
237 |
238 | CASE_CLASS(cls, NSNumber) {
239 | return [self _numberValue:kv];
240 | }
241 | CASE_CLASS(cls, NSString) {
242 | if (kv.valueOneOfCase == ALKVPair_Value_OneOfCase_StrVal) {
243 | return kv.strVal;
244 | }
245 | return [[self _numberValue:kv] stringValue];
246 | }
247 | CASE_CLASS(cls, NSData) {
248 | if (kv.valueOneOfCase == ALKVPair_Value_OneOfCase_BinaryVal) {
249 | return kv.binaryVal;
250 | }
251 | return nil;
252 | }
253 | CASE_CLASS(cls, NSDate) {
254 | Class octype = [self _objCType:kv];
255 | CASE_CLASS(octype, NSDate) {
256 | id val = [self _unarchiveValueForClass:NSDate.class fromItem:kv];
257 | if (val == nil) {
258 | val = [self _numberValue:kv];
259 | return val ? [NSDate dateWithTimeIntervalSince1970:[val doubleValue]] : nil;
260 | }
261 | return val;
262 | }
263 | return nil;
264 | }
265 | CASE_CLASS(cls, NSURL) {
266 | Class octype = [self _objCType:kv];
267 | CASE_CLASS(octype, NSURL) {
268 | id val = [self _unarchiveValueForClass:NSURL.class fromItem:kv];
269 | if (val == nil && kv.valueOneOfCase == ALKVPair_Value_OneOfCase_StrVal) {
270 | return [NSURL URLWithString:kv.strVal];
271 | }
272 | return val;
273 | }
274 | return nil;
275 | }
276 |
277 | return [self _unarchiveValueForClass:NSURL.class fromItem:kv];
278 | }
279 |
280 | #pragma mark - read: private
281 | - (ALKVPair *)_itemForKey:(NSString *)key {
282 | self->_lock.lock_read();
283 | ALMMKV_defer { self->_lock.unlock_read(); };
284 |
285 | ALKVPair *kv = _dict[key];
286 | return kv;
287 | }
288 |
289 | - (Class)_objCType:(ALKVPair *)kv {
290 | if (kv.objcType) {
291 | return NSClassFromString(kv.objcType);
292 | }
293 | return nil;
294 | }
295 |
296 | - (NSNumber *)_numberValue:(ALKVPair *)kv{
297 | switch (kv.valueOneOfCase) {
298 | case ALKVPair_Value_OneOfCase_Sint32Val: return @(kv.sint32Val);
299 | case ALKVPair_Value_OneOfCase_Sint64Val: return @(kv.sint64Val);
300 | case ALKVPair_Value_OneOfCase_BoolVal: return @(kv.boolVal);
301 | case ALKVPair_Value_OneOfCase_FloatVal: return @(kv.floatVal);
302 | case ALKVPair_Value_OneOfCase_DoubleVal: return @(kv.doubleVal);
303 | case ALKVPair_Value_OneOfCase_BinaryVal:
304 | return [self _unarchiveValueForClass:NSNumber.class fromItem:kv];
305 | default: return nil;
306 | }
307 | }
308 |
309 | - (id)_unarchiveValueForClass:(Class)cls fromItem:(ALKVPair *)kv {
310 | if (kv.valueOneOfCase == ALKVPair_Value_OneOfCase_BinaryVal) {
311 | @try {
312 | id val = [NSKeyedUnarchiver unarchiveObjectWithData:kv.binaryVal];
313 | return [val isKindOfClass:cls] ? val : nil;
314 | } @catch (NSException *e) {
315 | return nil;
316 | }
317 | }
318 | return nil;
319 | }
320 |
321 | #pragma mark - set: primitive types
322 | - (void)setBool:(BOOL)val forKey:(NSString *)key {
323 | ALKVPair *kv = [ALKVPair message];
324 | kv.name = key;
325 | kv.boolVal = val;
326 | kv.objcType = @"NSNumber";
327 | [self append:kv];
328 | }
329 |
330 | - (void)setInteger:(NSInteger)intval forKey:(NSString *)key {
331 | ALKVPair *kv = [ALKVPair message];
332 | kv.name = key;
333 | kv.objcType = @"NSNumber";
334 | if (intval > INT_MAX) {
335 | kv.sint64Val = (int64_t)intval;
336 | } else {
337 | kv.sint32Val = (int32_t)intval;
338 | }
339 | [self append:kv];
340 | }
341 |
342 | - (void)setFloat:(float)val forKey:(NSString *)key {
343 | ALKVPair *kv = [ALKVPair message];
344 | kv.name = key;
345 | kv.floatVal = val;
346 | kv.objcType = @"NSNumber";
347 | [self append:kv];
348 | }
349 |
350 | - (void)setDouble:(double)val forKey:(NSString *)key {
351 | ALKVPair *kv = [ALKVPair message];
352 | kv.name = key;
353 | kv.doubleVal = val;
354 | kv.objcType = @"NSNumber";
355 | [self append:kv];
356 | }
357 |
358 | - (void)setObject:(id)obj forKey:(NSString *)key {
359 | if (obj == nil) {
360 | [self removeObjectForKey:key];
361 | return;
362 | }
363 |
364 | ALKVPair *kv = [ALKVPair message];
365 | kv.name = key;
366 | kv.objcType = NSStringFromClass([obj class]);
367 |
368 | if ([obj isKindOfClass:NSString.class]) {
369 | kv.strVal = (NSString *)obj;
370 | } else if ([obj isKindOfClass:NSNumber.class]) {
371 | kv.strVal = [(NSNumber *)obj stringValue];
372 | } else if ([obj isKindOfClass:NSData.class]) {
373 | kv.binaryVal = (NSData *)obj;
374 | } else if ([obj isKindOfClass:NSDate.class]) {
375 | kv.doubleVal = ((NSDate *)obj).timeIntervalSince1970;
376 | } else if ([obj isKindOfClass:NSURL.class]) {
377 | kv.strVal = [(NSURL *)obj absoluteString];
378 | } else {
379 | kv.binaryVal = [NSKeyedArchiver archivedDataWithRootObject:obj]; // should throw if exception.
380 | }
381 |
382 | [self append:kv];
383 | }
384 |
385 | - (void)removeObjectForKey:(NSString *)key {
386 | ALKVPair *kv = [ALKVPair message];
387 | kv.name = key;
388 | kv.objcType = @"NSNull";
389 | [self append:kv];
390 | }
391 |
392 | - (void)reset {
393 | self->_lock.lock_write();
394 | ALMMKV_defer { self->_lock.unlock_write(); };
395 |
396 | [_dict removeAllObjects];
397 | munmap(_mmptr, _mmsize);
398 | [self mapWithSize:kPageSize];
399 | [self resetHeaderWithContentSize:0];
400 | }
401 |
402 | - (void)resetHeaderWithContentSize:(uint64_t)dataLength {
403 | char *ptr = (char *)_mmptr;
404 | memcpy(ptr, [kMagicString dataUsingEncoding:NSUTF8StringEncoding].bytes, 6);
405 | ptr += 6;
406 | memcpy(ptr, &kVersion, 4);
407 | ptr += 4;
408 | memcpy(ptr, &dataLength, 8);
409 | }
410 |
411 | - (void)append:(ALKVPair *)item {
412 | self->_lock.lock_write();
413 | ALMMKV_defer { self->_lock.unlock_write(); };
414 |
415 | _dict[item.name] = item;
416 |
417 | NSMutableData *data = [NSMutableData data];
418 | [data appendBytes:"\n" length:1];
419 | [data appendData:item.delimitedData];
420 |
421 | if (data.length + _cursize >= _mmsize) {
422 | [self reallocWithExtraSize:data.length];
423 | } else {
424 | memcpy((char *)_mmptr + _cursize, data.bytes, data.length);
425 | _cursize += data.length;
426 |
427 | uint64_t dataLength = _cursize - kHeaderSize;
428 | memcpy((char *)_mmptr + 10, &dataLength, 8);
429 | }
430 | }
431 |
432 | - (BOOL)mapWithSize:(size_t)mapSize {
433 | _mmptr = mmap(NULL, mapSize, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, _fd, 0);
434 | if (_mmptr == MAP_FAILED) {
435 | NSAssert(NO, @"create mmap failed: %d", errno);
436 | return NO;
437 | }
438 | ftruncate(_fd, mapSize);
439 | _mmsize = mapSize;
440 | return YES;
441 | }
442 |
443 | - (void)reallocWithExtraSize:(size_t)size {
444 | ALKVList *kvlist = [ALKVList message];
445 | for (ALKVPair *item in _dict.allValues) {
446 | if (![item.objcType isEqualToString:@"NSNull"]) {
447 | [kvlist.itemArray addObject:item];
448 | }
449 | }
450 | NSData *data = kvlist.data;
451 | NSUInteger dataLength = data.length;
452 |
453 | size_t totalSize = dataLength + kHeaderSize;
454 | size_t newTotalSize = totalSize + size;
455 | if (newTotalSize >= _mmsize) {
456 | munmap(_mmptr, _mmsize);
457 | [self mapWithSize:((newTotalSize / kPageSize) + 1) * kPageSize];
458 | [self resetHeaderWithContentSize:0];
459 | }
460 | memcpy((char *)_mmptr + kHeaderSize, data.bytes, dataLength);
461 | memcpy((char *)_mmptr + 10, &dataLength, 8);
462 | _cursize = dataLength + kHeaderSize;
463 | }
464 |
465 | #if DEBUG
466 | + (void)dump {
467 | typeof(kInstances) tmp = kInstances;
468 | NSLog(@"instances: %@", tmp);
469 | }
470 | #endif
471 | @end
472 |
--------------------------------------------------------------------------------
/mmkv.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 435043A920F63BA7002D4A9B /* Almmkv.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 435043A520F63BA6002D4A9B /* Almmkv.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
11 | 435043AA20F63BA7002D4A9B /* ALMMKV.proto in Resources */ = {isa = PBXBuildFile; fileRef = 435043A620F63BA6002D4A9B /* ALMMKV.proto */; };
12 | 435043AB20F63BA7002D4A9B /* Almmkv.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 435043A720F63BA6002D4A9B /* Almmkv.pbobjc.h */; };
13 | 435043AC20F63BA7002D4A9B /* almmkv.h in Headers */ = {isa = PBXBuildFile; fileRef = 435043A820F63BA7002D4A9B /* almmkv.h */; };
14 | 435043B420F63D2D002D4A9B /* mmkvTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 435043B320F63D2D002D4A9B /* mmkvTests.m */; };
15 | 435043B620F63D2D002D4A9B /* mmkv.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4350439920F60228002D4A9B /* mmkv.framework */; };
16 | 435043BF20F70121002D4A9B /* almmkv.mm in Sources */ = {isa = PBXBuildFile; fileRef = 435043BE20F70121002D4A9B /* almmkv.mm */; };
17 | 43760B1F20FC8D88003B93D2 /* rwlock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43760B1D20FC8D87003B93D2 /* rwlock.cpp */; };
18 | 43760B2020FC8D88003B93D2 /* rwlock.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 43760B1E20FC8D88003B93D2 /* rwlock.hpp */; };
19 | 451A67A47C24416F0D6F3A9B /* Pods_mmkvTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B7D6DA96E5F7F54D4E242E1F /* Pods_mmkvTests.framework */; };
20 | 5B51C854D9ABCC6AB91E99C1 /* Pods_mmkv.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 435043BC20F641A3002D4A9B /* Pods_mmkv.framework */; };
21 | EE05E2A1E1D8954DC7CC3C5B /* Pods_mmkv.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 44E5F65235C6D5FC5FBD6D70 /* Pods_mmkv.framework */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXContainerItemProxy section */
25 | 435043B720F63D2D002D4A9B /* PBXContainerItemProxy */ = {
26 | isa = PBXContainerItemProxy;
27 | containerPortal = 4350439020F60228002D4A9B /* Project object */;
28 | proxyType = 1;
29 | remoteGlobalIDString = 4350439820F60228002D4A9B;
30 | remoteInfo = mmkv;
31 | };
32 | /* End PBXContainerItemProxy section */
33 |
34 | /* Begin PBXFileReference section */
35 | 4350439920F60228002D4A9B /* mmkv.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = mmkv.framework; sourceTree = BUILT_PRODUCTS_DIR; };
36 | 4350439D20F60228002D4A9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
37 | 435043A520F63BA6002D4A9B /* Almmkv.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Almmkv.pbobjc.m; sourceTree = ""; };
38 | 435043A620F63BA6002D4A9B /* ALMMKV.proto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ALMMKV.proto; sourceTree = ""; };
39 | 435043A720F63BA6002D4A9B /* Almmkv.pbobjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Almmkv.pbobjc.h; sourceTree = ""; };
40 | 435043A820F63BA7002D4A9B /* almmkv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = almmkv.h; sourceTree = ""; };
41 | 435043B120F63D2D002D4A9B /* mmkvTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = mmkvTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
42 | 435043B320F63D2D002D4A9B /* mmkvTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = mmkvTests.m; sourceTree = ""; };
43 | 435043B520F63D2D002D4A9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
44 | 435043BC20F641A3002D4A9B /* Pods_mmkv.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Pods_mmkv.framework; sourceTree = BUILT_PRODUCTS_DIR; };
45 | 435043BE20F70121002D4A9B /* almmkv.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = almmkv.mm; sourceTree = ""; };
46 | 43760B1D20FC8D87003B93D2 /* rwlock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rwlock.cpp; sourceTree = ""; };
47 | 43760B1E20FC8D88003B93D2 /* rwlock.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rwlock.hpp; sourceTree = ""; };
48 | 44E5F65235C6D5FC5FBD6D70 /* Pods_mmkv.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_mmkv.framework; sourceTree = BUILT_PRODUCTS_DIR; };
49 | 58262C1AC3FF1EB40A240EE3 /* Pods-mmkv.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mmkv.debug.xcconfig"; path = "Pods/Target Support Files/Pods-mmkv/Pods-mmkv.debug.xcconfig"; sourceTree = ""; };
50 | 8FB9706C806FAC062677045F /* Pods-mmkvTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mmkvTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-mmkvTests/Pods-mmkvTests.debug.xcconfig"; sourceTree = ""; };
51 | AFF74EA1D355BA41BD8C0C93 /* Pods-mmkvTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mmkvTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-mmkvTests/Pods-mmkvTests.release.xcconfig"; sourceTree = ""; };
52 | B7D6DA96E5F7F54D4E242E1F /* Pods_mmkvTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_mmkvTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
53 | BE37F0D843A1F6E51A752EC2 /* Pods-mmkv.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mmkv.release.xcconfig"; path = "Pods/Target Support Files/Pods-mmkv/Pods-mmkv.release.xcconfig"; sourceTree = ""; };
54 | /* End PBXFileReference section */
55 |
56 | /* Begin PBXFrameworksBuildPhase section */
57 | 4350439520F60228002D4A9B /* Frameworks */ = {
58 | isa = PBXFrameworksBuildPhase;
59 | buildActionMask = 2147483647;
60 | files = (
61 | EE05E2A1E1D8954DC7CC3C5B /* Pods_mmkv.framework in Frameworks */,
62 | 5B51C854D9ABCC6AB91E99C1 /* Pods_mmkv.framework in Frameworks */,
63 | );
64 | runOnlyForDeploymentPostprocessing = 0;
65 | };
66 | 435043AE20F63D2D002D4A9B /* Frameworks */ = {
67 | isa = PBXFrameworksBuildPhase;
68 | buildActionMask = 2147483647;
69 | files = (
70 | 435043B620F63D2D002D4A9B /* mmkv.framework in Frameworks */,
71 | 451A67A47C24416F0D6F3A9B /* Pods_mmkvTests.framework in Frameworks */,
72 | );
73 | runOnlyForDeploymentPostprocessing = 0;
74 | };
75 | /* End PBXFrameworksBuildPhase section */
76 |
77 | /* Begin PBXGroup section */
78 | 4350438F20F60228002D4A9B = {
79 | isa = PBXGroup;
80 | children = (
81 | 4350439B20F60228002D4A9B /* mmkv */,
82 | 435043B220F63D2D002D4A9B /* mmkvTests */,
83 | 4350439A20F60228002D4A9B /* Products */,
84 | 7D87AE16D770DF2AF2021CF1 /* Pods */,
85 | 472589B8C499FA60B955910C /* Frameworks */,
86 | );
87 | sourceTree = "";
88 | };
89 | 4350439A20F60228002D4A9B /* Products */ = {
90 | isa = PBXGroup;
91 | children = (
92 | 4350439920F60228002D4A9B /* mmkv.framework */,
93 | 435043B120F63D2D002D4A9B /* mmkvTests.xctest */,
94 | );
95 | name = Products;
96 | sourceTree = "";
97 | };
98 | 4350439B20F60228002D4A9B /* mmkv */ = {
99 | isa = PBXGroup;
100 | children = (
101 | 435043A820F63BA7002D4A9B /* almmkv.h */,
102 | 435043BE20F70121002D4A9B /* almmkv.mm */,
103 | 43760B1D20FC8D87003B93D2 /* rwlock.cpp */,
104 | 43760B1E20FC8D88003B93D2 /* rwlock.hpp */,
105 | 435043A420F63BA6002D4A9B /* protobuf */,
106 | 4350439D20F60228002D4A9B /* Info.plist */,
107 | );
108 | path = mmkv;
109 | sourceTree = "";
110 | };
111 | 435043A420F63BA6002D4A9B /* protobuf */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 435043A720F63BA6002D4A9B /* Almmkv.pbobjc.h */,
115 | 435043A520F63BA6002D4A9B /* Almmkv.pbobjc.m */,
116 | 435043A620F63BA6002D4A9B /* ALMMKV.proto */,
117 | );
118 | path = protobuf;
119 | sourceTree = "";
120 | };
121 | 435043B220F63D2D002D4A9B /* mmkvTests */ = {
122 | isa = PBXGroup;
123 | children = (
124 | 435043B320F63D2D002D4A9B /* mmkvTests.m */,
125 | 435043B520F63D2D002D4A9B /* Info.plist */,
126 | );
127 | path = mmkvTests;
128 | sourceTree = "";
129 | };
130 | 472589B8C499FA60B955910C /* Frameworks */ = {
131 | isa = PBXGroup;
132 | children = (
133 | 435043BC20F641A3002D4A9B /* Pods_mmkv.framework */,
134 | 44E5F65235C6D5FC5FBD6D70 /* Pods_mmkv.framework */,
135 | B7D6DA96E5F7F54D4E242E1F /* Pods_mmkvTests.framework */,
136 | );
137 | name = Frameworks;
138 | sourceTree = "";
139 | };
140 | 7D87AE16D770DF2AF2021CF1 /* Pods */ = {
141 | isa = PBXGroup;
142 | children = (
143 | 58262C1AC3FF1EB40A240EE3 /* Pods-mmkv.debug.xcconfig */,
144 | BE37F0D843A1F6E51A752EC2 /* Pods-mmkv.release.xcconfig */,
145 | 8FB9706C806FAC062677045F /* Pods-mmkvTests.debug.xcconfig */,
146 | AFF74EA1D355BA41BD8C0C93 /* Pods-mmkvTests.release.xcconfig */,
147 | );
148 | name = Pods;
149 | sourceTree = "";
150 | };
151 | /* End PBXGroup section */
152 |
153 | /* Begin PBXHeadersBuildPhase section */
154 | 4350439620F60228002D4A9B /* Headers */ = {
155 | isa = PBXHeadersBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | 43760B2020FC8D88003B93D2 /* rwlock.hpp in Headers */,
159 | 435043AC20F63BA7002D4A9B /* almmkv.h in Headers */,
160 | 435043AB20F63BA7002D4A9B /* Almmkv.pbobjc.h in Headers */,
161 | );
162 | runOnlyForDeploymentPostprocessing = 0;
163 | };
164 | /* End PBXHeadersBuildPhase section */
165 |
166 | /* Begin PBXNativeTarget section */
167 | 4350439820F60228002D4A9B /* mmkv */ = {
168 | isa = PBXNativeTarget;
169 | buildConfigurationList = 435043A120F60228002D4A9B /* Build configuration list for PBXNativeTarget "mmkv" */;
170 | buildPhases = (
171 | A9D7BD47827209253B32D160 /* [CP] Check Pods Manifest.lock */,
172 | 4350439420F60228002D4A9B /* Sources */,
173 | 4350439520F60228002D4A9B /* Frameworks */,
174 | 4350439620F60228002D4A9B /* Headers */,
175 | 4350439720F60228002D4A9B /* Resources */,
176 | );
177 | buildRules = (
178 | );
179 | dependencies = (
180 | );
181 | name = mmkv;
182 | productName = mmkv;
183 | productReference = 4350439920F60228002D4A9B /* mmkv.framework */;
184 | productType = "com.apple.product-type.framework";
185 | };
186 | 435043B020F63D2D002D4A9B /* mmkvTests */ = {
187 | isa = PBXNativeTarget;
188 | buildConfigurationList = 435043BB20F63D2D002D4A9B /* Build configuration list for PBXNativeTarget "mmkvTests" */;
189 | buildPhases = (
190 | F6B50120DF75073CBECD1E74 /* [CP] Check Pods Manifest.lock */,
191 | 435043AD20F63D2D002D4A9B /* Sources */,
192 | 435043AE20F63D2D002D4A9B /* Frameworks */,
193 | 435043AF20F63D2D002D4A9B /* Resources */,
194 | A82A45F50E854D5974928C75 /* [CP] Embed Pods Frameworks */,
195 | );
196 | buildRules = (
197 | );
198 | dependencies = (
199 | 435043B820F63D2D002D4A9B /* PBXTargetDependency */,
200 | );
201 | name = mmkvTests;
202 | productName = mmkvTests;
203 | productReference = 435043B120F63D2D002D4A9B /* mmkvTests.xctest */;
204 | productType = "com.apple.product-type.bundle.unit-test";
205 | };
206 | /* End PBXNativeTarget section */
207 |
208 | /* Begin PBXProject section */
209 | 4350439020F60228002D4A9B /* Project object */ = {
210 | isa = PBXProject;
211 | attributes = {
212 | LastUpgradeCheck = 0940;
213 | ORGANIZATIONNAME = alexlee002;
214 | TargetAttributes = {
215 | 4350439820F60228002D4A9B = {
216 | CreatedOnToolsVersion = 9.4.1;
217 | };
218 | 435043B020F63D2D002D4A9B = {
219 | CreatedOnToolsVersion = 9.4.1;
220 | };
221 | };
222 | };
223 | buildConfigurationList = 4350439320F60228002D4A9B /* Build configuration list for PBXProject "mmkv" */;
224 | compatibilityVersion = "Xcode 9.3";
225 | developmentRegion = en;
226 | hasScannedForEncodings = 0;
227 | knownRegions = (
228 | en,
229 | );
230 | mainGroup = 4350438F20F60228002D4A9B;
231 | productRefGroup = 4350439A20F60228002D4A9B /* Products */;
232 | projectDirPath = "";
233 | projectRoot = "";
234 | targets = (
235 | 4350439820F60228002D4A9B /* mmkv */,
236 | 435043B020F63D2D002D4A9B /* mmkvTests */,
237 | );
238 | };
239 | /* End PBXProject section */
240 |
241 | /* Begin PBXResourcesBuildPhase section */
242 | 4350439720F60228002D4A9B /* Resources */ = {
243 | isa = PBXResourcesBuildPhase;
244 | buildActionMask = 2147483647;
245 | files = (
246 | 435043AA20F63BA7002D4A9B /* ALMMKV.proto in Resources */,
247 | );
248 | runOnlyForDeploymentPostprocessing = 0;
249 | };
250 | 435043AF20F63D2D002D4A9B /* Resources */ = {
251 | isa = PBXResourcesBuildPhase;
252 | buildActionMask = 2147483647;
253 | files = (
254 | );
255 | runOnlyForDeploymentPostprocessing = 0;
256 | };
257 | /* End PBXResourcesBuildPhase section */
258 |
259 | /* Begin PBXShellScriptBuildPhase section */
260 | A82A45F50E854D5974928C75 /* [CP] Embed Pods Frameworks */ = {
261 | isa = PBXShellScriptBuildPhase;
262 | buildActionMask = 2147483647;
263 | files = (
264 | );
265 | inputPaths = (
266 | "${SRCROOT}/Pods/Target Support Files/Pods-mmkvTests/Pods-mmkvTests-frameworks.sh",
267 | "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework",
268 | );
269 | name = "[CP] Embed Pods Frameworks";
270 | outputPaths = (
271 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework",
272 | );
273 | runOnlyForDeploymentPostprocessing = 0;
274 | shellPath = /bin/sh;
275 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-mmkvTests/Pods-mmkvTests-frameworks.sh\"\n";
276 | showEnvVarsInLog = 0;
277 | };
278 | A9D7BD47827209253B32D160 /* [CP] Check Pods Manifest.lock */ = {
279 | isa = PBXShellScriptBuildPhase;
280 | buildActionMask = 2147483647;
281 | files = (
282 | );
283 | inputPaths = (
284 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
285 | "${PODS_ROOT}/Manifest.lock",
286 | );
287 | name = "[CP] Check Pods Manifest.lock";
288 | outputPaths = (
289 | "$(DERIVED_FILE_DIR)/Pods-mmkv-checkManifestLockResult.txt",
290 | );
291 | runOnlyForDeploymentPostprocessing = 0;
292 | shellPath = /bin/sh;
293 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
294 | showEnvVarsInLog = 0;
295 | };
296 | F6B50120DF75073CBECD1E74 /* [CP] Check Pods Manifest.lock */ = {
297 | isa = PBXShellScriptBuildPhase;
298 | buildActionMask = 2147483647;
299 | files = (
300 | );
301 | inputPaths = (
302 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
303 | "${PODS_ROOT}/Manifest.lock",
304 | );
305 | name = "[CP] Check Pods Manifest.lock";
306 | outputPaths = (
307 | "$(DERIVED_FILE_DIR)/Pods-mmkvTests-checkManifestLockResult.txt",
308 | );
309 | runOnlyForDeploymentPostprocessing = 0;
310 | shellPath = /bin/sh;
311 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
312 | showEnvVarsInLog = 0;
313 | };
314 | /* End PBXShellScriptBuildPhase section */
315 |
316 | /* Begin PBXSourcesBuildPhase section */
317 | 4350439420F60228002D4A9B /* Sources */ = {
318 | isa = PBXSourcesBuildPhase;
319 | buildActionMask = 2147483647;
320 | files = (
321 | 435043A920F63BA7002D4A9B /* Almmkv.pbobjc.m in Sources */,
322 | 435043BF20F70121002D4A9B /* almmkv.mm in Sources */,
323 | 43760B1F20FC8D88003B93D2 /* rwlock.cpp in Sources */,
324 | );
325 | runOnlyForDeploymentPostprocessing = 0;
326 | };
327 | 435043AD20F63D2D002D4A9B /* Sources */ = {
328 | isa = PBXSourcesBuildPhase;
329 | buildActionMask = 2147483647;
330 | files = (
331 | 435043B420F63D2D002D4A9B /* mmkvTests.m in Sources */,
332 | );
333 | runOnlyForDeploymentPostprocessing = 0;
334 | };
335 | /* End PBXSourcesBuildPhase section */
336 |
337 | /* Begin PBXTargetDependency section */
338 | 435043B820F63D2D002D4A9B /* PBXTargetDependency */ = {
339 | isa = PBXTargetDependency;
340 | target = 4350439820F60228002D4A9B /* mmkv */;
341 | targetProxy = 435043B720F63D2D002D4A9B /* PBXContainerItemProxy */;
342 | };
343 | /* End PBXTargetDependency section */
344 |
345 | /* Begin XCBuildConfiguration section */
346 | 4350439F20F60228002D4A9B /* Debug */ = {
347 | isa = XCBuildConfiguration;
348 | buildSettings = {
349 | ALWAYS_SEARCH_USER_PATHS = NO;
350 | CLANG_ANALYZER_NONNULL = YES;
351 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
353 | CLANG_CXX_LIBRARY = "libc++";
354 | CLANG_ENABLE_MODULES = YES;
355 | CLANG_ENABLE_OBJC_ARC = YES;
356 | CLANG_ENABLE_OBJC_WEAK = YES;
357 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
358 | CLANG_WARN_BOOL_CONVERSION = YES;
359 | CLANG_WARN_COMMA = YES;
360 | CLANG_WARN_CONSTANT_CONVERSION = YES;
361 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
362 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
363 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
364 | CLANG_WARN_EMPTY_BODY = YES;
365 | CLANG_WARN_ENUM_CONVERSION = YES;
366 | CLANG_WARN_INFINITE_RECURSION = YES;
367 | CLANG_WARN_INT_CONVERSION = YES;
368 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
369 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
370 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
371 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
372 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
373 | CLANG_WARN_STRICT_PROTOTYPES = YES;
374 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
375 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
376 | CLANG_WARN_UNREACHABLE_CODE = YES;
377 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
378 | CODE_SIGN_IDENTITY = "iPhone Developer";
379 | COPY_PHASE_STRIP = NO;
380 | CURRENT_PROJECT_VERSION = 1;
381 | DEBUG_INFORMATION_FORMAT = dwarf;
382 | ENABLE_STRICT_OBJC_MSGSEND = YES;
383 | ENABLE_TESTABILITY = YES;
384 | GCC_C_LANGUAGE_STANDARD = gnu11;
385 | GCC_DYNAMIC_NO_PIC = NO;
386 | GCC_NO_COMMON_BLOCKS = YES;
387 | GCC_OPTIMIZATION_LEVEL = 0;
388 | GCC_PREPROCESSOR_DEFINITIONS = (
389 | "DEBUG=1",
390 | "$(inherited)",
391 | );
392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
394 | GCC_WARN_UNDECLARED_SELECTOR = YES;
395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
396 | GCC_WARN_UNUSED_FUNCTION = YES;
397 | GCC_WARN_UNUSED_VARIABLE = YES;
398 | IPHONEOS_DEPLOYMENT_TARGET = 11.4;
399 | MTL_ENABLE_DEBUG_INFO = YES;
400 | ONLY_ACTIVE_ARCH = YES;
401 | SDKROOT = iphoneos;
402 | VERSIONING_SYSTEM = "apple-generic";
403 | VERSION_INFO_PREFIX = "";
404 | };
405 | name = Debug;
406 | };
407 | 435043A020F60228002D4A9B /* Release */ = {
408 | isa = XCBuildConfiguration;
409 | buildSettings = {
410 | ALWAYS_SEARCH_USER_PATHS = NO;
411 | CLANG_ANALYZER_NONNULL = YES;
412 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
413 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
414 | CLANG_CXX_LIBRARY = "libc++";
415 | CLANG_ENABLE_MODULES = YES;
416 | CLANG_ENABLE_OBJC_ARC = YES;
417 | CLANG_ENABLE_OBJC_WEAK = YES;
418 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
419 | CLANG_WARN_BOOL_CONVERSION = YES;
420 | CLANG_WARN_COMMA = YES;
421 | CLANG_WARN_CONSTANT_CONVERSION = YES;
422 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
423 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
424 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
425 | CLANG_WARN_EMPTY_BODY = YES;
426 | CLANG_WARN_ENUM_CONVERSION = YES;
427 | CLANG_WARN_INFINITE_RECURSION = YES;
428 | CLANG_WARN_INT_CONVERSION = YES;
429 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
430 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
431 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
432 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
433 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
434 | CLANG_WARN_STRICT_PROTOTYPES = YES;
435 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
436 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
437 | CLANG_WARN_UNREACHABLE_CODE = YES;
438 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
439 | CODE_SIGN_IDENTITY = "iPhone Developer";
440 | COPY_PHASE_STRIP = NO;
441 | CURRENT_PROJECT_VERSION = 1;
442 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
443 | ENABLE_NS_ASSERTIONS = NO;
444 | ENABLE_STRICT_OBJC_MSGSEND = YES;
445 | GCC_C_LANGUAGE_STANDARD = gnu11;
446 | GCC_NO_COMMON_BLOCKS = YES;
447 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
448 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
449 | GCC_WARN_UNDECLARED_SELECTOR = YES;
450 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
451 | GCC_WARN_UNUSED_FUNCTION = YES;
452 | GCC_WARN_UNUSED_VARIABLE = YES;
453 | IPHONEOS_DEPLOYMENT_TARGET = 11.4;
454 | MTL_ENABLE_DEBUG_INFO = NO;
455 | SDKROOT = iphoneos;
456 | VALIDATE_PRODUCT = YES;
457 | VERSIONING_SYSTEM = "apple-generic";
458 | VERSION_INFO_PREFIX = "";
459 | };
460 | name = Release;
461 | };
462 | 435043A220F60228002D4A9B /* Debug */ = {
463 | isa = XCBuildConfiguration;
464 | baseConfigurationReference = 58262C1AC3FF1EB40A240EE3 /* Pods-mmkv.debug.xcconfig */;
465 | buildSettings = {
466 | CODE_SIGN_IDENTITY = "";
467 | CODE_SIGN_STYLE = Automatic;
468 | DEFINES_MODULE = YES;
469 | DEVELOPMENT_TEAM = 738UU3Y57V;
470 | DYLIB_COMPATIBILITY_VERSION = 1;
471 | DYLIB_CURRENT_VERSION = 1;
472 | DYLIB_INSTALL_NAME_BASE = "@rpath";
473 | INFOPLIST_FILE = mmkv/Info.plist;
474 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
475 | LD_RUNPATH_SEARCH_PATHS = (
476 | "$(inherited)",
477 | "@executable_path/Frameworks",
478 | "@loader_path/Frameworks",
479 | );
480 | PRODUCT_BUNDLE_IDENTIFIER = me.alexlee002.mmkv;
481 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
482 | SKIP_INSTALL = YES;
483 | TARGETED_DEVICE_FAMILY = "1,2";
484 | };
485 | name = Debug;
486 | };
487 | 435043A320F60228002D4A9B /* Release */ = {
488 | isa = XCBuildConfiguration;
489 | baseConfigurationReference = BE37F0D843A1F6E51A752EC2 /* Pods-mmkv.release.xcconfig */;
490 | buildSettings = {
491 | CODE_SIGN_IDENTITY = "";
492 | CODE_SIGN_STYLE = Automatic;
493 | DEFINES_MODULE = YES;
494 | DEVELOPMENT_TEAM = 738UU3Y57V;
495 | DYLIB_COMPATIBILITY_VERSION = 1;
496 | DYLIB_CURRENT_VERSION = 1;
497 | DYLIB_INSTALL_NAME_BASE = "@rpath";
498 | INFOPLIST_FILE = mmkv/Info.plist;
499 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
500 | LD_RUNPATH_SEARCH_PATHS = (
501 | "$(inherited)",
502 | "@executable_path/Frameworks",
503 | "@loader_path/Frameworks",
504 | );
505 | PRODUCT_BUNDLE_IDENTIFIER = me.alexlee002.mmkv;
506 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
507 | SKIP_INSTALL = YES;
508 | TARGETED_DEVICE_FAMILY = "1,2";
509 | };
510 | name = Release;
511 | };
512 | 435043B920F63D2D002D4A9B /* Debug */ = {
513 | isa = XCBuildConfiguration;
514 | baseConfigurationReference = 8FB9706C806FAC062677045F /* Pods-mmkvTests.debug.xcconfig */;
515 | buildSettings = {
516 | CODE_SIGN_STYLE = Automatic;
517 | DEVELOPMENT_TEAM = 738UU3Y57V;
518 | FRAMEWORK_SEARCH_PATHS = "$(inherited)";
519 | INFOPLIST_FILE = mmkvTests/Info.plist;
520 | LD_RUNPATH_SEARCH_PATHS = (
521 | "$(inherited)",
522 | "@executable_path/Frameworks",
523 | "@loader_path/Frameworks",
524 | );
525 | PRODUCT_BUNDLE_IDENTIFIER = me.alexlee002.mmkvTests;
526 | PRODUCT_NAME = "$(TARGET_NAME)";
527 | TARGETED_DEVICE_FAMILY = "1,2";
528 | };
529 | name = Debug;
530 | };
531 | 435043BA20F63D2D002D4A9B /* Release */ = {
532 | isa = XCBuildConfiguration;
533 | baseConfigurationReference = AFF74EA1D355BA41BD8C0C93 /* Pods-mmkvTests.release.xcconfig */;
534 | buildSettings = {
535 | CODE_SIGN_STYLE = Automatic;
536 | DEVELOPMENT_TEAM = 738UU3Y57V;
537 | FRAMEWORK_SEARCH_PATHS = "$(inherited)";
538 | INFOPLIST_FILE = mmkvTests/Info.plist;
539 | LD_RUNPATH_SEARCH_PATHS = (
540 | "$(inherited)",
541 | "@executable_path/Frameworks",
542 | "@loader_path/Frameworks",
543 | );
544 | PRODUCT_BUNDLE_IDENTIFIER = me.alexlee002.mmkvTests;
545 | PRODUCT_NAME = "$(TARGET_NAME)";
546 | TARGETED_DEVICE_FAMILY = "1,2";
547 | };
548 | name = Release;
549 | };
550 | /* End XCBuildConfiguration section */
551 |
552 | /* Begin XCConfigurationList section */
553 | 4350439320F60228002D4A9B /* Build configuration list for PBXProject "mmkv" */ = {
554 | isa = XCConfigurationList;
555 | buildConfigurations = (
556 | 4350439F20F60228002D4A9B /* Debug */,
557 | 435043A020F60228002D4A9B /* Release */,
558 | );
559 | defaultConfigurationIsVisible = 0;
560 | defaultConfigurationName = Release;
561 | };
562 | 435043A120F60228002D4A9B /* Build configuration list for PBXNativeTarget "mmkv" */ = {
563 | isa = XCConfigurationList;
564 | buildConfigurations = (
565 | 435043A220F60228002D4A9B /* Debug */,
566 | 435043A320F60228002D4A9B /* Release */,
567 | );
568 | defaultConfigurationIsVisible = 0;
569 | defaultConfigurationName = Release;
570 | };
571 | 435043BB20F63D2D002D4A9B /* Build configuration list for PBXNativeTarget "mmkvTests" */ = {
572 | isa = XCConfigurationList;
573 | buildConfigurations = (
574 | 435043B920F63D2D002D4A9B /* Debug */,
575 | 435043BA20F63D2D002D4A9B /* Release */,
576 | );
577 | defaultConfigurationIsVisible = 0;
578 | defaultConfigurationName = Release;
579 | };
580 | /* End XCConfigurationList section */
581 | };
582 | rootObject = 4350439020F60228002D4A9B /* Project object */;
583 | }
584 |
--------------------------------------------------------------------------------