puerts icon indicating copy to clipboard operation
puerts copied to clipboard

[Unity] 继承C#类的JS类,其对象的引用问题

Open littlesome opened this issue 3 years ago • 7 comments

举个例子说明一下

// C#代码
class TreeNode
{
  public object userData;
}
// TS代码
class MyNodeData extends System.Object
{
   text: string;
}

const node = new TreeNode();
const userData = new MyNodeData();
userData.text = "test";
node.userData = userData;  // 这里看起来像对userData产生了引用,实际上并没有,userData会被GC

rootNode.Add(node);

非常容易踩坑,是否应该在构造JS对象的时候维护一个 C#对象对JS对象的引用关系?

littlesome avatar Oct 17 '20 05:10 littlesome

补充:

造了个神奇的玩意儿,然而发现Unity里ConditionalWeakTable支持的不好

    static ConditionalWeakTable<object, Action> refTable = new ConditionalWeakTable<object, Action>();

    public static void RefJSObject(this object o, Action callback)
    {
        refTable.Add(o, callback);
    }
class MyNodeData extends System.Object {
    constructor() {
        super();
        this.RefJSObject(() => this);
    }
}

littlesome avatar Oct 17 '20 09:10 littlesome

搞得太复杂了, 你用临时变量才会被释放, 把你的userData也放到对象属性里面不就完事了. 你用 ConditionalWeakTable 本质上也只是给你的类添加了一个字段用来保存, 而不是直接用const xxx来产生一个临时变量, 这个只是临时变量和对象生命周期的问题吧, 跟c#和ts的交互好像没有什么关系

PleijaEternal avatar Oct 17 '20 22:10 PleijaEternal

@Joycraft 例子仅仅是为了说明这个问题,实际情况可能继承后添加很多JS这边特有的数据类型的成员. 一方面C#这边不好表达这些类型,另一方面不能总是修改C#代码. 想讨论一下这种继承能否很好的支持起来, 如果不能支持怎么告知使用者不要这样用.

littlesome avatar Oct 18 '20 04:10 littlesome

需求: 用JS扩展C#类, JS部分要随C#对象一起销毁

ConditionalWeakTable的想法是给任意C#对象引用一个JS对象(通过JS函数捕获JS对象,然后通过Action让C#这边持有它的引用) 遇到的问题是:

  1. ConditionalWeakTable mono下的实现似乎有问题 https://github.com/mono/mono/issues/13560, il2cpp下未验证

    ConditionalWeakTable不支持, 可以自己用WeakReference配合Tick检测实现类似效果

  2. 产生了循环引用: ObjectPool引用C#对象, 必须等JS对象销毁; 而JS对象被Action引用, Action在WeakTable里被C#对象引用;

    一个可能的解法: 添加到WeakTable的时候, 把ObjectPool里的变成若引用. 这样C#对象就能被释放, 从而打破循环引用.

@chexiongsheng

littlesome avatar Oct 18 '20 04:10 littlesome

我的个人看法, 按照标准的JS行为, 这些临时变量就应该直接释放的, 如果你不想被释放, 就需显式的用对象的字段来储存. 如果实现了你说的这个特性, 本来按照标准js行为应该释放的反而没有被释放了,变成了隐式的自动储存, 而开发者反而需要去维护循环引用的问题, 一旦开始循环引用, 那么正确的释放反而成了难题, 本来之前js开发者根据标准行为养成的编程习惯, 估计会对这种非标准行为很困惑.

如果这类的需求很多, 其实可以作为一个特定的框架来实现, 而不应该由通用的内核和底层来自带, 作为框架的标准行为是没有问题的.

@littlesome

PleijaEternal avatar Oct 19 '20 10:10 PleijaEternal

@Joycraft 可能例子没说清楚,rootNode是树控件的一个成员,不是临时的,而rootNode引用了node,node引用了userData; 从JS的使用角度只要rootNode不清空,userData就不应该被回收


node.userData = userData;

关键是这一句,目前的实现是“只会提取userData的C#部分进行赋值,JS部分不产生引用”,这个不符合JS用户的使用习惯,容易造踩坑

littlesome avatar Oct 19 '20 11:10 littlesome

MyNodeData虽然是继承自System.Object, 但它实际创建的应该是js对象而非C#对象. 据我目前所知, js对象是不允许被C#引用的(引发GC问题), 所以你这儿创建的userData没有引用计数

throw-out avatar Nov 20 '20 08:11 throw-out