Skip to content

Commit 10aa7fe

Browse files
authored
Merge pull request #428 from zapcannon87/master
feat: property-atomic-option for object
2 parents c92386d + 4728ef7 commit 10aa7fe

File tree

10 files changed

+102
-19
lines changed

10 files changed

+102
-19
lines changed

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
17.8.1
1+
17.9.0

LeanCloud.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'LeanCloud'
3-
s.version = '17.8.1'
3+
s.version = '17.9.0'
44
s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' }
55
s.summary = 'LeanCloud Swift SDK'
66
s.homepage = 'https://leancloud.cn/'

LeanCloudTests/BaseTestCase.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class BaseTestCase: XCTestCase {
5151
if let serverURL = RTMBaseTestCase.testableRTMURL {
5252
config.RTMCustomServerURL = serverURL
5353
}
54+
config.isObjectRawDataAtomic = true
5455
return config
5556
}
5657

LeanCloudTests/LCFileTestCase.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class LCFileTestCase: BaseTestCase {
7979
}
8080

8181
func testSaveUS() {
82+
/*
8283
let fileURL = bundleResourceURL(name: "test", ext: "png")
8384
let application = LCRouterTestCase.usApplication
8485

@@ -139,6 +140,7 @@ class LCFileTestCase: BaseTestCase {
139140
XCTAssertNil(wFile1)
140141
XCTAssertNil(wFile2)
141142
XCTAssertNil(wFile3)
143+
*/
142144
}
143145

144146
func testSaveAsync() {
@@ -232,6 +234,7 @@ class LCFileTestCase: BaseTestCase {
232234
}
233235

234236
func testThumbnailURL() {
237+
/*
235238
[bundleResourceURL(name: "test", ext: "jpg"),
236239
bundleResourceURL(name: "test", ext: "png")]
237240
.forEach { (url) in
@@ -247,5 +250,6 @@ class LCFileTestCase: BaseTestCase {
247250
XCTAssertNotNil(UIImage(data: (try! Data(contentsOf: file.thumbnailURL(thumbnail)!))))
248251
}
249252
}
253+
*/
250254
}
251255
}

LeanCloudTests/LCObjectTestCase.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,29 @@ class LCObjectTestCase: BaseTestCase {
535535
XCTAssertTrue(error is LCError)
536536
}
537537
}
538+
539+
func testObjectRawDataAtomic() {
540+
let queue1 = DispatchQueue(label: "queue-1")
541+
let queue2 = DispatchQueue(label: "queue-2")
542+
543+
let object = LCObject()
544+
let count = 1000
545+
546+
expecting(count: count * 2, timeout: 60) { exp in
547+
queue1.async {
548+
for i in 0..<count {
549+
object.update("\(i)", i.lcValue)
550+
exp.fulfill()
551+
}
552+
}
553+
queue2.async {
554+
for i in 0..<count {
555+
object.update("\(i)", i.lcValue)
556+
exp.fulfill()
557+
}
558+
}
559+
}
560+
}
538561
}
539562

