Khi khởi tạo managedObjectContext(MOC) thì sẽ có thể lựa chọn 1 trong 2 loại queue để khởi tạo MOC, đó là:
NSMainQueueConcurrencyType (main Thread)
NSPrivateQueueConcurrencyType (background Thread)
NSMainQueueConcurrencyType chỉ có thể được sử dụng trên main queue.
NSPrivateQueueConcurrencyType tạo ra 1 queue riêng để sử dụng. Vì queue này là private, nên chỉ có thể access queue thông qua hàm perform(_:) và performAndWait(_:) của MOC .
Nếu ứng dụng sử dụng nhiều thao tác data processing (parse JSON to data, …) thì việc sử dụng trên main queue sẽ gây ra block main. Khi đó, có thể khởi tạo 1 context dùng private queue và thực hiện xử lí data trên đó.
Trước khi sử dụng CoreData với MultiThread, chú í đến điều Apple recommend:
Hãy chắc chắn rằng MOC được sử dụng trên thread(queue) mà chúng được liên kết khi khởi tạo.
Nếu MOC không được sử dụng trên thread(queue) mà chúng được liên kết, trong trường hợp MOC liên kết với mainQueue nhưng được sử dụng trên background thread, hoặc ngược lại, sẽ khiến app đôi lúc sẽ gặp những lỗi crash lạ.
Vì vậy để chắc chắn MOC luôn được sử dụng trên thread mà MOC được liên kết, thì có thể sử dụng perform( _:) và performAndWait( _:) như sau:
perform( _:) và performAndWait( _:) sẽ tự động đưa đoạn code bên trong nó thực hiện trên queue mà context đó được khởi tạo -> Điều đó sẽ chắc chắn rằng context được sử dụng trên đúng queue.
perform(_:) sẽ thực hiện async hàm bên trong nó.
performAndWait(_:) sẽ thực hiện sync hàm bên trong nó -> Nó sẽ block thread hiện tại gọi đến hàm đó cho đến khi hàm bên trong thực hiện xong -> Không nên gọi trên main.
Debug Concurrency:
Để đảm bảo Context được chạy trên đúng luồng nó được liên kết khi khởi tạo, có thể bật debug CoreData Concurrency như sau:
Chọn Edit Scheme -> Run -> Thêm "-com.apple.CoreData.ConcurrencyDebug 1".
Khi bật debug này lên, nó sẽ dừng app của bạn lại tại nơi context bị dùng sai Thread.
Ví dụ:
Khởi tạo 1 context bằng private queue:
private(set) lazy var managedObjectContext: NSManagedObjectContext = {
let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator
return managedObjectContext
}()
Sử dụng 1 context trên main:
func createNewEntity() {
DispatchQueue.main.async {
let user = User(context: self.manager.managedObjectContext)
user.name = "Hoang Anh Tuan"
let account = Account(context: self.manager.managedObjectContext)
account.username = "sunlight"
account.password = "123"
user.account = account
account.user = user
do {
try self.manager.managedObjectContext.save()
} catch let error {
print("Save error: \(error.localizedDescription)")
}
}
}
Kết quả khi bật debug:
Có thể sử dụng perform( _:) / performAndWait( _:) để giải quyết tình huống này.
Kết luận:
Nên dùng background thread cho context để tránh block main thread.
Nên dùng các hàm perform( _:) và performAndWait( _:) để đảm bảo context được chạy trên đúng luồng.
Ngoài cách ở trên thì còn 1 vài cách như sử dụng child/parent context, nhưng sẽ không được đề cập ở bài viết này.
Về cơ bản, thì Cordova là framework phát triển các app iOS/Android (là chính) sử dụng html/js/css làm UI, và các bộ plugin làm cầu nối để call xuống source native của platform (iOS/Android)
Cordova bao gồm:
Bộ html/js/css làm UI.
Native webview engine làm bộ render hiển thị UI
Cordova framework chịu trách nhiệm cầu nối giữa function call js và funtion native.
Source code native làm plugin cùng các config và các pulic js method.
Cordova project struct
Bình thường đối với người làm Cordova thì chủ yếu họ sẽ focus vào tầng UI bằng html/js/css. Việc sử dụng các chức năng native của platform thì sẽ sử dụng các plugin được cung cấp sẵn. Vậy nên về cơ bản, một lập trình viên làm Cordova chỉ cần làm được html/js/css là đủ.
Tuy nhiên trong một số trường hợp, các plugin có sẵn không đảm bảo giải quyết được vấn đề bài toán, lúc này việc phát triển riêng một plugin thực hiện được logic của project và support được các platform là điều cần phải làm.
Trong một vài trường hợp khác, có thể dự án đã có sẵn source native, tuy nhiên cần chuyển sang Cordova để support multi platform và tận dụng source code native có sẵn.
-> Đo đó, hiểu biết về cách tạo một plugin để giải quyết nhu câu bài toán sẽ nảy sinh. Bài viết này sẽ tập trung vào việc
Làm thế nào để tạo plugin
Luồng xử lý từ js xuống native source của plugin như nào
Install plugin vào Cordova project
Build và test plugin trên iOS và Androd
Tạo plugin bằng plugman
Tạo Cordova project
Để tạo Cordova plugin sample thì trước tiên cần có một Cordova project để test việc add plugin và kiểm tra hoạt động của plugin trên từng platform.
Ở đây, plugin.xml là file config cho plugin, bao gồm các thông tin như tên của plugin, các file assets, resources. Define js-module như file js của plugin, define namespace của plugin, define các plugin phụ thuộc của plugin đang phát triển…
<clobbers target="cordova.plugins.GSTPlugin" /> đây là namespace phần js của plugin. Từ file js, call xuống method native của plugin thì sẽ sử dụng cordova.plugins.GSTPlugin.sampleMethod
<param name="android-package" value="cordova-plugin-gstplugin.GSTPlugin" /> đây là config package name của Android, cần đổi sang tên đúng => <param name="android-package" value="com.gst.gstplugin.GSTPlugin" />. Như vậy Cordova sẽ tạo ra file GSTPlugin.java trong thư mục com/gst/gstplugin.
Trong sample này, chúng ta sẽ sử dụng Swift làm ngôn ngữ code Native logic cho iOS platform chứ không dùng Objective-C, do đó phần platform iOS cần update.
Trong thẻ <platform name="ios> thêm tag <dependency id="cordova-plugin-add-swift-support" version="2.0.2"/>. Đây là plugin support việc import các file source Swift vào source Objective-C. Mà bản chất Cordova sẽ generate ra source Objective-C cho platform iOS.
Vì sử dụng Swift nên ta thay thế <source-file src="src/ios/GSTPlugin.m" /> bằng <source-file src="src/ios/GSTPlugin.swift" />. Và đổi tên file GSTPlugin.m sang GSTPlugin.swift
Tiếp theo, chỉnh sửa các file js và native tương ứng cho plugin.
File GSTPlugin.js
File này export các public method của plugin. Update file như dưới
var exec = require('cordova/exec');
exports.helloNative = function (arg0, success, error) {
exec(success, error, 'GSTPlugin', 'helloNative', [arg0]);
};
File này sẽ export method helloNative ra js và call method helloNative của native platform tương ứng.
File GSTPlugin.swift
File này chứa logic và implementation cho iOS platform. Chỉnh sửa file như dưới
@objc(GSTPlugin) class GSTPlugin : CDVPlugin {
@objc(helloNative:)
func helloNative(command: CDVInvokedUrlCommand) {
// If plugin result nil, then we should let app crash
var pluginResult: CDVPluginResult!
if let message = command.arguments.first as? String {
let returnMessage = "GSTPlugin hello \(message) from iOS"
pluginResult = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: returnMessage)
} else {
pluginResult = CDVPluginResult (status: CDVCommandStatus_ERROR, messageAs: "Expected one non-empty string argument.")
}
commandDelegate.send(pluginResult, callbackId: command.callbackId)
}
}