Question about NSMutableArray Sort using sortedArrayUsingFunction:

Discussion in 'Mac Programming' started by mdeh, Mar 18, 2010.

  1. mdeh macrumors 6502

    Joined:
    Jan 3, 2009
    #1
    Hi all,
    Following along in the documentation using "sortedArrayUsingFunction", I cannot seem to to be able to get the Array to sort as I expect.
    So, here is the code...in a command line ap for simplicity. I would have expected the last 2 sorts to be inverse of the other, but both sorts are the same.
    I **think** I have pretty carefully copied the examples from the docs...so would really like to get some insight.

    Thanks as always.



    Code:
    #import <Foundation/Foundation.h>
    
    
    NSInteger alphabeticSort(id string1, id string2, void *reverse)
    {
    	if (( NSInteger *) reverse == NO)
    	{
    		return [string2 localizedCaseInsensitiveCompare:string1];
    	}
    	
    		return [string1 localizedCaseInsensitiveCompare:string2];
    	
    }
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
    	NSMutableArray *anArray =
        
    	[NSMutableArray arrayWithObjects:@ "i", @"a", @"d", @"b",   @"c",  @"e",@"g",@"L", nil];
    	
    	NSLog(@"anArray:%@", anArray);
    	
    	
    	
    	
    	NSArray *sortedArray;
    	
    	int reverseSort = NO;
    	
    	sortedArray = [anArray sortedArrayUsingFunction:alphabeticSort context:&reverseSort];
    	NSLog(@"Sort using function: \"NO\" %@", sortedArray);
    
    	
    	reverseSort = YES;
    	
    	sortedArray = [anArray sortedArrayUsingFunction:alphabeticSort context:&reverseSort];
    	NSLog(@"Sort using function:\"YES\" %@", sortedArray);
    	
    	
    	
        [pool drain];
        return 0;
    }
    
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    You pass the address of an int, accept it as void *, the cast to an NSInteger *, then compare it to YES and NO without dereferencing. I think a dereference would fix it, but also make the types agree. NSInteger won't always be typedef'd to int.

    -Lee
     
  3. mdeh thread starter macrumors 6502

    Joined:
    Jan 3, 2009
    #3

    Hmmm??? I wonder if something has changed since they published this example, as it is pretty much word for word from their docs.


    http://developer.apple.com/mac/libr...lections/Articles/sortingFilteringArrays.html
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    Congratulation, you found a bug in Apple's code.

    I tried compiling and running their code as given, and it produces the desired output (sorted in ascending order, not reversed), but it does so for the wrong reasons.

    A simple test case to show the bug: change reverseSort to YES, recompile and rerun, and the same ascending-order output is produced.

    There are several bugs in Apple's code, which combine to give the appearance of correctness:
    1. the reverseSort flag is passed by reference, i.e. a pointer, but the void* received by the function is not dereferenced, i.e. the pointer itself is compared to NO, not the value pointed to.
    2. the conditional logic in the sort-ordering function is inverted.

    The same bug(s) occurs in other samples on the web page, although with Bool pointer.

    There is a feedback form at the bottom of Apple's web page. I suggest sending them feedback that their example is wrong, and describe why. You can point them to this MacRumors thread.

    Here's a simple way to start fixing your posted code:
    Code:
    NSInteger alphabeticSort(id string1, id string2, void *reverse)
    {
    	int reverseSort = * ((int*)reverse);
    	if (reverseSort)
    ...
    
    This fixes both bugs: 1. it dereferences the pointer to get the flag, 2. it uses the correct sort-ordering logic (reverseSort non-zero produces a reversed sort).

    You could also simplify the code to pass an actual YES/NO flag, rather than by reference. This will need a type-cast when invoking the sorting method, and a corresponding change in the sort-ordering function.
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    It definitely seems wrong to me:
    Code:
    #import <Foundation/Foundation.h>
    
    
    NSInteger alphabeticSort(id string1, id string2, void *reverse)
    {
    	if ((*(int *) reverse) == NO)
    	{
    		return [string2 localizedCaseInsensitiveCompare:string1];
    	}
    	
    		return [string1 localizedCaseInsensitiveCompare:string2];
    	
    }
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
    	NSMutableArray *anArray =
        
    	[NSMutableArray arrayWithObjects:@ "i", @"a", @"d", @"b",   @"c",  @"e",@"g",@"L", nil];
    	
    	NSLog(@"anArray:%@", anArray);
    	
    	
    	
    	
    	NSArray *sortedArray;
    	
    	int reverseSort = NO;
    	
    	sortedArray = [anArray sortedArrayUsingFunction:alphabeticSort context:&reverseSort];
    	NSLog(@"Sort using function: \"NO\" %@", sortedArray);
    
    	
    	reverseSort = YES;
    	
    	sortedArray = [anArray sortedArrayUsingFunction:alphabeticSort context:&reverseSort];
    	NSLog(@"Sort using function:\"YES\" %@", sortedArray);
    	
    	
    	
        [pool drain];
        return 0;
    }
    
    I think with that change (i didn't test it) it should work.

    -Lee
     
  6. mdeh thread starter macrumors 6502

    Joined:
    Jan 3, 2009
    #6
    Yep..it does. Rare to find an issue in the docs...I bet it has been there for years.

    Thanks Lee...I knew I could rely on you!!!
    :):)
     
  7. GorillaPaws macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #7
    They should send you an iTunes gift-card or something every time you find a bug. Or run it like Chucky-Cheese where you bank up your tickets and can buy bigger/better prizes the more you have. Save up 500 tickets and you get your own personal official Apple objective-c oompa-loompa.
     
  8. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #8
    Just to clarify, but Lee's code still has the conditional-logic in the function inverted.

    Sample output:
    Code:
    2010-03-18 13:59:30.454 a.out[8082] anArray:(i, a, d, b, c, e, g, L)
    2010-03-18 13:59:30.470 a.out[8082] Sort using function: "NO" (L, i, g, e, d, c, b, a)
    2010-03-18 13:59:30.470 a.out[8082] Sort using function:"YES" (a, b, c, d, e, g, i, L)
    
    The one labeled "NO" should be ascending order, but it's descending. And vice versa for the one labeled "YES".

    To fix it:
    Code:
    NSInteger alphabeticSort(id string1, id string2, void *reverse)
    {
    	if ((*(int *) reverse))
    
     
  9. mdeh thread starter macrumors 6502

    Joined:
    Jan 3, 2009
    #9

    Thanks Chown
    I guess you should be getting the iTunes gift card!!!
     

Share This Page