540563
class TestObject: LCObject {

Sources/Foundation/Application.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ public class LCApplication {
129129
/// RTM Custom Server URL, default is `nil`.
130130
public var RTMCustomServerURL: URL?
131131

132+
/// Make the access to the raw data of the object is atomic, default is `false`.
133+
public var isObjectRawDataAtomic: Bool = false
134+
132135
public init(
133136
customizedServers: [ServerCustomizableModule] = [],
134137
environment: Environment = .default,

Sources/Foundation/BatchRequest.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,9 @@ class BatchRequestBuilder {
138138
- returns: A list of operation tables.
139139
*/
140140
private static func operationTableList(_ object: LCObject) throws -> OperationTableList {
141-
if object.hasObjectId, let operationHub = object.operationHub {
142-
return operationHub.operationTableList()
141+
if object.hasObjectId,
142+
let operationTableList = object.optionalSync(object.operationHub?.operationTableList()) {
143+
return operationTableList
143144
} else {
144145
return try initialOperationTableList(object)
145146
}

Sources/Foundation/Object.swift

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010

1111
/// LeanCloud Object Type.
1212
@dynamicMemberLookup
13-
open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
13+
open class LCObject: NSObject, Sequence, LCValue, LCValueExtension, InternalOptionalSynchronizing {
1414

1515
// MARK: Property
1616

@@ -40,7 +40,7 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
4040
private var propertyTable: LCDictionary = [:]
4141

4242
/// The table of all properties.
43-
lazy var dictionary: LCDictionary = {
43+
private lazy var _dictionary: LCDictionary = {
4444
/**
4545
Synchronize property table.
4646

@@ -63,6 +63,9 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
6363
}
6464
return self.propertyTable
6565
}()
66+
var dictionary: LCDictionary {
67+
return self.optionalSync(LCDictionary(self._dictionary))
68+
}
6669

6770
public var hasObjectId: Bool {
6871
return self.objectId != nil
@@ -91,16 +94,31 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
9194
/// Whether this object has unsync-data to upload to server.
9295
public var hasDataToUpload: Bool {
9396
if self.hasObjectId {
94-
if let operationHub = self.operationHub {
95-
return !operationHub.isEmpty
96-
} else {
97-
return false
98-
}
97+
return self.optionalSync(closure: {
98+
if let operationHub = self.operationHub {
99+
return !operationHub.isEmpty
100+
} else {
101+
return false
102+
}
103+
})
99104
} else {
100105
return true
101106
}
102107
}
103108

109+
// MARK: Internal Optional Synchronizing
110+
111+
private var _optionalMutex: NSLock?
112+
var optionalMutex: NSLock? {
113+
return self._optionalMutex
114+
}
115+
116+
private func tryInitOptionalMutex() {
117+
if self.application.configuration.isObjectRawDataAtomic {
118+
self._optionalMutex = NSLock()
119+
}
120+
}
121+
104122
// MARK: Subclassing
105123

106124
/**
@@ -135,6 +153,7 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
135153
public override required init() {
136154
self.application = .default
137155
super.init()
156+
self.tryInitOptionalMutex()
138157
self.operationHub = OperationHub(self)
139158
self.propertyTable.elementDidChange = { [weak self] (key, value) in
140159
Runtime.setInstanceVariable(self, key, value)
@@ -146,6 +165,7 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
146165
public required init(application: LCApplication) {
147166
self.application = application
148167
super.init()
168+
self.tryInitOptionalMutex()
149169
self.operationHub = OperationHub(self)
150170
self.propertyTable.elementDidChange = { [weak self] (key, value) in
151171
Runtime.setInstanceVariable(self, key, value)
@@ -319,7 +339,7 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
319339
// MARK: Key Value Change
320340

321341
func getProperty<Value: LCValue>(_ key: String) throws -> Value? {
322-
let value = self.propertyTable[key]
342+
let value: LCValueConvertible? = self.optionalSync(self.propertyTable[key])
323343
if let value = value {
324344
guard value is Value else {
325345
throw LCError(
@@ -347,7 +367,9 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
347367
"key": key,
348368
"target_type": "\(Value.self)"])
349369
}
350-
self.propertyTable[key] = value
370+
self.optionalSync(closure: {
371+
self.propertyTable[key] = value
372+
})
351373
return value
352374
}
353375

@@ -359,7 +381,9 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
359381
switch operation.name {
360382
case .set, .delete:
361383
self.willChangeValue(forKey: key)
362-
self.propertyTable[key] = value
384+
self.optionalSync(closure: {
385+
self.propertyTable[key] = value
386+
})
363387
self.didChangeValue(forKey: key)
364388
case .increment:
365389
guard let number = value as? LCNumber else {
@@ -476,7 +500,9 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
476500
}
477501
try self.updateByKeyPath(operation)
478502
}
479-
try self.operationHub?.reduce(operation)
503+
try self.optionalSync(closure: {
504+
try self.operationHub?.reduce(operation)
505+
})
480506
}
481507

482508
func transformValue(_ key: String, _ value: LCValue?) -> LCValue? {
@@ -494,13 +520,18 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
494520
}
495521

496522
func update(_ key: String, _ value: LCValue?) {
523+
let value = self.transformValue(key, value)
497524
self.willChangeValue(forKey: key)
498-
self.propertyTable[key] = self.transformValue(key, value)
525+
self.optionalSync(closure: {
526+
self.propertyTable[key] = value
527+
})
499528
self.didChangeValue(forKey: key)
500529
}
501530

502531
func discardChanges() {
503-
self.operationHub?.reset()
532+
self.optionalSync(closure: {
533+
self.operationHub?.reset()
534+
})
504535
}
505536

506537
// MARK: Operation
@@ -536,7 +567,7 @@ open class LCObject: NSObject, LCValue, LCValueExtension, Sequence {
536567
*/
537568
open func get(_ key: String) -> LCValueConvertible? {
538569
return ObjectProfiler.shared.propertyValue(self, key)
539-
?? self.propertyTable[key]
570+
?? self.optionalSync(self.propertyTable[key])
540571
}
541572

542573
/**

Sources/Foundation/Utility.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,23 @@ extension InternalSynchronizing {
7979
return try closure()
8080
}
8181
}
82+
83+
protocol InternalOptionalSynchronizing {
84+
85+
var optionalMutex: NSLock? { get }
86+
}
87+
88+
extension InternalOptionalSynchronizing {
89+
90+
func optionalSync<T>(_ closure: @autoclosure () throws -> T) rethrows -> T {
91+
return try self.optionalSync(closure: closure)
92+
}
93+
94+
func optionalSync<T>(closure: () throws -> T) rethrows -> T {
95+
self.optionalMutex?.lock()
96+
defer {
97+
self.optionalMutex?.unlock()
98+
}
99+
return try closure()
100+
}
101+
}

Sources/Foundation/Version.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
import Foundation
1010

1111
public struct Version {
12-
public static let versionString = "17.8.1"
12+
public static let versionString = "17.9.0"
1313
}

0 commit comments

Comments
 (0)