Eatton
Eatton

Reputation: 577

Unrecognized selector crash in Core Data unless I call saveContext first

Here are three functions from my code:

func workOutResults() {
  var fixtures = [Fixture]()

  let fixtureFetch: NSFetchRequest<Fixture> = Fixture.fetchRequest()

  do {
    fixtures = try coreDataStack.managedContext.fetch(fixtureFetch)
  } catch let error as NSError {
    print("Could not fetch. \(error), \(error.userInfo)")
  }

  for fixture in fixtures {
    // do stuff here
  }
}

func processUpdates() {
  // relevant code snippet below
  let theTable = loadLeagueTableFor(leagueName: "Championship", cds: cds)
}

func loadLeagueTableFor(leagueName: String, cds: CoreDataStack) -> [Club] {
  var leagueArray = [Club]()

  // Set up the sort descriptors 
  let pointsSortDescriptor: NSSortDescriptor = {
  let compareSelector = #selector(NSString.localizedStandardCompare(_:))
  return NSSortDescriptor(key: #keyPath(Club.leaguePoints),
                          ascending: false,
                          selector: compareSelector)
  }()

  let goalDiffSortDescriptor: NSSortDescriptor = {
  let compareSelector = #selector(NSString.localizedStandardCompare(_:))
  return NSSortDescriptor(key: #keyPath(Club.leagueGoalDiff),
                          ascending: false,
                          selector: compareSelector)
  }()

  let goalsForSortDescriptor: NSSortDescriptor = {
  let compareSelector = #selector(NSString.localizedStandardCompare(_:))
  return NSSortDescriptor(key: #keyPath(Club.leagueGoalsFor),
                          ascending: false,
                          selector: compareSelector)
  }()

  let clubNameSortDescriptor: NSSortDescriptor = {
  let compareSelector = #selector(NSString.localizedStandardCompare(_:))
  return NSSortDescriptor(key: #keyPath(Club.name),
                          ascending: true,
                          selector: compareSelector)
  }()

  // Do the Fetch request of Clubs, placing them in order into leagueArray
  let clubFetch: NSFetchRequest<Club> = Club.fetchRequest()
  clubFetch.predicate = NSPredicate(format: "%K == %@", #keyPath(Club.league.nameID), leagueName)
  clubFetch.sortDescriptors = [pointsSortDescriptor, goalDiffSortDescriptor, goalsForSortDescriptor, clubNameSortDescriptor]

  do {
    leagueArray = try cds.managedContext.fetch(clubFetch)
  } catch let error as NSError {
    print("Could not fetch. \(error), \(error.userInfo)")
  }

  cds.saveContext()

  return leagueArray
}

If I call...

workOutResults()
coreDataStack.saveContext()
processUpdates()

... everything works fine. Yet if I call...

workOutResults()
processUpdates()

... I get the following error in loadLeagueTableFor():

2018-01-17 19:37:38.228954+0000 Tycoon[1331:32725] -[__NSCFNumber localizedStandardCompare:]: unrecognized selector sent to instance 0xb000000000000022
2018-01-17 19:37:38.278901+0000 Tycoon[1331:32725] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber localizedStandardCompare:]: unrecognized selector sent to instance 0xb000000000000022'
*** First throw call stack:
(
0   CoreFoundation                      0x0000000104da212b __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x0000000103d6ef41 objc_exception_throw + 48
2   CoreFoundation                      0x0000000104e23024 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3   CoreFoundation                      0x0000000104d24f78 ___forwarding___ + 1432
4   CoreFoundation                      0x0000000104d24958 _CF_forwarding_prep_0 + 120
5   Foundation                          0x00000001037aaad2 _NSCompareObject + 46
6   Foundation                          0x0000000103806097 _NSSortFunctionMany + 674
7   CoreFoundation                      0x0000000104d1b3bc __CFSimpleMergeSort + 124
8   CoreFoundation                      0x0000000104d1b41c __CFSimpleMergeSort + 220
9   CoreFoundation                      0x0000000104d1b41c __CFSimpleMergeSort + 220
10  CoreFoundation                      0x0000000104d1b41c __CFSimpleMergeSort + 220
11  CoreFoundation                      0x0000000104d1b2fb CFSortIndexes + 827
12  CoreFoundation                      0x0000000104d51726 CFMergeSortArray + 454
13  Foundation                          0x00000001037aa81e _sortedObjectsUsingDescriptors + 596
14  Foundation                          0x00000001037aa570 -[NSArray(NSKeyValueSorting) sortedArrayUsingDescriptors:] + 531
15  CoreData                            0x000000010477909e -[NSManagedObjectContext executeFetchRequest:error:] + 4590
16  libswiftCoreData.dylib              0x00000001045e8f1a )
libc++abi.dylib: terminating with uncaught exception of type NSException

So saving the context in between the two fetches avoids the crash, but poses a problem as it means I have to save in a place I would rather not. Any idea why I am getting the error?

Upvotes: 1

Views: 308

Answers (1)

Tom Harrington
Tom Harrington

Reputation: 70966

It's unclear why this would ever work, because you're comparing numeric values with compareSelector, which you define to be a method on NSString. The error message describes this exactly-- you're trying to compare two numbers using a method that doesn't exist for numbers.

When using sort descriptors, the selector and comparator versions of the constructors are only necessary if you don't want the common sorting implied by the value of ascending. In the case of numeric values, if you simply want them sorted by value, you don't need either. You can simply use something like

NSSortDescriptor(key: #keyPath(Club.leaguePoints),
                      ascending: false)

Upvotes: 1

Related Questions