I am using JavaScriptCore with multiple threads on iOS 9.3. One JSContext is shared by all the threads. The app crashes sometimes. Does anybody knows what is going on ?
func callFunction(function:String, date:NSDate, dataConfiguration:CDTimePeriodConfiguration, viz:CDViz, didFinish:((response:[String:AnyObject]?, error:NSError?) -> Void)?){
guard let jsonString = self.jsonString(self.apiOptions(date, dataConfiguration: dataConfiguration, viz: viz)) else{
let error = Error.errorWithCode(.ParametersMissing, failureReason: "Cannot create Rocket APIOptions")
didFinish?(response: nil, error: error)
return
}
let randomCallBackFunctionName = "callBackFromJS\(Int64(NSDate().timeIntervalSince1970 * 1000))\(random() % 100000)"
let callBackClosure: #convention(block) (response: [String:AnyObject]?, error: String?) -> Void = {
[weak self]
(response: [String:AnyObject]?, error: String?) -> Void in
var errorMessage:String? = error
if error == "null"{
errorMessage = nil
}
dispatch_async(dispatch_get_main_queue()) {
if let response = response where errorMessage == nil{
didFinish?(response: response, error: nil)
}
else{
if errorMessage == nil && response == nil{
errorMessage = "No reponse from Rocket"
}
let error = Error.errorWithCode(.RocketError, failureReason: errorMessage ?? "Rocket failed for unknown reason")
didFinish?(response: response, error: error)
self?.context.setObject(nil, forKeyedSubscript: randomCallBackFunctionName)
}
}
}
context.setObject(unsafeBitCast(callBackClosure, AnyObject.self), forKeyedSubscript: randomCallBackFunctionName)
let js = "var options = \(jsonString) ; Rocket.\(function)(options).then(function(res) { \(randomCallBackFunctionName)(res, null); }).catch(function(err) { \(randomCallBackFunctionName)(null, err); })"
self.context.evaluateScript(js)
}
0 JavaScriptCore 0x000000018533f3d0 JSC::JSLock::lock(long) + 28
1 JavaScriptCore 0x00000001850453f4 JSC::JSLockHolder::JSLockHolder(JSC::VM&) + 48
2 WebCore 0x0000000185e079a0 WebCore::JSGlobalObjectCallback::call() + 64
3 WebCore 0x0000000185aa3dd8 std::__1::__function::__func<WebCore::Document::postTask(WebCore::ScriptExecutionContext::Task)::$_0, std::__1::allocator<WebCore::Document::postTask(WebCore::ScriptExecutionContext::Task)::$_0>, void ()>::operator()() + 116
4 JavaScriptCore 0x0000000184f35540 WTF::callFunctionObject(void*) + 28
5 JavaScriptCore 0x0000000184f354c0 WTF::dispatchFunctionsFromMainThread() + 240
6 Foundation 0x00000001826a7e20 __NSThreadPerformPerform + 336
7 CoreFoundation 0x0000000181c9cefc __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 20
8 CoreFoundation 0x0000000181c9c990 __CFRunLoopDoSources0 + 536
9 CoreFoundation 0x0000000181c9a690 __CFRunLoopRun + 720
10 CoreFoundation 0x0000000181bc9680 CFRunLoopRunSpecific + 380
11 WebCore 0x0000000185779998 RunWebThread(void*) + 452
12 libsystem_pthread.dylib 0x000000018194fb28 _pthread_body + 152
13 libsystem_pthread.dylib 0x000000018194fa8c _pthread_start + 152
14 libsystem_pthread.dylib 0x000000018194d028 thread_start + 0
Try to call every JSContext related method and functions in one thread:
Create dispatch_queue:
my_queue = dispatch_queue_create("com.example.subsystem.taskXYZ", NULL);
Call methods and functions:
...
// Sync call
dispatch_sync(my_queue) {
...
}
// Async call
dispatch_async(my_queue) {
....
}
Related
I am getting a crash reported in crashlytics about partial apply in closure. I have tried other answers that mainly focus on using [weak self] but that didn't help. However, i am not getting a crash on my device.
According to the report the crash is somewhere in the function where i show my dropDown here is the code
func showDropDown(_ names: [String], forResult result: SearchPatientResultResponnse) {
guard let vc = viewController
else { return }
self.searchedPatientNames = result.data?.patientSearchSuggestions
Helpers.showSearchDropdown(anchorView: vc.searchBar.searchTextField, dataSource: names, scrollToTop: true, completion: { [weak self] (i, item) in
guard let self = self else {
return
}
vc.searchBar.text = item
vc.dismissKeyboard()
self.selectedSearchPatient = self.searchedPatientNames?[i]
if let d = self.searchedPatientNames?[i].dob {
vc.dobTF.text = DateTimeFormatters.userUIDateFormatterInUTC.string(from: d)
}
self.search(self.selectedSearchFilter)
}, fetchPaginationData: { [weak self] in
guard let self = self, !self.isNameSearchLastPage, self.page != -1 else {
return
}
vc.searchBar.isLoadingWithoutSearchIcon = true
self.fetchPatientNameNextPage()
}, cancel: { [weak self] in
self?.resetPaginationParams()
})
}
This is the function from where i am calling this showDropDown function
func getQueryPatientResponse() {
guard let vc = viewController else {
return
}
SocketIOManager.shared.queryResponseForPatient = { [weak self] result in
var names: [String] = []
self?.isNameSearchLastPage = result.data?.isLastPage ?? true
self?.page = result.data?.pageNumber ?? -1
for data in result.data?.patientSearchSuggestions ?? [] {
var name = ""
if !data.middleName.isEmpty {
name = data.firstName + " " + data.middleName + " " + data.lastName
} else {
name = data.firstName + " " + (data.lastName)
}
names.append(name)
}
vc.searchBar.isLoadingWithoutSearchIcon = false
self?.page == 1 ? self?.showDropDown(names, forResult: result) : self?.updateDropDown(names, forResult: result)
}
}
This is the crash report from crashlytics:-
Crashed: com.apple.main-thread
0 Ovada CM - PT 0x639b0 closure #1 in VisitStatusViewModel.showDropDown(_:forResult:)
+ 4340890032 (<compiler-generated>:4340890032)
1 Ovada CM - PT 0x3f328 partial apply for closure #1 in static Helpers.showSearchDropdown(anchorView:dataSource:scrollToTop:completion:fetchPaginationData:cancel:)
+ 121 (Helpers.swift:121)
2 Ovada CM - PT 0x15604c DropDown.tableView(_:didSelectRowAt:)
+ 4341882956 (<compiler-generated>:4341882956)
3 Ovada CM - PT 0x156548 #objc DropDown.tableView(_:didSelectRowAt:)
+ 4341884232 (<compiler-generated>:4341884232)
4 UIKitCore 0xfe36d0 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:deselectPrevious:]
+ 1640
5 UIKitCore 0xfe3050 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:]
+ 112
6 UIKitCore 0xfe394c -[UITableView _userSelectRowAtPendingSelectionIndexPath:]
+ 316
7 UIKitCore 0x30c240 -[_UIAfterCACommitBlock run] + 64
8 UIKitCore 0x227770 -[_UIAfterCACommitQueue flush] + 200
9 UIKitCore 0x155eb0 _runAfterCACommitDeferredBlocks + 640
10 UIKitCore 0x15657c _cleanUpAfterCAFlushAndRunDeferredBlocks + 128
11 UIKitCore 0x156748 _afterCACommitHandler + 56
12 CoreFoundation 0x3e83c _CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION_ + 32
13 CoreFoundation 0xfa74 __CFRunLoopDoObservers + 616
14 CoreFoundation 0xaffc __CFRunLoopRun + 1012
15 CoreFoundation 0x1e250 CFRunLoopRunSpecific + 572
16 GraphicsServices 0x1988 GSEventRunModal + 160
17 UIKitCore 0x4e5a94 -[UIApplication _run] + 1080
18 UIKitCore 0x27efd4 UIApplicationMain + 336
19 Ovada CM - PT 0x6070 main + 14 (AppDelegate.swift:14)
20 ??? 0x1029dc4d0 (Missing)
Exception Type: EXC_BAD_ACCESS
Exception Subtype: KERN_INVALID_ADDRESS 0x00000003d74249bd
Release Type: User
Realm framework version: 10.5.1
Xcode version: 12.3
iOS/OSX version: 14.3
-->
Crash on transaction
Crashed: com.apple.root.background-qos
0 Realm 0x1058abfb8 realm::Allocator::translate_less_critical(realm::Allocator::RefTranslation*, unsigned long) const + 68
1 Realm 0x1057fce30 realm::Array::init_from_ref(unsigned long) + 192
2 Realm 0x105ca356c realm::_impl::GroupFriend::get_history_ref(realm::Allocator&, unsigned long) + 68
3 Realm 0x105c3ba60 bool realm::Transaction::internal_advance_read<(anonymous namespace)::TransactLogValidator>((anonymous namespace)::TransactLogValidator*, realm::VersionID, realm::_impl::History&, bool) + 176
4 Realm 0x105c36400 realm::_impl::transaction::begin(std::__1::shared_ptrrealm::Transaction const&, realm::BindingContext*, realm::_impl::NotifierPackage&) + 416
5 Realm 0x105c2b77c realm::_impl::RealmCoordinator::promote_to_write(realm::Realm&) + 268
6 Realm 0x105ca1570 realm::Realm::begin_transaction() + 404
7 Realm 0x10588ee24 -[RLMRealm beginWriteTransactionWithError:] + 24
8 Inventa 0x10510b0a0 $s7Inventa15RealmDataHelperC33saveDataAA0I0CG_tFyycfU_Tf2i_n + 52
9 Inventa 0x1050c2c98 $sIeg_IeyB_TR + 20
10 libdispatch.dylib 0x19941c24c _dispatch_call_block_and_release + 32
11 libdispatch.dylib 0x19941ddb0 _dispatch_client_callout + 20
12 libdispatch.dylib 0x19942ea68 _dispatch_root_queue_drain + 656
13 libdispatch.dylib 0x19942f120 _dispatch_worker_thread2 + 116
14 libsystem_pthread.dylib 0x1e52d97d8 _pthread_wqthread + 216
15 libsystem_pthread.dylib 0x1e52e076c start_wqthread + 8
func Inventa15RealmDataHelperC33saveData(notifications: [Notifications]) {
Inventa23.shared56.realmHelper.removeStaleDistinctNotifications654(notificationsofZone: "ezone")
var filtered:[Notifications] = []
for notif in notifications {
for stId in notif.st_ids {
notif.stId = stId
notif.notif_key = "\(notif.ntId)-\(notif.stId))"
filtered.append(notif)
}
}
let backgroundQueue = DispatchQueue.global(qos: .background)
backgroundQueue.async(execute: {
do{
let realm = try Realm()
realm.beginWrite()
realm.add(filtered, update: .all)
try realm.commitWrite()
}
catch{
print(error)
}
})
}
internal func removeStaleDistinctNotifications654(notificationsofZone: String) {
let notificationsInDB = realm?.objects(Notifications.self)
let onlyGeoNotifsInDB = notificationsInDB?.filter({$0.notification_identifier == notificationsofZone})
if let toBeDeletedGeoNotifs = onlyGeoNotifsInDB?.filter({$0.createdAtTimeStamp < Inventa.shared.insertedTimeStamp}) {
if toBeDeletedGeoNotifs.count > 0 {
do {
try realm?.write {
realm?.delete(toBeDeletedGeoNotifs)
}
} catch {
print(error)
}
}
}
}
I have the user already Signed in Anonymously in my app through the following code:
Auth.auth().signInAnonymously { (result, error) in
if let error = error {
Auth.auth().handleFireAuthError(error: error, vc: self)
debugPrint(error)
}
}
Then at one point I require the user to sign up and I am using the following code to link the Anonymous account to a permanent account:
guard let email = userEmailTextField.text, email.isNotEmpty,
let password = userPasswordTextField.text, password.isNotEmpty else {
simpleAlert(title: "Error", msg: "Please fill out all fields.")
return
}
guard let confirmPass = userRepeatPasswordTextField.text , confirmPass == password else{
simpleAlert(title: "Error", msg: "Passwords do not match.")
return
}
guard let authUser = Auth.auth().currentUser else {
return
}
let credential = EmailAuthProvider.credential(withEmail: email, link: password)
authUser.link(with: credential) { (result, error) in
if let error = error {
debugPrint(error)
Auth.auth().handleFireAuthError(error: error, vc: self)
self.activityIndicator.stopAnimating()
return
}
guard let fireUser = result?.user else {return}
let artUser = User.init(id: fireUser.uid, email: email , stripeId:"")
//Upload to Firestore
self.createFirestoreUser(user: artUser)
}
But I get an error here. I put break point and this is where the execution stop authUser.link(with: credential) { (result, error) in
This is the error I am getting:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[NSURLComponents initWithString:]: nil URLString parameter'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff23e39f0e __exceptionPreprocess + 350
1 libobjc.A.dylib 0x00007fff50ad79b2 objc_exception_throw + 48
2 Foundation 0x00007fff259659ca -[__NSConcreteURLComponents URL] + 0
3 Foundation 0x00007fff259673a8 +[NSURLComponents componentsWithString:] + 33
4 Silobee 0x000000010b144c52 +[FIRAuthWebUtils parseURL:] + 98
5 Silobee 0x000000010b16c638 __56-[FIRUser linkAndRetrieveDataWithCredential:completion:]_block_invoke_3 + 440
6 Silobee 0x000000010b16b084 __51-[FIRUser internalGetTokenForcingRefresh:callback:]_block_invoke + 340
7 Silobee 0x000000010b15df8c __65-[FIRSecureTokenService fetchAccessTokenForcingRefresh:callback:]_block_invoke + 156
8 Silobee 0x000000010b13fa7b __38-[FIRAuthSerialTaskQueue enqueueTask:]_block_invoke + 155
9 libdispatch.dylib 0x0000000110850f11 _dispatch_call_block_and_release + 12
10 libdispatch.dylib 0x0000000110851e8e _dispatch_client_callout + 8
11 libdispatch.dylib 0x00000001108586fd _dispatch_lane_serial_drain + 788
12 libdispatch.dylib 0x00000001108592c5 _dispatch_lane_invoke + 476
13 libdispatch.dylib 0x000000011085851c _dispatch_lane_serial_drain + 307
14 libdispatch.dylib 0x000000011085928f _dispatch_lane_invoke + 422
15 libdispatch.dylib 0x0000000110864b65 _dispatch_workloop_worker_thread + 719
16 libsystem_pthread.dylib 0x00007fff51b37a3d _pthread_wqthread + 290
17 libsystem_pthread.dylib 0x00007fff51b36b77 start_wqthread + 15
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Just saw where my problem is! Instead of this:
let credential = EmailAuthProvider.credential(withEmail: email, link: password)
I had to write this:
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
One small details and stoped my code from working
I'm getting a random crash on my live app on Float to Int conversion line.
I can't figure out why it's crashing and can't reproduce it as it's random crash on live app. Any suggestion on how to fix crash?
Crash log
Crashed: com.apple.main-thread
0 MyApp 0x100703778 closure #1 in MyViewController.getBalance(isRefreshed:) + 797 (MyViewController.swift:797)
1 MyApp 0x1005c7670 closure #1 in closure #1 in fetchtBalance(completed:) + 185 (Utils.swift:185)
2 MyApp 0x10051ab10 thunk for #escaping #callee_guaranteed () -> () + 4335790864 (<compiler-generated>:4335790864)
3 libdispatch.dylib 0x1ae908b7c _dispatch_call_block_and_release + 32
4 libdispatch.dylib 0x1ae909fd8 _dispatch_client_callout + 20
5 libdispatch.dylib 0x1ae915cc8 _dispatch_main_queue_callback_4CF + 968
6 CoreFoundation 0x1aebdee0c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
7 CoreFoundation 0x1aebd9b68 __CFRunLoopRun + 1980
8 CoreFoundation 0x1aebd9084 CFRunLoopRunSpecific + 480
9 GraphicsServices 0x1b8e27534 GSEventRunModal + 108
10 UIKitCore 0x1b2d49670 UIApplicationMain + 1940
11 MyApp 0x1004ec6b8 main + 20 (ClientMyProfileViewController.swift:20)
12 libdyld.dylib 0x1aea58e18 start + 4
Code
import UIKit
var balance: String!
var cost: String?
func getBalance () {
fetchBalance() { (result, error) in
guard let balanceFloatValue = Float(balance!) else {
print("Error")
return
}
guard let costFloat = Float(cost!) else {
print("Error")
return
}
let costPerSec = costFloat / 60
let talkTime = balanceFloatValue / costPerSec
let talkTimeInt = Int(talkTime) // Line No. 797 Crash here
// ....
}
}
The most likely cause for this error is the values themselves
let a:Float = 9999999999999999999999.9
let b = "0.000000000000000000001"
let c:Float = a/Float(b)!
let x = Int(c) // <- Crash here
0/0 will make you crash in: let c:Float = a/Float(b)! so thats not it, but anything else that can make talkTime outside of a Int will make you crash
This would also explain why you only encounter the error randomly since its only for values that make that division NaN or inf or something outside the int like bg2b said
I think you are force unwrapping the balance value. Try to unwrap the converted value like: „Float(balance)!“
I am doing a registration page for me iOS app. I know that my server side code is fine. When the user has successfully signed in the alert that it triggers crashes my app. If i remove the alert then my app is fine (I know this because
i use the console to print out 'success'). Please can someone advise on why my alert causes this crash?
.....
let task = session.dataTask(with: request) { (data, response, error) in
// When request is complete, run code below
if let jsonData = data{
do{
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
print("jsonObject: \(jsonObject)")
guard
let myArray = jsonObject as? [String:Any] else{
print("error-2")
return
}
if let status = myArray["status"] as? Int{
// Communication with server successful
if(status == 200){
// Registration successful
if let message = myArray["message"] as? String{
self.displayAlertMessage(msg: message)
}
}
}
}catch let error{
print("print error: \(error)")
}
}else if let requestError = error{
print("error detail: \(requestError)")
}else{
print("unexpected error")
}
}
task.resume()
My alert function
// General Alert message function
func displayAlertMessage(msg: String){
let alert = UIAlertController.init(title: "Alert", message: msg, preferredStyle: .alert)
let userAction = UIAlertAction.init(title: "OK", style: .destructive, handler: nil)
alert.addAction(userAction)
present(alert, animated: true, completion: nil)
}
CONSOLE:
[MobileAssetError:29] Unable to copy asset information from https://mesu.apple.com/assets/ for asset type com.apple.MobileAsset.TextInput.SpellChecker
url: https://www.myDomaincom/myFolder/myphpFile.php?userEmail=e#gm.com&firstname=Tim&lastname=t&userPassword=bab
jsonObject: {
email = "e#gm.com";
firstname = Tim;
lastname = t;
message = "Registration success";
status = 200;
userId = 32;
}
2017-04-29 12:33:57.042 lmyApp[60847:11563379] *** Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.6.21/Keyboard/UIKeyboardTaskQueue.m:432
2017-04-29 12:33:57.057 myApp[60847:11563379] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
*** First throw call stack:
(
0 CoreFoundation 0x0000000109891d4b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000106ba421e objc_exception_throw + 48
2 CoreFoundation 0x0000000109895e42 +[NSException raise:format:arguments:] + 98
3 Foundation 0x000000010673966d -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
4 UIKit 0x0000000107bc4b65 -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] + 165
5 UIKit 0x0000000107335441 -[UIKeyboardImpl setDelegate:force:] + 1404
6 UIKit 0x0000000107755dde -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:] + 981
7 UIKit 0x000000010775f5f8 -[UIPeripheralHost(UIKitInternal) _preserveInputViewsWithId:animated:reset:] + 498
8 UIKit 0x0000000107227543 -[UIViewController _presentViewController:modalSourceViewController:presentationController:animationController:interactionController:completion:] + 1178
9 UIKit 0x000000010722928e -[UIViewController _presentViewController:withAnimationController:completion:] + 4971
10 UIKit 0x000000010722c26b -[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 530
11 UIKit 0x000000010722bd51 -[UIViewController presentViewController:animated:completion:] + 179
12 lifesci-PubMed 0x00000001065b3fd2 _TFC14lifesci_PubMed22RegisterViewController19displayAlertMessagefT3msgSS_T_ + 834
13 lifesci-PubMed 0x00000001065b3378 _TFFC14lifesci_PubMed22RegisterViewController24submitRegistrationTappedFP_T_U_FTGSqV10Foundation4Data_GSqCSo11URLResponse_GSqPs5Error___T_ + 2088
14 lifesci-PubMed 0x00000001065b3bcb _TTRXFo_oGSqV10Foundation4Data_oGSqCSo11URLResponse_oGSqPs5Error____XFdCb_dGSqCSo6NSData_dGSqS1__dGSqCSo7NSError___ + 203
15 CFNetwork 0x0000000109cafccc __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19
16 CFNetwork 0x0000000109caf578 __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 308
17 Foundation 0x00000001066a69ad __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
18 Foundation 0x00000001066a668f -[NSBlockOperation main] + 101
19 Foundation 0x00000001066a4d8c -[__NSOperationInternal _start:] + 672
20 Foundation 0x00000001066a0ccf __NSOQSchedule_f + 201
21 libdispatch.dylib 0x000000010a7f50cd _dispatch_client_callout + 8
22 libdispatch.dylib 0x000000010a7d2e17 _dispatch_queue_serial_drain + 236
23 libdispatch.dylib 0x000000010a7d3b4b _dispatch_queue_invoke + 1073
24 libdispatch.dylib 0x000000010a7d6385 _dispatch_root_queue_drain + 720
25 libdispatch.dylib 0x000000010a7d6059 _dispatch_worker_thread3 + 123
26 libsystem_pthread.dylib 0x000000010aba4712 _pthread_wqthread + 1299
27 libsystem_pthread.dylib 0x000000010aba41ed start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Read the error reason, it implies the solution:
... may only be called from the main thread
DispatchQueue.main.async {
self.displayAlertMessage(msg: message)
}
[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.
Ans : thread that was not the main thread (from a callback in a network request). The solution is to dispatch your calls to present the view controllers to the main thread.present your alert in main thread,
DispatchQueue.main.async(execute: {() -> Void in
//Code that presents or dismisses a view controller here
self.present(alert, animated: true, completion: nil)
})
or else update your view in main thread in here
if let message = myArray["message"] as? String{
DispatchQueue.main.async(execute: {() -> Void in
//Code that presents or dismisses a view controller here
self.displayAlertMessage(msg: message)
})
}
First of all, you should display your alert message on main thread.
Also, it might be useful to capture a weak reference to your controller.
final class SomeController: UIViewController {
func sendRequest() {
let message = "your message"
DispatchQueue.main.async { [weak self] in
self?.displayAlertMessage(msg: message)
}
}
func displayAlertMessage(msg: String){
// present
}
}