Bug 19442 - NSPredicate.FromFormat, NSCompoundPredicate binding and retainCount==-1 (GC'd?) problem
Summary: NSPredicate.FromFormat, NSCompoundPredicate binding and retainCount==-1 (GC'd...
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: master
Hardware: PC Mac OS
: Normal normal
Target Milestone: Untriaged
Assignee: Rolf Bjarne Kvinge [MSFT]
Depends on: 19718
  Show dependency tree
Reported: 2014-04-30 21:59 UTC by Paul Auman
Modified: 2014-07-17 13:16 UTC (History)
6 users (show)

Is this bug a regression?: ---
Last known good build:

Sample to reproduce issues (18.01 KB, application/zip)
2014-04-30 23:02 UTC, Paul Auman
Sample #2, NSCompoundPredicate (17.39 KB, application/zip)
2014-05-01 18:08 UTC, Paul Auman

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Paul Auman 2014-04-30 21:59:51 UTC
Part One : monotouch is missing bindings for these two methods 

// + (NSPredicate *)predicateWithFormat:(NSString *)format,, ...
// + (NSPredicate *)predicateWithFormat:(NSString *)format arguments:(va_list)argList

We should be able to pass null to predicateWithFormat, which would turn this:

NSPredicate predicate = NSPredicateExtensions.FromFormat("a == %@",null);

into a valid predicate (a == nil)						

// https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/Reference/NSPredicate.html
NSPredicate.FromFormat("TRUEPREDICATE",null); // Fails because binding is looking for NSObject[]

Part Two: GC issues with NSPredicate.FromFormat and NSCompoundPredicate

This fails randomly with a predicate.retainCount of -1:

NSPredicate predicate = NSPredicate.FromFormat ("TRUEPREDICATE", new NSObject[]{}); 

The following code fails 1/4 of the time:

public static NSPredicate AppendDeletedPredicate(NSPredicate predicate)
			NSPredicate[] predicates = new NSPredicate[]
				NSPredicate.FromFormat("(markedDeleted == NO)", new NSObject[]{})
			return new NSCompoundPredicate(NSCompoundPredicateType.And, predicates);

When it fails it raises a nil insertion error.

Attached sample program shows failed cases and a special case that uses the requested binding that only fails 1/1000th of the time.

Sample ran on Device and Sim under 7.2.0, runs on sim on 7.2.1, and dies on device after 1st nil insertion error, or when GC runs the first time on device under 7.2.1.

Both Sgen, and new reference counting tried with same results.

We need the last case "NSCompoundPredicate w/ correct binding from method" to not fail.

Please feel free to contact me directly for any information you need.

Paul Auman
Auman Software, LLC
Comment 1 Paul Auman 2014-04-30 23:02:51 UTC
Created attachment 6691 [details]
Sample to reproduce issues
Comment 2 Sebastien Pouliot 2014-05-01 09:27:10 UTC
> Part One : monotouch is missing bindings for these two methods 

In this case it's (mostly*) normal that only one selector was bound. Once translated to C# the API should be identical, i.e. accepting a variable number of arguments.

* However it looks like the `params` has been omitted and , while the documentation is not clear, it makes sense to allow `null`.

Those changes will allow you do call the API like:

    var predicate = NSPredicate.FromFormat ("TRUEPREDICATE");

which is the right way (i.e. if you add `null` or anything else like an empty array the ObjC runtime does not like it and can return a nil handle).

This is also the cause for the later exceptions in your sample. The NSException being (re)thrown states:

> *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]

> Part Two: GC issues with NSPredicate.FromFormat and NSCompoundPredicate 

No, this is not GC issue (which is why switching GC or mode did not help).

-1 is `NSUIntegerMax` and that means the returned instance is a singleton (immortal). It's one of the few special values (Int32.MaxValue is another) that Apple use (at least on 32bits OS) to track special instances (like singletons).

With my changes I can run your sample without a glitch (once I remove NSPredicateExtensions and the extraneous `null` arguments).
Comment 3 Udham Singh 2014-05-01 09:56:14 UTC
I have checked this issue with the sample project attached in comment 1 and
also created a sample application to test this issue. As per my understanding,
I have noticed points mentioned below:

1. For "Part One": Below method are missing.

+ (NSPredicate *)predicateWithFormat:(NSString *)format,, ...   
+ (NSPredicate *)predicateWithFormat:(NSString *)format

2. For "Part Two": I have created a sample application and implemented the code
given in bug description, then run the project 8-10 times but not able to
reproduce the issue for Part Two.

Screencast :
For Part One : http://screencast.com/t/7R3F7FB5
For Part Two : http://screencast.com/t/rB5L5vasVh 

Environment Info:

Mac OS X 10.9.2
Xamarin Studio : 4.2.3 (build 60)
Xamarin.iOS : (Business Edition)

Please let me know if I am missing any thing, so that I can reproduce the issue
for Part Two.
Comment 5 Paul Auman 2014-05-01 18:06:23 UTC
Thank you for fixing the bindings in part one.

Regrading part two: Udham's example only iterated the problem a few times. We are seeing 50 fails for 500 iterations.

I simplified the example project to highlight the NSCompoundPredicate problem, and removed the references to NSObject[]{}, in this example we pass a valid NSString. 

The sample iterates over long.max.
Comment 6 Paul Auman 2014-05-01 18:08:59 UTC
Created attachment 6702 [details]
Sample #2, NSCompoundPredicate

please iterate on device for long.max
Comment 7 Sebastien Pouliot 2014-05-04 13:36:48 UTC
Paul, you can disregard comment #3 (and #4). It seems QA had some issue to duplicate the issue. Personally I had no problem to duplicate this using your original sample.

I've added [1] some convenience methods, which allows only a string, e.g.:

    var predicate = NSPredicate.FromFormat ("TRUEPREDICATE");

note that it's faster to call `NSPredicate.FromValue(true);` since there won't be a `string to NSString` conversion required. Otherwise it's identical, both will return the same handle (as it's a singleton).

or only one additional parameter, e.g.:

    var predicate = NSPredicate.FromFormat("a == %@", (NSObject) null);
    var predicate = NSPredicate.FromFormat("a == %@", NSNull.Null);

or a variable number of parameters, e.g.:

    var predicate = NSPredicate.FromFormat("(%@ == %@)", NSNumber.FromByte (42), NSNull.Null);

That makes your sample (with a few adjustment [2]) works _most_ of the time. 

It still fails in one case that I'm (still) debugging. It looks similar to the previous singleton issue but affecting code from static method (which is handled a bit differently). I'll close the bug once resolved.

[1] maccore 76f63a3f9ed94c1df4ba5d11cc8f51c2ff313041
[2] https://gist.github.com/spouliot/2e826ab2a86783cbd9a9
Comment 8 Rolf Bjarne Kvinge [MSFT] 2014-05-13 07:50:20 UTC
The remaining issue is tracked in #19718 (which is internal because this involves internal implementation details).
Comment 9 Rolf Bjarne Kvinge [MSFT] 2014-07-17 13:16:39 UTC
Closing since bug #19718 has been fixed.