From 5d65a98f78802d76f56bad26fddee74d997dae15 Mon Sep 17 00:00:00 2001 From: wangjufan Date: Tue, 21 Jan 2020 23:31:57 +0800 Subject: [PATCH 1/2] feat: thread safe --- YYModel/NSObject+YYModel.m | 7 ++++++- YYModel/YYClassInfo.m | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/YYModel/NSObject+YYModel.m b/YYModel/NSObject+YYModel.m index 3d7c470..6ef4b4b 100644 --- a/YYModel/NSObject+YYModel.m +++ b/YYModel/NSObject+YYModel.m @@ -641,7 +641,12 @@ + (instancetype)metaWithClass:(Class)cls { meta = [[_YYModelMeta alloc] initWithClass:cls]; if (meta) { dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); - CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta)); + _YYModelMeta *rmeta = CFDictionaryGetValue(cache, (__bridge const void *)(cls)); + if (!rmeta) { + CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta)); + } else { + meta = rmeta; + } dispatch_semaphore_signal(lock); } } diff --git a/YYModel/YYClassInfo.m b/YYModel/YYClassInfo.m index 16df6d9..7cae42e 100644 --- a/YYModel/YYClassInfo.m +++ b/YYModel/YYClassInfo.m @@ -347,7 +347,12 @@ + (instancetype)classInfoWithClass:(Class)cls { info = [[YYClassInfo alloc] initWithClass:cls]; if (info) { dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); - CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); + YYClassInfo *rinfo = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); + if (!rinfo) { + CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); + } else { + info = rinfo; + } dispatch_semaphore_signal(lock); } } From 5ff693c88cf1c5ffbeae99ea5d5c8211a5cc58e1 Mon Sep 17 00:00:00 2001 From: wangjufan Date: Wed, 22 Jan 2020 22:21:29 +0800 Subject: [PATCH 2/2] feat: thread safe test case --- Framework/YYModel.xcodeproj/project.pbxproj | 4 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++ YYModelTests/YYTestThreadSafe.m | 109 ++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 Framework/YYModel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 YYModelTests/YYTestThreadSafe.m diff --git a/Framework/YYModel.xcodeproj/project.pbxproj b/Framework/YYModel.xcodeproj/project.pbxproj index a912ebd..67d50cb 100644 --- a/Framework/YYModel.xcodeproj/project.pbxproj +++ b/Framework/YYModel.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0D84B87223D890F7005CC9E7 /* YYTestThreadSafe.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D84B87123D890F7005CC9E7 /* YYTestThreadSafe.m */; }; D927FA881F36EB6C007B972E /* YYModel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D927FA7E1F36EB6C007B972E /* YYModel.framework */; }; D930C3B51F36EDC4005A2D00 /* NSObject+YYModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D930C3B01F36EDC4005A2D00 /* NSObject+YYModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; D930C3B61F36EDC4005A2D00 /* NSObject+YYModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D930C3B11F36EDC4005A2D00 /* NSObject+YYModel.m */; }; @@ -39,6 +40,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0D84B87123D890F7005CC9E7 /* YYTestThreadSafe.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YYTestThreadSafe.m; sourceTree = ""; }; D927FA7E1F36EB6C007B972E /* YYModel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YYModel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D927FA821F36EB6C007B972E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Framework/Info.plist; sourceTree = ""; }; D927FA871F36EB6C007B972E /* YYModelTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YYModelTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -116,6 +118,7 @@ D97394F71F36EE2D00FC2DEB /* YYModelTests */ = { isa = PBXGroup; children = ( + 0D84B87123D890F7005CC9E7 /* YYTestThreadSafe.m */, D97394F91F36EE2D00FC2DEB /* YYTestAutoTypeConvert.m */, D97394FA1F36EE2D00FC2DEB /* YYTestBlacklistWhitelist.m */, D97394FB1F36EE2D00FC2DEB /* YYTestClassInfo.m */, @@ -262,6 +265,7 @@ D930C3B71F36EDC4005A2D00 /* NSObject+YYModel.m in Sources */, D973950E1F36EE2D00FC2DEB /* YYTestModelMapper.m in Sources */, D97395101F36EE2D00FC2DEB /* YYTestNestModel.m in Sources */, + 0D84B87223D890F7005CC9E7 /* YYTestThreadSafe.m in Sources */, D973950D1F36EE2D00FC2DEB /* YYTestHelper.m in Sources */, D973950A1F36EE2D00FC2DEB /* YYTestCustomClass.m in Sources */, D97395081F36EE2D00FC2DEB /* YYTestClassInfo.m in Sources */, diff --git a/Framework/YYModel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Framework/YYModel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Framework/YYModel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/YYModelTests/YYTestThreadSafe.m b/YYModelTests/YYTestThreadSafe.m new file mode 100644 index 0000000..81dc49c --- /dev/null +++ b/YYModelTests/YYTestThreadSafe.m @@ -0,0 +1,109 @@ +// +// YYTestThreadSafe.m +// YYModelTests +// +// Created by jufan wang on 2020/1/22. +// Copyright © 2020 ibireme. All rights reserved. +// + +#import +#import "YYModel.h" +#import "YYTestHelper.h" + + +@interface YYTestThreadSafeModel : NSObject + +@property bool boolValue; +@property BOOL BOOLValue; +@property char charValue; +@property unsigned char unsignedCharValue; +@property short shortValue; +@property unsigned short unsignedShortValue; +@property int intValue; +@property unsigned int unsignedIntValue; +@property long longValue; +@property unsigned long unsignedLongValue; +@property long long longLongValue; +@property unsigned long long unsignedLongLongValue; +@property float floatValue; +@property double doubleValue; +@property long double longDoubleValue; +@property (strong) Class classValue; +@property SEL selectorValue; +@property (copy) void (^blockValue)(); +@property void *pointerValue; +@property CGRect structValue; +@property CGPoint pointValue; + +@end + + +@implementation YYTestThreadSafeModel ++ (NSDictionary *)modelCustomPropertyMapper { + return @{ @"boolValue" : @"v", + @"BOOLValue" : @"v", + @"charValue" : @"v" + }; +} + + +@end + + + + + +@interface YYTestThreadSafe : XCTestCase + +@end + +@implementation YYTestThreadSafe + +- (void)testThreadSafe { + NSString *json; + NSOperationQueue * queue = [[NSOperationQueue alloc] init]; + + json = @"[{\"v\":1},{\"v\":2},{\"v\":3}]"; + [queue setSuspended:YES]; + for (int i = 0; i < 100; i++) { + [queue addOperationWithBlock:^{ + NSArray *array = [NSArray yy_modelArrayWithClass:YYTestThreadSafeModel.class json:json]; + XCTAssertTrue(array.count == 3); + XCTAssertTrue([array.firstObject isKindOfClass:[YYTestThreadSafeModel class]]); + }]; + + } + [queue setSuspended:NO]; + sleep(200); + for (int i = 0; i < 100; i++) { + [queue addOperationWithBlock:^{ + NSArray *array = [NSArray yy_modelArrayWithClass:YYTestThreadSafeModel.class json:json]; + XCTAssertTrue(array.count == 3); + XCTAssertTrue([array.firstObject isKindOfClass:[YYTestThreadSafeModel class]]); + }]; + + } + sleep(200); +} + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end