I'm writing a project in Combine and SwiftUI with backward compatibility for iOS 12/11 with RxSwift, RxCocoa and UIKit. My project is based on a lot of protocols, associated types and generics. When I'm running the project on iOS 13 it works fine but on iOS 12 the app suddenly crash.
I'm trying a Redux architecture with some changes. I have reducers, actions (as enums), stores and store providers (a store provider is intended to instantiate two stores: rxstore and a combinestore . (i know it's not the best way to do that but I would like to understand why the bug occurs).
Also I have a so called ReduxArchiver
and a ReduxArchiverElement
. This one (ReduxArchiver
) is like a listener or a delegate intended to handle and save redux store changes. (also I know it's not the best solution). When a store have a new state the ReduxArchiver
handle the change, modify data as needed and decide to save or not the state on disk. Archiver is also intended to load app states from disk when a store is instantiated.
The error is Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)
and appears when I'm instantiating an object of ReduxArchiver
type. It happens because of the instantiation of ReduxArchiverElement<T: Codable>
array (see the code below or here: https://github.com/mariusjcb/CrossReduxSOA )
Sometimes it appears in console:
libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not supported
on this platform.
I don't know if it's my fault or just a swift bug because of too many associated types/generics in the project.
I have both combine and swiftui linked as weak frameworks so there is no problem with missing SwiftUI/Combine.
Also my frameworks and libraries are embedded in target settings as Embed & Sign
.
The actual error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)
When I'm doing po $arg1:
// On #0 0x0000000108f6a93f in swift_getWitnessTable ():
(lldb) po $arg1
<nil>
// On #16 0x0000000108f6b308 in swift_getAssociatedTypeWitness ():
(lldb) po $arg1
error: Couldn't materialize: couldn't read the value of register rdi
error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression
When I'm examinating the $rdi:
//Xcode: 11.2
(lldb) register read
General Purpose Registers:
rbx = 0x000000010ff7bdb8 libswiftCore.dylib`InitialAllocationPool + 4304
rbp = 0x00007ffee39a3990
rsp = 0x00007ffee39a3960
r12 = 0x000000010cc69448 Models`protocol requirements base descriptor for Redux.Reducer
r13 = 0x00000000000000ff
r14 = 0x000000010cc69478 Models`associated type descriptor for StateType
r15 = 0x000000010d1ba718 type metadata for Reducers.ExampleReducer
rip = 0x000000010fea2308 libswiftCore.dylib`swift_getAssociatedTypeWitness + 152
13 registers were unavailable.
//Xcode 11.1
(lldb) register read --all
General Purpose Registers:
rax = 0x0000000000000000
rbx = 0x0000000105d1db74 Models`protocol conformance descriptor for Models.ExampleModel : Swift.Identifiable in Models + 16
rcx = 0x0000000000000000
rdx = 0x0000000000030000
rdi = 0x0000000000000000
rsi = 0x0000000105d1db78 Models`protocol conformance descriptor for Models.ExampleModel : Swift.Identifiable in Models + 20
rbp = 0x00007ffeea8fa960
rsp = 0x00007ffeea8fa8d0
r8 = 0x0000000000000000
r9 = 0x0000000105d1db50 Models`protocol conformance descriptor for Models.ExampleModel : Swift.Encodable in Models + 20
r10 = 0x0000000108fc10a4 libswiftCore.dylib`method descriptor for Swift.Encodable.encode(to: Swift.Encoder) throws -> ()
r11 = 0x0000000000000001
r12 = 0x0000000105d9c3b0 Models`__unnamed_24
r13 = 0x0000000000000000
r14 = 0x0000000105d1db74 Models`protocol conformance descriptor for Models.ExampleModel : Swift.Identifiable in Models + 16
r15 = 0x0000000105d1db64 Models`protocol conformance descriptor for Models.ExampleModel : Swift.Identifiable in Models
rip = 0x0000000108f6a93f libswiftCore.dylib`swift_getWitnessTable + 447
rflags = 0x0000000000000247
cs = 0x000000000000002b
fs = 0x0000000000000000
Instantiation of the object:
// In AppDelegate (as property):
// ...
let state = ExampleStoreBuilder.build()
// The Build Method from ExampleStoreBuilder
let archiveListeners = [GenericReduxArchiverLogger("example_archiver")]
var listeners: [ReduceStoreOutputDelegate] = [GenericReduxStoreLogger("example_logger")]
ERROR>> let defaultStoreArchiver = ExampleArchiver<ExampleRxStore>(outputDelegates: archiveListeners) <<<< Error: EXC_BAD_ACCESS
listeners.append(defaultStoreArchiver)
listeners.append(contentsOf: archiveListeners)
if #available(iOS 13.0, *) {
listeners.append(ExampleArchiver<ExampleCombineStore>(outputDelegates: archiveListeners))
}
let reducer = ExampleReduceBuilder.build()
let initialState = defaultStoreArchiver.statesHistory.last?.state ?? .emptyState
return ReduxState<ExampleStoreProvider<ExampleReducer>>(initialState,
reducedBy: reducer,
outputDelegates: listeners)
The EXC_BAD_ACCESS from let defaultStoreArchiver is located in ExampleArchiver on the line:
public var statesHistory = [ReduxArchiveElement<StoreType.ReducerType.StateType>]()
ReduxArchiverElement is defined as:
public struct ReduxArchiveElement<T: Codable>: Codable {
public let date: Date
public let state: T
public init(date: Date, state: T) {
self.date = date
self.state = state
}
}
finally the StoreType and Reducer are defined as:
// ReduceStoreInitializable protocol
public protocol ReduceStoreInitializable {
associatedtype ReducerType: Reducer
...
// Store:
public protocol ReduceStore: class, ReduceStoreInitializable {
var reducer: ReducerType! { get set }
...
// Reducer:
public protocol Reducer: class {
associatedtype ActionType
associatedtype ItemType: Codable
associatedtype StateType: Codable
associatedtype ErrorType: Error
About the actual error the stack looks like:
Thread 1 Queue : com.apple.main-thread (serial)
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
frame #0: 0x00000001063746e4 libswiftCore.dylib`swift_getWitnessTable + 392
frame #1: 0x0000000105802e38 Models`lazy protocol witness table accessor for type ExampleModel and conformance ExampleModel at <compiler-generated>:0
frame #2: 0x0000000105802cf4 Models`instantiation function for generic protocol witness table for ExampleModel at <compiler-generated>:0
frame #3: 0x0000000106374c08 libswiftCore.dylib`swift_getWitnessTable + 1708
frame #4: 0x0000000106385ee8 libswiftCore.dylib`swift::TargetProtocolConformanceDescriptor<swift::InProcess>::getWitnessTable(swift::TargetMetadata<swift::InProcess> const*) const + 496
frame #5: 0x0000000105112fd8 ExampleApp-SwiftUI`swift::swift50override_conformsToProtocol(swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolDescriptor<swift::InProcess> const*, swift::TargetWitnessTable<swift::InProcess> const* (*)(swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolDescriptor<swift::InProcess> const*)) + 60
frame #6: 0x00000001063607fc libswiftCore.dylib`swift::_conformsToProtocol(swift::OpaqueValue const*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolDescriptorRef<swift::InProcess>, swift::TargetWitnessTable<swift::InProcess> const**) + 44
frame #7: 0x00000001063863b0 libswiftCore.dylib`swift::_checkGenericRequirements(llvm::ArrayRef<swift::TargetGenericRequirementDescriptor<swift::InProcess> >, std::__1::vector<void const*, std::__1::allocator<void const*> >&, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 1160
frame #8: 0x0000000106383410 libswiftCore.dylib`(anonymous namespace)::DecodedMetadataBuilder::createBoundGenericType(swift::TargetContextDescriptor<swift::InProcess> const*, llvm::ArrayRef<swift::TargetMetadata<swift::InProcess> const*>, swift::TargetMetadata<swift::InProcess> const*) const + 576
frame #9: 0x0000000106382ac4 libswiftCore.dylib`swift::Demangle::TypeDecoder<(anonymous namespace)::DecodedMetadataBuilder>::decodeMangledType(swift::Demangle::Node* const&) + 2832
frame #10: 0x0000000106382100 libswiftCore.dylib`swift::Demangle::TypeDecoder<(anonymous namespace)::DecodedMetadataBuilder>::decodeMangledType(swift::Demangle::Node* const&) + 332
frame #11: 0x00000001063819e8 libswiftCore.dylib`swift_getTypeByMangledNodeImpl(swift::MetadataRequest, swift::Demangle::Demangler&, swift::Demangle::Node*, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 460
frame #12: 0x00000001063817a8 libswiftCore.dylib`swift::swift_getTypeByMangledNode(swift::MetadataRequest, swift::Demangle::Demangler&, swift::Demangle::Node*, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 520
frame #13: 0x0000000106381d18 libswiftCore.dylib`swift_getTypeByMangledNameImpl(swift::MetadataRequest, llvm::StringRef, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 648
frame #14: 0x0000000106380110 libswiftCore.dylib`swift::swift_getTypeByMangledName(swift::MetadataRequest, llvm::StringRef, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 520
frame #15: 0x0000000106376a6c libswiftCore.dylib`swift_getAssociatedTypeWitnessSlowImpl(swift::MetadataRequest, swift::TargetWitnessTable<swift::InProcess>*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*) + 492
frame #16: 0x0000000106375018 libswiftCore.dylib`swift_getAssociatedTypeWitness + 176
frame #17: 0x00000001056df53c Stores`ExampleArchiver.init(storeLocation=nil, outputDelegates=1 value, self=0x00000001c0463840) at ExampleArchiver.swift:18:85
frame #18: 0x00000001056df38c Stores`ExampleArchiver.__allocating_init(storeLocation:outputDelegates:) at ExampleArchiver.swift:0
frame #19: 0x00000001056ebc48 Stores`static ExampleStoreBuilder.build(self=Stores.ExampleStoreBuilder) at ExampleStoreBuilder.swift:23:64
frame #20: 0x0000000105000d20 ExampleApp-SwiftUI`AppDelegate.init() at AppDelegate.swift:17:36
frame #21: 0x0000000105000e28 ExampleApp-SwiftUI`@objc AppDelegate.init() at <compiler-generated>:0
frame #22: 0x000000018e4a8a00 UIKit`_UIApplicationMainPreparations + 1688
frame #23: 0x000000018e39d724 UIKit`UIApplicationMain + 184
frame #24: 0x0000000105001404 ExampleApp-SwiftUI`main at AppDelegate.swift:14:7
* frame #25: 0x0000000183e11fc0 libdyld.dylib`start + 4
for #16 0x0000000106375018 in swift_getAssociatedTypeWitness () the actual assembly looks like:
libswiftCore.dylib`swift_getAssociatedTypeWitness:
0x106374f68 <+0>: stp x24, x23, [sp, #-0x40]!
0x106374f6c <+4>: stp x22, x21, [sp, #0x10]
0x106374f70 <+8>: stp x20, x19, [sp, #0x20]
0x106374f74 <+12>: stp x29, x30, [sp, #0x30]
0x106374f78 <+16>: add x29, sp, #0x30 ; =0x30
0x106374f7c <+20>: mov x23, x0
0x106374f80 <+24>: sub x8, x4, x3
0x106374f84 <+28>: lsr x8, x8, #3
0x106374f88 <+32>: ldr x0, [x1, w8, uxtw #3]
0x106374f8c <+36>: tbnz w0, #0x0, 0x106374fa8 ; <+64>
0x106374f90 <+40>: mov x1, #0x0
0x106374f94 <+44>: ldp x29, x30, [sp, #0x30]
0x106374f98 <+48>: ldp x20, x19, [sp, #0x20]
0x106374f9c <+52>: ldp x22, x21, [sp, #0x10]
0x106374fa0 <+56>: ldp x24, x23, [sp], #0x40
0x106374fa4 <+60>: ret
0x106374fa8 <+64>: mov x19, x4
0x106374fac <+68>: mov x20, x3
0x106374fb0 <+72>: mov x21, x2
0x106374fb4 <+76>: mov x22, x1
0x106374fb8 <+80>: adr x0, #0xcd130 ; swift_getAssociatedTypeWitnessSlow::Predicate
0x106374fbc <+84>: nop
0x106374fc0 <+88>: adr x1, #0x3690 ; swift_getAssociatedTypeWitnessSlow::$_8::__invoke(void*)
0x106374fc4 <+92>: nop
0x106374fc8 <+96>: mov x2, #0x0
0x106374fcc <+100>: bl 0x106385ad4 ; swift_once
0x106374fd0 <+104>: nop
0x106374fd4 <+108>: ldr x8, #0xcd10c ; swift_getAssociatedTypeWitnessSlow::Override
0x106374fd8 <+112>: cbz x8, 0x106375000 ; <+152>
0x106374fdc <+116>: adr x5, #0x18a4 ; swift_getAssociatedTypeWitnessSlowImpl(swift::MetadataRequest, swift::TargetWitnessTable<swift::InProcess>*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*)
0x106374fe0 <+120>: nop
0x106374fe4 <+124>: mov x0, x23
0x106374fe8 <+128>: mov x1, x22
0x106374fec <+132>: mov x2, x21
0x106374ff0 <+136>: mov x3, x20
0x106374ff4 <+140>: mov x4, x19
0x106374ff8 <+144>: blr x8
0x106374ffc <+148>: b 0x106374f94 ; <+44>
0x106375000 <+152>: mov x0, x23
0x106375004 <+156>: mov x1, x22
0x106375008 <+160>: mov x2, x21
0x10637500c <+164>: mov x3, x20
0x106375010 <+168>: mov x4, x19
0x106375014 <+172>: bl 0x106376880 ; swift_getAssociatedTypeWitnessSlowImpl(swift::MetadataRequest, swift::TargetWitnessTable<swift::InProcess>*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*)
-> 0x106375018 <+176>: b 0x106374f94 ; <+44> <<< Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)
also for #0 0x00000001063746e4 in swift_getWitnessTable () the asm looks like:
0x106374660 <+260>: adr x26, #0xb57a0 ; AllocationPool
0x106374664 <+264>: nop
0x106374668 <+268>: str x21, [sp, #0x30]
0x10637466c <+272>: ldar x24, [x28]
0x106374670 <+276>: cbnz x24, 0x1063747c8 ; <+620>
0x106374674 <+280>: b 0x106374688 ; <+300>
0x106374678 <+284>: add x8, x24, #0x8 ; =0x8
0x10637467c <+288>: csel x28, x24, x8, lo
0x106374680 <+292>: ldar x24, [x28]
0x106374684 <+296>: cbnz x24, 0x1063747c8 ; <+620>
0x106374688 <+300>: cbnz x23, 0x1063747b0 ; <+596>
0x10637468c <+304>: ldrsw x9, [x20]
0x106374690 <+308>: cbz w9, 0x1063746a8 ; <+332>
0x106374694 <+312>: and x8, x9, #0xfffffffffffffffe
0x106374698 <+316>: add x8, x8, x20
0x10637469c <+320>: tbz w9, #0x0, 0x1063746ac ; <+336>
0x1063746a0 <+324>: ldr x8, [x8]
0x1063746a4 <+328>: b 0x1063746ac ; <+336>
0x1063746a8 <+332>: mov x8, #0x0
0x1063746ac <+336>: ldr w12, [x20, #0xc]
0x1063746b0 <+340>: ubfx x9, x12, #6, #1
0x1063746b4 <+344>: add x10, x21, x9, lsl #2
0x1063746b8 <+348>: ubfx x11, x12, #8, #8
0x1063746bc <+352>: madd x9, x11, x14, x10
0x1063746c0 <+356>: ubfx x13, x12, #16, #1
0x1063746c4 <+360>: add x9, x9, x13, lsl #2
0x1063746c8 <+364>: tbnz w12, #0x10, 0x1063746d4 ; <+376>
0x1063746cc <+368>: mov x10, #0x0
0x1063746d0 <+372>: b 0x1063746dc ; <+384>
0x1063746d4 <+376>: mul x11, x11, x14
0x1063746d8 <+380>: ldr w10, [x10, x11]
0x1063746dc <+384>: add x9, x9, x10, lsl #3
0x1063746e0 <+388>: ldrh w9, [x9, #0x2]
-> 0x1063746e4 <+392>: ldr w8, [x8, #0x10] <<< ERROR; I don't understand this line... :(
I don't fully understand the root cause of the issue, but I was able to solve this by removing Identifiable
conformance on one of my protocols. My deployment target is iOS 10, so in hindsight, I'm unsure why my framework was ever able to compile without @available checks.