NSPredicate funkyness

Discussion in 'iOS Programming' started by dantastic, Feb 21, 2014.

  1. dantastic macrumors 6502

    dantastic

    Joined:
    Jan 21, 2011
    #1
    Was tearing my hair out for a while. didn't understand what was going on. Performing a fetchRequest from my core data database I used this predicate:
    Code:
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"read == NO"];
    
    And it wouldn't work, no hits at all. I was pulling out just all records and loggin them etc. I was pretty damn sure there were records in my database which should have been caught, but nope.

    After some fiddling I tried:
    Code:
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"read != YES"];
    
    And it worked! wtf?! :eek: :confused:

    Can someone explain this one to me, why does one work and not the other. In my understanding they should be the same?
     
  2. xStep macrumors 68000

    Joined:
    Jan 28, 2003
    Location:
    Less lost in L.A.
    #2
    I'm not a CoreDate expert yet. A guess would be that the 'read' column isn't setup to accept only NO or YES and the code is only adding YES values and skipping setting up NO values. In that case the column would be NULL, which is not the same thing as NO, and NULL != YES.

    If SQLite is the backend, you could pull the database and manually inspect the column via SQLite on your Mac.
     
  3. dantastic thread starter macrumors 6502

    dantastic

    Joined:
    Jan 21, 2011
    #3
    I haven't pulled the underlying sqlite file but the read property is defined as a boolean in code data with a default value of NO.

    As the app is used these all have been set to YES so I wrote a method to set a random record to NO again on a timer.

    So I have explicitly set a @(NO) against the read property but the predicate is still only returning hits on != YES.

    a not set value (null) was my first thought as well but I really think I have set these properly to NO.
     
  4. xStep macrumors 68000

    Joined:
    Jan 28, 2003
    Location:
    Less lost in L.A.
    #4
    I created a little project to see what I could come up with. At first it worked perfectly. I then mucked with the assignments and how I generated the .h entity file and can create unexpected results. I'll need more details from you to have a chance of coming up with the right answer.

    In your generated entity .h file how is the "read" variable defined? One of the following two?
    Code:
    @property (nonatomic) BOOL read;
    @property (nonatomic, retain) NSNumber * read;
    
    What do all of your assignment statements look like for both YES and NO? (My table is names table1.)
    Code:
    table1.read = YES;
    table1.read = [NSNumber numberWithBool: YES];
    
    table1.read = NO;
    table1.read = [NSNumber numberWithBool: NO];
    
    Is Xcode giving any warning for any of your assignment lines? In one case it doesn't where it would be helpful.

    What version of Xcode are you using?

    Inspect your sqlite db. I'm assuming your storage is sqlite. Odd values for your boolean column to look for are 80, 88, and an empty value.
     
  5. dantastic thread starter macrumors 6502

    dantastic

    Joined:
    Jan 21, 2011
    #5
    Thanks for taking interest. To answer your question

    The property as defined in the NSManagedObject subclass
    Code:
    @property (nonatomic, strong) NSNumber * read;
    
    The way I assign the property.
    Code:
    table1.read = @(NO);
    
    table1.read = @(YES);
    
    Can you tell a bit more about the odd behaviors you are seeing. I use core data a lot and if there are any gotyas I really want to be up to date with them.
     
  6. xStep macrumors 68000

    Joined:
    Jan 28, 2003
    Location:
    Less lost in L.A.
    #6
    Well, that really is a big WTF. I altered my test program to your setup and looked at the DB result and it was as it should be. The predicates also worked as one would expect. You should need that negative on YES as you are doing. I also found your default of NO is working properly.

    As for odd behavior, the following is whey you have to be careful.

    When your property is defined as a NSNumber, trying to assign raw YES or NO has issues. The YES is caught as a compiler error and the NO is not noticed as an issue at all. The problem with that is that the assignment of NO ends up placing NULL in the column. See the attached image for this problem.

    If you setup your properties to use scalar values (BOOL) then assigning NSNumbers numberWithBool values, it ends up setting the columns to something other than 1 or 0. Last week when I responded the numbers were 80 and 88. Now they are 92 and 100. Xcode does warn you about the inappropriate assignment.

    Now, given the above quirks, you can see why you would get unexpected results. Any one line in error will give unexpected values. This is why you really should check your DB, and pay attention to any compiler warnings.
     

    Attached Files:

  7. dantastic thread starter macrumors 6502

    dantastic

    Joined:
    Jan 21, 2011
    #7
    Don't know what gives.
    I opened the actual sqlite file and the value for read in the underlying table is NULL or 1

    So for whatever reason when I set @(NO) it gets translated into NULL.

    I don't know if this is due to some configuration value or some other reason.

    pretty scary... :(
     
  8. xStep macrumors 68000

    Joined:
    Jan 28, 2003
    Location:
    Less lost in L.A.
    #8
    I would do a few things.

    Go through the code very closely and check every line where you assign a value to the 'read' variable, or forget to miss the assignment where you expect the default to work.

    Do a Clean of your project; Menu - Project - Option-key Clean.

    Remove the copy of the derived data file; Organizer - Projects - Delete for the project.

    Remove any copies of the app from devices and the simulator.

    You could try the alternative assignment. I don't see why it should be a difference for you as it makes no difference in my test code.
    Code:
    [NSNumber numberWithBool: NO];
     
  9. dantastic thread starter macrumors 6502

    dantastic

    Joined:
    Jan 21, 2011
    #9
    Felt I needed to update this as you have been a real help xStep.

    So I haven't really had time to look at this - crazy busy at the moment. But I just spotted that the icon this method takes it's value from was not showing the right thing all of a sudden.

    So the code has not changed, the model has not changed. The project has been cleaned and all apps removed from devices etc. Just stopped working. wtf?

    So I started looking. I have 4 objects in my database. 3 are unread and one is read.

    Code:
    read != YES //returns 0
    read == YES //returns 1
    read == NO //returns 0
    read != NO //returns 0
    
    This is a bit different than before. I don't have time right now to actually get to the bottom of this but for some reason it seems that core data is storing my @(NO) as a null value and I can't use a boolean in my predicate.

    For the time being I just fetch all objects and loop through and query read.boolvalue - at least it's always right....
     

Share This Page