Using someone else's framework from GitHub for a UIButton Process effect.
Works fine when installed through latest XCode, but app crashes as soon as button starts animation, if installed through HockeyApp.
Here is the Animation Function in question:
private func startAnimating() {
isAnimating = true
views = []
for i in 0..<colors.count {
let view = UIView(frame: lineRect())
view.backgroundColor = colors[i]
views.append(view)
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
var count: Int = 0
while self.isAnimating {
if count == self.views.count {
count = 0
}
var next = false
dispatch_async(dispatch_get_main_queue(), {
UIView.animateWithDuration(self.duration, delay: 0, options: [], animations: { () -> Void in
if self.isAnimating {
if !self.views.isEmpty {
self.addSubview(self.views[count])
self.views[count].frame.origin = CGPoint(x: self.bounds.origin.x, y: 0)
self.views[count].frame.size.width = self.frame.width
}
}
}, completion: { (Bool) -> Void in
if self.isAnimating {
var lastIndex = count - 1
if lastIndex < 0 {
lastIndex = self.colors.count - 1
}
self.views[lastIndex].frame = self.lineRect()
self.views[lastIndex].removeFromSuperview()
}
next = true
})
})
// Let's wait until the current animation is done before moving forward
while !next {
}
count++
}
})
}
HockeyApp points to the first reference of 'views' inside the second Queue, which in this case is the empty check ( I've added that, but if not there it will point to the next reference ) :
UIView.animateWithDuration(self.duration, delay: 0, options: [], animations: { () -> Void in
if self.isAnimating {
if !self.views.isEmpty {
self.addSubview(self.views[count])
self.views[count].frame.origin = CGPoint(x: self.bounds.origin.x, y: 0)
self.views[count].frame.size.width = self.frame.width
}
}
Here is the key piece of the HockeyApp's crash report:
Date/Time: 2016-02-03T18:01:01Z
Launch Time: 2016-02-03T18:00:33Z
OS Version: iPhone OS 9.2.1 (13D15)
Report Version: 104
Exception Type: SIGTRAP
Exception Codes: #0 at 0x100099080
Crashed Thread: 0
Application Specific Information:
Selector name found in current argument registers: release
Thread 0 Crashed:
0 barscan1 0x0000000100099080 barscan1.ProcessView.((startAnimating in _126B4789AED4AC2C363037724C3D4FEF) (barscan1.ProcessView) -> () -> ()).(closure #1).(closure #1).(closure #1) (ProcessView.swift:78)
1 UIKit 0x0000000185eb8210 +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 616
2 UIKit 0x0000000185ecfc58 +[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:] + 104
3 barscan1 0x0000000100098d90 barscan1.ProcessView.((startAnimating in _126B4789AED4AC2C363037724C3D4FEF) (barscan1.ProcessView) -> () -> ()).(closure #1).(closure #1) (ProcessView.swift:94)
4 libdispatch.dylib 0x0000000180be1630 _dispatch_call_block_and_release + 20
5 libdispatch.dylib 0x0000000180be15f0 _dispatch_client_callout + 12
6 libdispatch.dylib 0x0000000180be6cf8 _dispatch_main_queue_callback_4CF + 1840
7 CoreFoundation 0x0000000181144bb0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8
8 CoreFoundation 0x0000000181142a18 __CFRunLoopRun + 1624
9 CoreFoundation 0x0000000181071680 CFRunLoopRunSpecific + 380
10 GraphicsServices 0x0000000182580088 GSEventRunModal + 176
11 UIKit 0x0000000185ee8d90 UIApplicationMain + 200
12 barscan1 0x00000001000aa32c main (AppDelegate.swift:15)
13 ??? 0x0000000180c128b8 0x0 + 0
Any help is greatly appreciated !
Thanks !
Turned out this is a problem with Apple's Swift Optimization - set it to NONE and now the problem is gone.
Related
I got three similar crash reports that I can't reproduce (all on iOS 14.4). The stracktrace says the following (I only pasted the part where my app is starting):
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
Thread 0 name:
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001c077d414 __pthread_kill + 8
1 libsystem_pthread.dylib 0x00000001de2d8b50 pthread_kill + 272 (pthread.c:1392)
2 libsystem_c.dylib 0x000000019bc5bb74 abort + 104 (abort.c:110)
3 libswiftCore.dylib 0x0000000196795f20 swift::fatalError(unsigned int, char const*, ...) + 60 (Errors.cpp:393)
4 libswiftCore.dylib 0x0000000196796078 swift::swift_abortRetainUnowned(void const*) + 36 (Errors.cpp:460)
5 libswiftCore.dylib 0x00000001967e5844 swift_unknownObjectUnownedLoadStrong + 76 (SwiftObject.mm:895)
6 SwiftUI 0x00000001992b0cdc ViewGraph.graphDelegate.getter + 16 (ViewGraph.swift:234)
7 SwiftUI 0x00000001997e4d58 closure #1 in GraphHost.init(data:) + 80
8 SwiftUI 0x00000001997e6550 partial apply for closure #1 in GraphHost.init(data:) + 40 (<compiler-generated>:0)
9 AttributeGraph 0x00000001bbcc9b88 AG::Graph::Context::call_update() + 76 (ag-closure.h:108)
10 AttributeGraph 0x00000001bbcca1a0 AG::Graph::call_update() + 56 (ag-graph.cc:176)
11 AttributeGraph 0x00000001bbccfd70 AG::Subgraph::update(unsigned int) + 92 (ag-graph.h:709)
12 SwiftUI 0x00000001997e1cdc GraphHost.runTransaction() + 172 (GraphHost.swift:491)
13 SwiftUI 0x00000001997e4e1c GraphHost.runTransaction(_:) + 92 (GraphHost.swift:471)
14 SwiftUI 0x00000001997e37a8 GraphHost.flushTransactions() + 176 (GraphHost.swift:459)
15 SwiftUI 0x00000001997e2c78 specialized GraphHost.asyncTransaction<A>(_:mutation:style:) + 252 (<compiler-generated>:0)
16 SwiftUI 0x00000001993bd2fc AttributeInvalidatingSubscriber.invalidateAttribute() + 236 (AttributeInvalidatingSubscriber.swift:89)
17 SwiftUI 0x00000001993bd1f8 AttributeInvalidatingSubscriber.receive(_:) + 100 (AttributeInvalidatingSubscriber.swift:53)
18 SwiftUI 0x00000001993bd914 protocol witness for Subscriber.receive(_:) in conformance AttributeInvalidatingSubscriber<A> + 24 (<compiler-generated>:0)
19 SwiftUI 0x000000019956ba34 SubscriptionLifetime.Connection.receive(_:) + 100 (SubscriptionLifetime.swift:195)
20 Combine 0x00000001a6e67900 ObservableObjectPublisher.Inner.send() + 136 (ObservableObject.swift:115)
21 Combine 0x00000001a6e670a8 ObservableObjectPublisher.send() + 632 (ObservableObject.swift:153)
22 Combine 0x00000001a6e4ffdc PublishedSubject.send(_:) + 136 (PublishedSubject.swift:82)
23 Combine 0x00000001a6e76994 specialized static Published.subscript.setter + 388 (Published.swift:0)
24 Combine 0x00000001a6e75f74 static Published.subscript.setter + 40 (<compiler-generated>:0)
25 MyApp 0x00000001005d1228 counter.set + 32 (Preferences.swift:0)
26 MyApp 0x00000001005d1228 Preferences.counter.modify + 120 (Preferences.swift:0)
27 MyApp 0x00000001005ca440 MyView.changeCounter(decrease:) + 344 (MyView.swift:367)
28 MyApp 0x00000001005cf110 0x100584000 + 307472
29 MyApp 0x00000001005e65d8 thunk for #escaping #callee_guaranteed () -> () + 20 (<compiler-generated>:0)
30 MyApp 0x00000001005a8828 closure #2 in MySheet.body.getter + 140 (MySheet.swift:0)
What is happening is, that I have a Sheet with a button and when clicking on it the sheet disappears and in the onDisappear the changeCounter method in the main View MyView is called to change the counter. The method changeCounter is passed to the Sheet from MyView when calling/opening the Sheet.
This is the .sheet method in MyView:
.sheet(item: $activeSheet) { item in
switch item {
case .MY_SHEET:
MySheet(changeCounter: {changeCounter(decrease: true)}, changeTimer, item: $activeSheet)
}
}
This is the (important part of the) sheet:
struct MySheet: View {
var changeCounter: () -> Void
var changeTimer: () -> Void
#Binding var item: ActiveSheet?
#State var dismissAction: (() -> Void)?
var body: some View {
GeometryReader { metrics in
VStack {
Button(action: {
self.dismissAction = changeCounter
self.item = nil
}, label: {
Text("change_counter")
})
Button(action: {
self.dismissAction = changeTimer
self.item = nil
}, label: {
Text("change_timer")
})
}.frame(width: metrics.size.width, height: metrics.size.height * 0.85)
}.onDisappear(perform: {
if self.dismissAction != nil {
self.dismissAction!()
}
})
}
}
Here is changeCounter with the preferences object:
struct MyView: View {
#EnvironmentObject var preferences: Preferences
var body: some View {...}
func changeCounter(decrease: Bool) {
if decrease {
preferences.counter -= COUNTER_INTERVAL
}
}
}
The Preferences is an ObservableObject with the counter variable:
class Preferences: ObservableObject {
let userDefaults: UserDefaults
init(_ userDefaults: UserDefaults) {
self.userDefaults = userDefaults
self.counter = 0
}
#Published var counter: Int {
didSet {
self.userDefaults.set(counter, forKey: "counter")
}
}
}
It changes a value in the userDefaults that are UserDefaults.standard.
Anyone has an idea how that crash can happen and in what situations? Because it only happened three times now on users devices and I can't reproduce it.
Let's analyze
Button(action: {
self.dismissAction = changeCounter 1)
self.item = nil 2)
}, label: {
Line 1) changes internal sheet state initiating update of sheet's view
Line 2) changes external state initiating close of sheet (and probably update of parent view).
It even sounds as two conflicting process (even if there are no dependent flows, but looking at your code second depends on result of first). So, this is very dangerous logic and should be avoided.
In general, as I wrote in comment, changing two states in one closure is always risky, so I would rewrite logic to have something like (sketch):
Button(action: {
self.result = changeCounter // one external binding !!
}, label: {
, ie. the one state change that initiates some external activity...
Possible workaround for your code (if for any reason you cannot change logic) is to separate changes of those states in time, like
Button(action: {
self.dismissAction = changeCounter // updates sheet
DispatchQueue.main.async { // or after some min delay
self.item = nil // closes sheet after (!) update
}
}, label: {
My healthkit swift code keeps crashing in the background in production, and I can't figure it out for the life of me. I am fairly new to Swift, so maybe I am making a fundamental error in my implementation.
Features of the crash:
It seems to happen only in production. (our internal test program contains 10 devices only (so maybe it is co-incidence that it doesn't get picked up there).
Occurs in iOS versions - 10,11,12,13
Occurs for a very small set of users (1.5% of active audience), but very frequently for these same users.
Please find the crash log from my Crashlytics account below.
Crashed: com.facebook.react.HKManagerQueue
0 stepapp 0x100f4f324 specialized HKManager.getTotal(_:typeStr:unit:options:completion:) + 4372984612 (<compiler-generated>:4372984612)
1 stepapp 0x100f52e04 HKManager._getTotals(_:completion:) + 397 (HKManager.swift:397)
2 stepapp 0x100f532ac #objc HKManager.getTotals(_:resolver:rejecter:) + 4373000876 (<compiler-generated>:4373000876)
3 CoreFoundation 0x1af698c20 __invoking___ + 144
4 CoreFoundation 0x1af568d30 -[NSInvocation invoke] + 300
5 CoreFoundation 0x1af569908 -[NSInvocation invokeWithTarget:] + 76
6 stepapp 0x101184e6c -[RCTModuleMethod invokeWithBridge:module:arguments:] + 241556
7 stepapp 0x101187248 facebook::react::invokeInner(RCTBridge*, RCTModuleData*, unsigned int, folly::dynamic const&) + 250736
8 stepapp 0x101186fac invocation function for block in facebook::react::RCTNativeModule::invoke(unsigned int, folly::dynamic&&, int) + 250068
9 libdispatch.dylib 0x1af35e610 _dispatch_call_block_and_release + 24
10 libdispatch.dylib 0x1af35f184 _dispatch_client_callout + 16
11 libdispatch.dylib 0x1af30b404 _dispatch_lane_serial_drain$VARIANT$mp + 608
12 libdispatch.dylib 0x1af30bdf8 _dispatch_lane_invoke$VARIANT$mp + 420
13 libdispatch.dylib 0x1af315314 _dispatch_workloop_worker_thread + 588
14 libsystem_pthread.dylib 0x1af3aeb88 _pthread_wqthread + 276
15 libsystem_pthread.dylib 0x1af3b1760 start_wqthread + 8
com.apple.main-thread
0 libsystem_kernel.dylib 0x19c960634 mach_msg_trap + 8
1 libsystem_kernel.dylib 0x19c95faa0 mach_msg + 72
2 CoreFoundation 0x19cb08288 __CFRunLoopServiceMachPort + 216
3 CoreFoundation 0x19cb033a8 __CFRunLoopRun + 1444
4 CoreFoundation 0x19cb02adc CFRunLoopRunSpecific + 464
5 GraphicsServices 0x1a6aa3328 GSEventRunModal + 104
6 UIKitCore 0x1a0c1063c UIApplicationMain + 1936
7 stepapp 0x100edf330 main + 14 (main.m:14)
8 libdyld.dylib 0x19c98c360 start + 4
com.apple.uikit.eventfetch-thread
0 libsystem_kernel.dylib 0x19c960634 mach_msg_trap + 8
1 libsystem_kernel.dylib 0x19c95faa0 mach_msg + 72
2 CoreFoundation 0x19cb08288 __CFRunLoopServiceMachPort + 216
3 CoreFoundation 0x19cb033a8 __CFRunLoopRun + 1444
4 CoreFoundation 0x19cb02adc CFRunLoopRunSpecific + 464
5 Foundation 0x19ce42784 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 228
6 Foundation 0x19ce42664 -[NSRunLoop(NSRunLoop) runUntilDate:] + 88
7 UIKitCore 0x1a0ca8e80 -[UIEventFetcher threadMain] + 152
8 Foundation 0x19cf7309c __NSThread__start__ + 848
9 libsystem_pthread.dylib 0x19c8a5d8c _pthread_start + 156
10 libsystem_pthread.dylib 0x19c8a976c thread_start + 8
I have attached my implementation below, which contains the line mentioned in the crash logs
func _getTotals(_ options: Dictionary<String, Any>, completion: #escaping (Dictionary<String, Double>?) -> Void) {
var stepsDone = false;
var distanceDone = false;
var caloriesDone = false;
let steps = HKQuantityType.quantityType(forIdentifier: .stepCount);
let distance = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning);
let calories = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned);
var results = Dictionary<String, Double>();
// 👇 THIS IS LINE 397 which is indicated in the crash report above
self.getTotal(steps!, typeStr: HKManager.STEP_TYPE_STR, unit: HKUnit.count(), options: options) { (totalSteps, error) in
stepsDone = true;
if (totalSteps != nil) {
results["steps"] = totalSteps;
}
if (stepsDone == true && distanceDone == true && caloriesDone == true) {
return completion(results);
}
}
self.getTotal(distance!, typeStr: HKManager.DISTANCE_TYPE_STR, unit: HKUnit.meter(), options: options) { (totalDistance, error) in
distanceDone = true;
if (totalDistance != nil) {
results["distance"] = totalDistance;
}
if (stepsDone == true && distanceDone == true && caloriesDone == true) {
return completion(results);
}
}
self.getTotal(calories!, typeStr: HKManager.CALORIES_TYPE_STR, unit: HKUnit.kilocalorie(), options: options) { (totalCalories, error) in
caloriesDone = true;
if (totalCalories != nil) {
results["calories"] = totalCalories;
}
if (stepsDone == true && distanceDone == true && caloriesDone == true) {
return completion(results);
}
}
}
I have also attached my implementation of the self.getTotal(...) function which is used in the above code. Point to note in this function, I switch to performing my HealthKit query in the background qos to ensure that these queries don't run on the main thread. I think it might be the cause for the crash.
func getTotal(_ type: HKQuantityType, typeStr: String, unit: HKUnit, options: Dictionary<String, Any>, completion: #escaping (Double?, Error?) -> Void) {
guard (self.healthStore != nil) else {
let error = NSError(domain: "Healthkit not initialized", code: 50, userInfo: [:]);
return completion(nil, error);
}
var start: Date;
if (options["startDate"] != nil) {
start = self.strToDate(dateStr: options["startDate"] as! String);
} else {
let date = Date()
let cal = Calendar(identifier: .gregorian)
let midnight = cal.startOfDay(for: date);
start = midnight;
}
var ignoreMin = false;
if (options["ignoreMin"] != nil) {
ignoreMin = options["ignoreMin"] as! Bool;
}
if (ignoreMin != true && start < self.minStartDate && self.minStartDate != nil) {
start = self.minStartDate;
}
var end: Date = Date();
if (options["endDate"] != nil) {
end = self.strToDate(dateStr: options["endDate"] as! String);
}
var sources = options["sources"] as? [String];
if (sources == nil || (sources?.capacity)! < 1) {
sources = ["com.apple.health."];
}
DispatchQueue.global(qos: .background).async { [weak self] in
// fetch sources
self?.getSources(sampleTypeStr: typeStr, sampleType: type, sources: sources!) { (s, error) in
if (s == nil || ((s?.capacity) ?? 0) < 1) {
return completion(0.0, nil);
}
let sourcePredicate = HKQuery.predicateForObjects(from: s!);
// todo: enter date patterns
let datePredicate = HKQuery.predicateForSamples(withStart: start, end: end, options: []);
// predicate = [NSPredicate predicateWithFormat:#"metadata.%K != YES", HKMetadataKeyWasUserEntered];
let manualPredicate = HKQuery.predicateForObjects(withMetadataKey: HKMetadataKeyWasUserEntered, operatorType: .notEqualTo, value: "YES");
let compound = NSCompoundPredicate(andPredicateWithSubpredicates: [
sourcePredicate,
datePredicate,
manualPredicate
]);
let statOptions = HKStatisticsOptions.cumulativeSum;
let query = HKStatisticsQuery.init(quantityType:type , quantitySamplePredicate: compound, options: statOptions, completionHandler: { (query, results, error) in
if (error != nil) {
return completion(nil, error);
}
var total = 0.0;
// handle if results came back as nil, or sum came back as nil
guard (results != nil && results?.sumQuantity() != nil) else {
return completion(total, nil);
}
total = results?.sumQuantity()?.doubleValue(for: unit) ?? 0.0;
return completion(total, nil);
});
// execute stats query for step counts by source
self?.healthStore?.execute(query);
}
}
}
I would really appreciate any form of help, or pointers. Thanks in advance.
The obvious issue is parallel write from several threads to dictionary object. In pseudo code:
results = [:]
getTotal() // start thread 1
getTotal() // start thread 2
getTotal() // start thread 3
thread 1: write results
thread 2: write results
thread 3: write results
Swift Dictionary is not thread-safe, parallel writes have to be synchronized.
In your code, simple change would be to move DispatchQueue async up, into _getTotals, and removing DispatchQueue from getTotal:
func _getTotals(_ options: Dictionary<String, Any>, completion: #escaping (Dictionary<String, Double>?) -> Void) {
DispatchQueue.global(qos: .background).async {
var results = Dictionary<String, Double>()
self.getTotal(...)
self.getTotal(...)
self.getTotal(...)
}
}
In this way everything runs inside single background thread; getTotal calls are invoked serially one after another.
But if you need to run getTotal in parallel, you have to synchronize access to results variable. This is commonly done with another call to DispatchQueue async with a predefined shared serial queue.
Below is my code
func getImageFrom(_ asset: PHAsset, completion: #escaping (UIImage) -> Void) {
var thumbnail = UIImage()
let imageManager = PHCachingImageManager()
let options = PHImageRequestOptions()
options.version = .current
let size = CGSize(width: 150.0, height: 150.0)
imageManager.requestImage(for: asset, targetSize: size, contentMode: .aspectFill,
options: nil) { (image, info) in
guard let img = image else { return }
thumbnail = img
}
completion(thumbnail)
}
I'm using this function to get an Image from PHAsset.
some time crash is reported at
thumbnail = img or at completion(thumbnail).
using this function in below code block
func getImageDataAndFindDupImages() {
var i = 0
var similarImages = [OSTuple<NSString, NSString>]()
similarImages = OSImageHashing.sharedInstance().similarImages(with: .high) { () -> OSTuple<NSString, NSData>? in
#if DEBUG
print(i)
#endif
var tuple = OSTuple<NSString, NSData>()
if self.tempArrAssets.count > 0 {
DispatchQueue.main.async {
self.lblNumberOfPhoto.text = R.string.localizable.scanning_photos_number("\(i+1)", "\(self.arrPhotoAssets.count)")
}
if !self.tempArrAssets.isEmpty {
let tempArrAsset = self.tempArrAssets.removeFirst()
self.getImageFrom(tempArrAsset) { (imageAsset) in
self.arrImageFromAsset.append(imageAsset)
if let imgData = imageAsset.pngData() {
tuple = (OSTuple<NSString, NSData>(first: NSString(string: "\(i)"), andSecond: imgData as NSData))
}
}
i += 1
return tuple
} else {
return nil
}
} else {
return nil
}
}
print("To make dup tuples:", Date().timeIntervalSince(self.date))
}
I'm using getImageFrom in getImageDataAndFindDupImages
This is a log for the crash
Crashed: com.apple.mobileslideshow.accessCallbacks
0 libobjc.A.dylib 0x18a874020 objc_retain + 16
1 Duplicate Cleaner 0x1001f0e80 closure #1 in DuplicatePhotoFinderVC.getImageDataAndFindDupImages() + 519 (DuplicatePhotoFinderVC.swift:519)
2 Duplicate Cleaner 0x1001f16a4 thunk for #escaping #callee_guaranteed () -> (#owned OSTuple<NSString, NSData>?) + 4338357924 (<compiler-generated>:4338357924)
3 CocoaImageHashing 0x1004f3514 -[OSSimilaritySearch similarImagesWithProvider:withHashDistanceThreshold:forImageStreamHandler:forResultHandler:] + 46 (OSSimilaritySearch.m:46)
4 CocoaImageHashing 0x1004f3924 -[OSSimilaritySearch similarImagesWithProvider:withHashDistanceThreshold:forImageStreamHandler:] + 95 (OSSimilaritySearch.m:95)
5 CocoaImageHashing 0x1004f28d0 -[OSImageHashing similarImagesWithProvider:withHashDistanceThreshold:forImageStreamHandler:] + 222 (OSImageHashing.m:222)
6 CocoaImageHashing 0x1004f27b0 -[OSImageHashing similarImagesWithHashingQuality:withHashDistanceThreshold:forImageStreamHandler:] + 201 (OSImageHashing.m:201)
7 CocoaImageHashing 0x1004f2734 -[OSImageHashing similarImagesWithHashingQuality:forImageStreamHandler:] + 190 (OSImageHashing.m:190)
8 Duplicate Cleaner 0x1001ee63c DuplicatePhotoFinderVC.getImageDataAndFindDupImages() + 316 (DuplicatePhotoFinderVC.swift:316)
9 Duplicate Cleaner 0x1001ee434 DuplicatePhotoFinderVC.fetchPhotosandCheckforDuplicate() + 146 (DuplicatePhotoFinderVC.swift:146)
10 Duplicate Cleaner 0x1001eea1c closure #1 in DuplicatePhotoFinderVC.checkForPermissionAndScanPhotos() + 162 (DuplicatePhotoFinderVC.swift:162)
11 Duplicate Cleaner 0x10021bdec thunk for #escaping #callee_guaranteed (#unowned PHAuthorizationStatus) -> () + 4338531820 (<compiler-generated>:4338531820)
12 Photos 0x195ff9098 __39+[PHPhotoLibrary requestAuthorization:]_block_invoke + 64
13 AssetsLibraryServices 0x19f048edc __79-[PLPrivacy _isPhotosAccessAllowedWithScope:forceHandler:accessAllowedHandler:]_block_invoke.14 + 520
14 AssetsLibraryServices 0x19f01486c __pl_dispatch_async_block_invoke + 36
15 libdispatch.dylib 0x18a7fe610 _dispatch_call_block_and_release + 24
16 libdispatch.dylib 0x18a7ff184 _dispatch_client_callout + 16
17 libdispatch.dylib 0x18a7ab404 _dispatch_lane_serial_drain$VARIANT$mp + 608
18 libdispatch.dylib 0x18a7abdf8 _dispatch_lane_invoke$VARIANT$mp + 420
19 libdispatch.dylib 0x18a7b5314 _dispatch_workloop_worker_thread + 588
20 libsystem_pthread.dylib 0x18a84eb88 _pthread_wqthread + 276
21 libsystem_pthread.dylib 0x18a851760 start_wqthread + 8
I'm not able to understand why I'm getting these crashes. If you have any knowledge please guide me.
You are passing nil to options parameter in the imageManager.requestImage(for:targetSize: contentMode:options:). If you pass nil as options, the method calls the completion block asyncrohonously. So there is a chance by the time your completion block gets called the method getImageFrom(: completion:) has exited and the local variable thumbnail has been destroyed(I am not talking about the UIImage object being destroyed but the variable which holds address of the UIImage itself). Then trying to set the thumbnail varibale which is no more on the stack, inside the block might be the reason there is a crash.
To solve your problem Create an instance of PHImageRequestOptions set its isSynchronous property to true like so.
let options = PHImageRequestOptions()
options.isSynchronous = true
Now pass this options as a parameter to the imageManager.requestImage(for:targetSize: contentMode:options:)
As a result your imageManager.requestImage(for:targetSize: contentMode:options:) will call the block synchronously meaning before the getImageFrom(: completion:) method exits and so the variable thumbnail is still alive on the stack to be used.
Hoping this should solve your problem.
PS: Take care you call the method getImageFrom(: completion:) in a background thread or your main thread will be frozen
I have an intermittent (rarely occurring) crash which, so far has only occurred in the wild with reports from AppStore. With the limited crash log, I have not been able to grok the issue.
I am trying to display an App Store Product page view.
Here is the code
The enclosing class includes the SKStoreProductViewControllerDelegate and
the delegate is set to self.
#objc func adTap(sender: UITapGestureRecognizer? = nil) -> Void {
if adAppID.isEmpty {return}
let paramDict = [SKStoreProductParameterITunesItemIdentifier: adAppID]
storeProductViewController.loadProduct(withParameters: paramDict, completionBlock: { (status: Bool, error: Error?) -> Void in
if status {
self.storeProductViewController.view.frame.origin.y = 0
let eventParams = ["AppID": self.adAppID]
self.present(self.storeProductViewController, animated: true, completion: nil)
}
else {
if let error = error {
print("Error: \(error.localizedDescription)")
}
}})
}
The error is triggered at the self.present:
#8 (null) in thunk for #escaping #callee_guaranteed (#unowned Bool, #guaranteed Error?) -> () ()
#7 0x100b68350 in closure #1 in ViewController.adTap(sender:)
#6 (null) in -[UIViewController presentViewController:animated:completion:] ()
#0 (null) in __exceptionPreprocess ()
Dumping the actual cashpoint file shows a SIGABRT:
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
Last Exception Backtrace:
0 CoreFoundation 0x1ad1db278 __exceptionPreprocess + 220 (NSException.m:199)
1 libobjc.A.dylib 0x1acf040a4 objc_exception_throw + 56 (objc-exception.mm:565)
2 UIKitCore 0x1b0bcbe0c -[UIViewController _presentViewController:withAnimationController:completion:] + 4880 (UIViewController.m:7185)
3 UIKitCore 0x1b0bce074 __63-[UIViewController _presentViewController:animated:completion:]_block_invoke + 104 (UIViewController.m:7671)
4 UIKitCore 0x1b0bce570 -[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 508 (UIViewController.m:7772)
5 UIKitCore 0x1b0bcdfc4 -[UIViewController _presentViewController:animated:completion:] + 196 (UIViewController.m:7678)
6 UIKitCore 0x1b0bce22c -[UIViewController presentViewController:animated:completion:] + 160 (UIViewController.m:7716)
7 MyAppName 0x100b68350 closure #1 in ViewController.adTap(sender:) + 1016 (ViewController.swift:991)
8 MyAppName 0x100b68580 thunk for #escaping #callee_guaranteed (#unowned Bool, #guaranteed Error?) -> () + 60 (<compiler-generated>:0)
9 StoreKit 0x1bba3c1e8 -[SKStoreProductViewController _loadDidFinishWithResult:error:] + 48 (SKStoreProductViewController.m:425)
10 StoreKit 0x1bba3e624 -[SKRemoteProductViewController loadDidFinishWithResult:error:] + 128 (SKRemoteProductViewController.m:61)
///////
I understand that the completion block is an implicitly escaping block, but I do not see how it does not fulfill the block spec (arguments, if indeed that is the issue). Again, this is an intermittent event. Never seen it on a device tethered to Xcode or on one of our untethered devices. Recently it has occurred in the field, once on a iPhone 7 running iOS 13, and once on a X running 12.3.1
/////
Any thoughts would be appreciated
Let me write this as answer, as I see now that my comment may be unclear. The block
if status {
self.storeProductViewController.view.frame.origin.y = 0
let eventParams = ["AppID": self.adAppID]
self.present(self.storeProductViewController, animated: true, completion: nil)
}
accesses some UI elements, yet it may not be running on main thread (since it's inside callback). So first thing I would suspect is that it's crashing if it happens to be called outside of main thread. Hence I would try this:
if status {
DispatchQueue.main.async {
self.storeProductViewController.view.frame.origin.y = 0
let eventParams = ["AppID": self.adAppID]
self.present(self.storeProductViewController, animated: true, completion: nil)
}
}
If it helps, maybe think of better MVC separation...
I have two operations fetching a value. I only care about the sum both values. And I don't care about the value at all if it takes too long.
So I thought it would be an easy task for GCD using groups. Unfortunately, the below code only works fine on iOS 9. Each time I have no matching calls of dispatch_group_enter()/dispatch_group_leave() I get a crash.
The documentation states clearly that I have to match both call. But when I use a timeout on dispatch_group_wait(), it is impossible to have the same amount of leave calls as enter calls; that is the whole point of specifying a timeout.
So is this a known bug in iOS 8? Am I doing something wrong? Is there another solution to my initial problem that works on iOS 8 as well?
EDIT: Actually we can boil it down to this:
var sync_group: dispatch_group_t = dispatch_group_create();
dispatch_group_enter(sync_group);
let maxWait = dispatch_time(DISPATCH_TIME_NOW, Int64(60 * NSEC_PER_SEC))
let result = dispatch_group_wait(sync_group, maxWait)
sync_group = dispatch_group_create();
Works as expected on iOS 9 but does crash on iOS 8 on the last line because the old dispatch_group_t instance can not be release. Any easy workarounds?
EDIT 2: Turns out it is broken on iOS 9.0 too. It only works as as it should in iOS 9.1+.
Original Code:
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
/* We want this method to block until the network code succeeded. */
let sync_group: dispatch_group_t = dispatch_group_create();
/* Start async operation 1. */
dispatch_group_enter(sync_group);
self.someAsyncOperation1({(value: Int, finalValue: Bool) in
if (finalValue) {
valOp1 = value
dispatch_group_leave(sync_group);
}
})
/* Start async operation 2. */
dispatch_group_enter(sync_group);
self.someAsyncOperation2({(value: Int, finalValue: Bool) in
if (finalValue) {
valOp2 = value
dispatch_group_leave(sync_group)
}
})
/* Block current thread until all leaves were called. If it takes more then 60 sec we don't care and let go. */
let maxWait = dispatch_time(DISPATCH_TIME_NOW, Int64(60 * NSEC_PER_SEC))
let result = dispatch_group_wait(sync_group, maxWait)
if (result > 0) {
/* This will result in a crash when we leave the scope: SIGTRAP in dispatch_semaphore_dispose */
return
}
dispatch_async(dispatch_get_main_queue(), {
let newValue = valOp1 + valOp2
self.lastKnownNotificationCombinedCounter = newValue
success(newValue)
})
})
The actual crash loops like that:
Exception Type: SIGTRAP
Exception Codes: #0 at 0x3958a2a4
Thread 2 Crashed:
0 libdispatch.dylib 0x3958a2a4 _dispatch_semaphore_dispose$VARIANT$mp + 48
1 libdispatch.dylib 0x3958b491 _dispatch_dispose$VARIANT$mp + 30
2 libdispatch.dylib 0x3957ea8f -[OS_dispatch_object _xref_dispose] + 44
3 myApp 0x00176a24 block_destroy_helper67 + 354
4 myApp 0x00176ab8 0x2e000 + 1346232
5 myApp 0x00178334 0x2e000 + 1352500
6 libsystem_blocks.dylib 0x395d3adb _Block_release + 216
7 Foundation 0x2c4143b9 -[NSBlockOperation dealloc] + 58
8 libobjc.A.dylib 0x39036d57 objc_object::sidetable_release(bool) + 164
9 libobjc.A.dylib 0x390371a9 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 402
10 libdispatch.dylib 0x39589423 _dispatch_root_queue_drain + 1176
11 libdispatch.dylib 0x3958a1fb _dispatch_worker_thread3 + 104
12 libsystem_pthread.dylib 0x396fae25 _pthread_wqthread + 666
13 libsystem_pthread.dylib 0x396fab78 start_wqthread + 6
I came up with this workaround:
private let MAX_TRIES = 20;
func dispatch_group_wait_ios8Safe(group: dispatch_group_t, _ timeout: dispatch_time_t) -> Int {
if #available(iOS 9, *) {
/* Just forward the call. */
return dispatch_group_wait(group, timeout)
} else {
/* Forward the call to original function and store result. */
let firstResult = dispatch_group_wait(group, timeout)
var result = firstResult, tries = 0
while(result > 0 && tries < MAX_TRIES) {
dispatch_group_leave(group)
result = dispatch_group_wait(group, DISPATCH_TIME_NOW)
tries += 1
}
/* Return original result. */
return firstResult
}
}
So until someone comes up with a better solution I stick with this.