ios - Sorting NSArray, NSComparator block vs. NSSortDescriptor, with null values -
i've been using block based approach sort nsarray
... however, i'd noticed sort-related bug started investigate.
background: i'm dealing nsarray
of ekreminder
objects, have creationdate
property. want sort reminders descending creationdate
(newest reminders, first).
this previous code:
// nsarray* fetchedreminders... contents pulled reminder calendars... nsarray* sortedarray = [fetchedreminders sortedarrayusingcomparator:^nscomparisonresult(id a, id b) { nsdate* first = [(ekreminder*)a creationdate]; nsdate* second = [(ekreminder*)b creationdate]; return [second compare:first]; }];
that code, believe, correct. however, ended reminders in database had null
creation date. introduced bug - resulting sort incorrect. null
values neither @ beginning or end, , seems having nulls in array messed comparison approach, many of reminders out of sequence.
nssortdescriptor
so, tried swapping out block-based approach in favour of sortedarrayusingdescriptors
. here's current code:
// nsarray* fetchedreminders... contents pulled reminder calendars... nssortdescriptor* sortdescriptor; sortdescriptor = [[nssortdescriptor alloc] initwithkey:@"creationdate" ascending:no]; nsarray* sortdescriptors = [nsarray arraywithobject:sortdescriptor]; nsarray* sortedarray = [fetchedreminders sortedarrayusingdescriptors:sortdescriptors];
this works.
(with current data, 101 reminders, 6 of them have null
creation dates , they're placed @ end. other reminders in correct sequence).
questions
first, doing wrong sortedarrayusingcomparator
approach?
if not, expected these different approaches handle null
s differently?
in case, make nssortdescriptor
approach preferred method, if can potentially have null
s in data?
the core issue you're running here you're not manually handling null values in block pass sortedarrayusingcomparator:
. depending on values block gets called with, first
can nil
, second
can nil
, or both can nil
.
any message sent nil
returns equivalent 0
value (e.g. method returning float
, when sent nil
returns 0.0f
, method returning int
when sent nil
returns 0
, , method returning object sent nil
returns nil
). means have following cases:
[<non-nil> compare:<non-nil>]
(returns valid value)[<non-nil> compare:nil]
(undefined behavior, per https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/classes/nsdate_class/#//apple_ref/occ/instm/nsdate/compare:)[nil compare:<non-nil>]
(returns 0; called onnil
)[nil compare:nil]
(returns 0, called onnil
)
this means call being made across values in array, values being returned non-sensical (e.g. [nil compare:[nsdate date]]
returns 0
, equivalent nsorderedsame
, not true), not mention results returned undefined calls.
in effect, these invalid values being sorted strange places in array. if had defined behavior should happen if either value nil
, you'd consistent behavior.
the following code makes sorting behavior consistent (and should give similar behavior sort descriptors method above):
nsarray* sortedarray = [fetchedreminders sortedarrayusingcomparator:^nscomparisonresult(id a, id b) { nsdate* first = [(ekreminder*)a creationdate]; nsdate* second = [(ekreminder*)b creationdate]; if (!first && !second) { // nils have same relative ordering return nsorderedsame; } else if (!first) { // second not nil; send first toward end of array return nsordereddescending; } else if (!second) { // first not nil; send second toward end of array return nsorderedascending; } else { // neither nil; valid return [second compare:first]; } }];
Comments
Post a Comment