Bug 60541 - Expose initWithDictionary:copyItems: in NS[Mutable]Dictionary
Summary: Expose initWithDictionary:copyItems: in NS[Mutable]Dictionary
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll ()
Version: XI 11.3 (xcode9.1)
Hardware: PC Mac OS
: --- enhancement
Target Milestone: 15.6
Assignee: Alex Soto [MSFT]
URL:
Depends on:
Blocks:
 
Reported: 2017-11-06 20:46 UTC by Randy
Modified: 2017-11-21 18:37 UTC (History)
3 users (show)

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

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:
Status:
RESOLVED FIXED

Description Randy 2017-11-06 20:46:01 UTC
With the latest xamarin ios 11.2.1.0, the NSMutableDictionary constructor that takes an NSDictionary parameter has changed to use a shallow copy instead of a deep copy. This used to be a deep copy since I had code that would walk a multi-tiered dictionary extracted from the metadata of an image file. That code used to work and has stopped working with an exception that the 2nd tier object was an immutable object.

I don't see any option to make a deep copy in the class.

Currently the constructor is defined as:

[Export ("initWithDictionary:"), CompilerGenerated]
public NSMutableDictionary (NSDictionary other);

I need a version of constructor that uses the "initWithDictionary:copyItems:" method. This would allow the constructor to make a deep copy.

https://developer.apple.com/documentation/foundation/nsdictionary/1410124-initwithdictionary?language=objc

I suspect it used to use that method and default 'copyItems' to YES.
Comment 1 Randy 2017-11-06 20:47:15 UTC
It's also possible that the xamarin constructor always used "initWithDictionary:" and ios 11 has changed the meaning of that method from deep to shallow copy.
Comment 2 Randy 2017-11-06 20:50:07 UTC
I just checked an ios 9 device and it works there. ios 11 must have made the change to how 'initWithDictionary:' works. Adding the constructor for 'initWithDictionary:copyItems' should solve the problem.
Comment 3 Alex Soto [MSFT] 2017-11-07 05:35:47 UTC
Hello Randy, here is some magic to get you unblocked :) you can check for iOS 11 and use CopyDictionary code:

> [DllImport (Constants.ObjectiveCLibrary, EntryPoint = "objc_msgSend")]
> extern static IntPtr IntPtr_objc_msgSend_IntPtr_bool (IntPtr receiver, IntPtr selector, IntPtr arg1, bool arg2);
> 
> public static NSMutableDictionary CopyDictionary (NSDictionary source)
> {
> 	var ctor = typeof (NSMutableDictionary).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null, new Type [] { typeof (NSObjectFlag) }, null);
> 	var shell = (NSMutableDictionary) ctor.Invoke (new object [] { NSObjectFlag.Empty });
> 	shell.Handle = IntPtr_objc_msgSend_IntPtr_bool (shell.Handle, Selector.GetHandle ("initWithDictionary:copyItems:"), source.Handle, true);
> 	return shell;
> }

We'll try to include the "initWithDictionary:copyItems:" ASAP.
Comment 4 Alex Soto [MSFT] 2017-11-15 15:28:33 UTC
PR: https://github.com/xamarin/xamarin-macios/pull/3008
Comment 5 Alex Soto [MSFT] 2017-11-15 16:52:22 UTC
fixed in xamarin-macios/master @ https://github.com/xamarin/xamarin-macios/commit/7064d35442229bd4df896011be3feb0395d8e8d9