I am currently porting a small Winforms-based .NET application to use the built-in Mac interface with MonoMac. The application has a TreeControl with icons and text that does not exist out of the box in Cocoa.
So far, I have ported almost all of the ImageAndTextCell code in the Apple DragNDrop example: https://developer.apple.com/library/mac/#samplecode/DragNDropOutlineView/Listings/ImageAndTextCell_m.html#//apple_ref/doc/uid/DTS4m8831-31 -DontLinkElementID_6 , which is assigned by NSOutlineView as a custom cell.
It seems to work almost perfectly, except that I did not understand how to correctly copyWithZone method. Unfortunately, this means that the internal copies that NSOutlineView makes do not have an image field, and this causes the images to disappear briefly during the expand and collapse operations. Objective-c Code:
- (id)copyWithZone:(NSZone *)zone { ImageAndTextCell *cell = (ImageAndTextCell *)[super copyWithZone:zone];
The first line is what turns me off, since MonoMac does not provide the copyWithZone method, and I do not know what to call it otherwise.
Update
Based on current answers and additional research and testing, I came up with many models for copying an object.
static List<ImageAndTextCell> _refPool = new List<ImageAndTextCell>(); // Method 1 static IntPtr selRetain = Selector.GetHandle ("retain"); [Export("copyWithZone:")] public virtual NSObject CopyWithZone(IntPtr zone) { ImageAndTextCell cell = new ImageAndTextCell() { Title = Title, Image = Image, }; Messaging.void_objc_msgSend (cell.Handle, selRetain); return cell; } // Method 2 [Export("copyWithZone:")] public virtual NSObject CopyWithZone(IntPtr zone) { ImageAndTextCell cell = new ImageAndTextCell() { Title = Title, Image = Image, }; _refPool.Add(cell); return cell; } [Export("dealloc")] public void Dealloc () { _refPool.Remove(this); this.Dispose(); } // Method 3 static IntPtr selRetain = Selector.GetHandle ("retain"); [Export("copyWithZone:")] public virtual NSObject CopyWithZone(IntPtr zone) { ImageAndTextCell cell = new ImageAndTextCell() { Title = Title, Image = Image, }; _refPool.Add(cell); Messaging.void_objc_msgSend (cell.Handle, selRetain); return cell; } // Method 4 static IntPtr selRetain = Selector.GetHandle ("retain"); static IntPtr selRetainCount = Selector.GetHandle("retainCount"); [Export("copyWithZone:")] public virtual NSObject CopyWithZone (IntPtr zone) { ImageAndTextCell cell = new ImageAndTextCell () { Title = Title, Image = Image, }; _refPool.Add (cell); Messaging.void_objc_msgSend (cell.Handle, selRetain); return cell; } public void PeriodicCleanup () { List<ImageAndTextCell> markedForDelete = new List<ImageAndTextCell> (); foreach (ImageAndTextCell cell in _refPool) { uint count = Messaging.UInt32_objc_msgSend (cell.Handle, selRetainCount); if (count == 1) markedForDelete.Add (cell); } foreach (ImageAndTextCell cell in markedForDelete) { _refPool.Remove (cell); cell.Dispose (); } } // Method 5 static IntPtr selCopyWithZone = Selector.GetHandle("copyWithZone:"); [Export("copyWithZone:")] public virtual NSObject CopyWithZone(IntPtr zone) { IntPtr copyHandle = Messaging.IntPtr_objc_msgSendSuper_IntPtr(SuperHandle, selCopyWithZone, zone); ImageAndTextCell cell = new ImageAndTextCell(copyHandle) { Image = Image, }; _refPool.Add(cell); return cell; }
Method 1: Increases the number of holds of an unmanaged object The unmanaged object will persist forever (I think dealloc never called), and the managed object will be assembled earlier. It seems that he loses everything around, but works in practice.
Method 2: Save the managed entity reference. The unmanaged object remains at rest, and dealloc is called by the caller in a reasonable amount of time. At this point, the managed entity is freed and deleted. This seems reasonable, but then again, the base dealloc type will not start (I think?)
Method 3: Increases the number of deductions and saves the link. Unmanaged and managed objects go on forever.
Method 4: Extends Method 3 by adding a cleanup function that runs periodically (for example, during the Init of each new ImageAndTextCell object). The cleanup function checks the number of saved saved objects. Holding account 1 means the caller has issued it, so we must do this too. A leak in theory should be eliminated.
Method 5: Attempting to call the copyWithZone method on the base type, and then create a new ImageAndTextView object with the resulting handle. Seems to do the right thing (the underlying data is cloned). Internally, NSObject strikes by saving objects constructed in this way, so we also use the PeriodicCleanup function to free these objects when they are no longer used.
Based on the foregoing, I believe that method 5 is the best approach, since it should be the only one that leads to a truly correct copy of the base type data, but I do not know if this approach is inherently dangerous (I also make some assumptions about the base implementation of NSObject). So far, nothing bad has happened βyet,β but if someone could verify my analysis, I would go forward more confidently.