私は現在、小さな Winforms ベースの .NET アプリケーションを移植して、MonoMac でネイティブの Mac フロントエンドを使用しています。アプリケーションにはアイコンとテキストを含む TreeControl がありますが、これは Cocoa にはそのままでは存在しません。
これまでのところ、Apple の DragNDrop の例の ImageAndTextCell コードのほとんどすべてを移植しました: https://developer.apple.com/library/mac/#samplecode/DragNDropOutlineView/Listings/ImageAndTextCell_m.html#//apple_ref/doc/uid /DTS40008831-ImageAndTextCell_m-DontLinkElementID_6、カスタム セルとして NSOutlineView に割り当てられます。
メソッドを適切に移植するcopyWithZone
方法がわからないことを除いて、ほぼ完全に機能しているようです。残念ながら、これは NSOutlineView が作成している内部コピーに画像フィールドがないことを意味し、展開および折りたたみ操作中に画像が一時的に消えてしまいます。問題の Objective-C コードは次のとおりです。
- (id)copyWithZone:(NSZone *)zone {
ImageAndTextCell *cell = (ImageAndTextCell *)[super copyWithZone:zone];
// The image ivar will be directly copied; we need to retain or copy it.
cell->image = [image retain];
return cell;
}
MonoMac は copyWithZone メソッドを公開しておらず、それ以外の方法でそれを呼び出す方法がわからないため、最初の行は私をつまずかせます。
アップデート
現在の回答と追加の調査とテストに基づいて、オブジェクトをコピーするためのさまざまなモデルを考え出しました。
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;
}
方法 1:アンマネージド オブジェクトの保持カウントを増やします。管理されていないオブジェクトは永久に存続し (dealloc は呼び出されなかったと思います)、管理されたオブジェクトは早期に収集されます。負け負けオールラウンドに見えますが、実際は走ります。
方法 2:管理対象オブジェクトの参照を保存します。管理されていないオブジェクトは放置され、dealloc は適切なタイミングで呼び出し元によって呼び出されたように見えます。この時点で、管理対象オブジェクトは解放され、破棄されます。これは理にかなっているように見えますが、欠点として、基本型の dealloc は実行されません (私が思うに?)
方法 3:保持カウントを増やし、参照を保存します。管理されていないオブジェクトと管理されているオブジェクトは永久にリークします。
方法 4:定期的に実行されるクリーンアップ関数を追加することにより、方法 3 を拡張します (たとえば、新しい ImageAndTextCell オブジェクトの初期化中)。クリーンアップ機能は、格納されたオブジェクトの保持カウントをチェックします。1 の保持カウントは、呼び出し元がそれを解放したことを意味するため、同様に保持する必要があります。理論上は漏れをなくすはずです。
方法 5:基本型で copyWithZone メソッドの呼び出しを試み、結果のハンドルを使用して新しい ImageAndTextView オブジェクトを構築します。正しいことをしているようです (基本データが複製されます)。内部的には、NSObject はこのように構築されたオブジェクトの保持カウントを増加させるため、使用されなくなったオブジェクトを解放するために PeriodicCleanup 関数も使用します。
上記に基づいて、基本型データの真に正しいコピーが得られる唯一の方法であるため、方法 5 が最良の方法であると考えていますが、この方法が本質的に危険であるかどうかはわかりません (私も作成しています)。 NSObject の基本的な実装に関するいくつかの仮定)。これまでのところ「まだ」悪いことは何も起きていませんが、誰かが私の分析を精査できるなら、私は前進することにもっと自信を持つでしょう.