我目前正在移植一個基於Winforms的小型.NET應用程序,使用MonoMac的本地Mac前端。該應用程序有一個帶有圖標和文本的TreeControl,它在Cocoa中不存在。如何在MonoMac C#中的派生對象上實現Cocoa copyWithZone?
到目前爲止,我移植了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:通過添加定期運行的清理函數(例如在每個新ImageAndTextCell對象的Init期間)來擴展方法3。清理功能檢查存儲對象的保留計數。保留數爲1意味着主叫方已經釋放它,所以我們也應該如此。理論上應該消除泄漏。
方法5:嘗試調用基類型的copyWithZone方法,然後使用生成的句柄構造一個新的ImageAndTextView對象。似乎做正確的事情(基礎數據被克隆)。在內部,NSObject碰到像這樣構造的對象上的保留計數,所以我們還使用PeriodicCleanup函數在不再使用它們時釋放這些對象。
基於上述情況,我認爲方法5是最好的方法,因爲它應該是唯一一個能夠生成基本類型數據的真正正確副本的方法,但我不知道方法是否具有內在危險性(我也對NSObject的底層實現做了一些假設)。到目前爲止,沒有什麼不好的事情發生在「尚未」,但如果任何人都能夠審查我的分析,那麼我會更有信心前行。
您可以將手放在'retain'選擇器上並在圖像上調用它。而FWIW將一個被管理對象的引用返回給運行時,而不用自己堅持,它會讓你最終獲得成功。 GC不能跟蹤參考。 –
'-copyWithZone:'應該將一個實例複製到內存的* zone *中。我不太瞭解Mono,但是有沒有處理NSZone的具體方法? – CodaFi
@CodaFi問題在於Cocoa(Touch)中的''''''參數已被棄用,並且被忽略,您不必過多地佔用自己。 – 2012-11-04 07:52:23