以下大部分是正确的;我可能错过了一些事情或者误解了一些事情;阅读链接的文档(它太短了)并在评论中大放异彩 - 谢谢。
不好的类比:一个类就是一个千篇一律的东西。 一个例子是用饼干切割机制作的饼干。
类比问题: 我也准备打电话给JSL班级A原型。 原型是一个实际的 cookie。 原型 cookie可以被吃掉或被修改,但你通常不想这样做。 相反,您应该在饼干面团中描出其轮廓,然后用剪刀将其剪开以制作新的饼干实例。 但是,如果您确实咬了原型 cookie,则所有未来的 cookie 实例也会看起来被咬了(第二个示例)。 另外,_init_(...) 方法不是针对原型 cookie 运行的,因此它可能是半生不熟的,但它具有正确的形状。
这文档用途模板而不是原型。 与模板相比,我更喜欢原型;原型是一个可用的对象,而模板只是显示喷漆应该去的地方。 对于第一个例子来说,这种微妙的区别并不重要。
一个简单的例子
这是一个简单的单类示例气泡。 类原型由 DefineClass() 创建。 NewObject() 使得原型中的 61 个实际实例。 61 个实例中的每一个都有一个半径和一个 x,y 中心。 该原型还提供了 _init_(...)、drift() 和 draw() 方法。 使用对象显示图形每个对象的绘制方法。 然后运行一个循环,为每个对象调用对象漂移方法,然后告诉图形重新绘制。 结果是一个像这样的动画图:
该图的 JSL:
// define a simple class (like a template) to represent a 2D bubble
Define Class("bubble",
// class member variables
m_radius = .; // class member variables are specified in
m_xpos = .; // the class definition with initial values.
m_ypos = .; // these values are copied into instances created by NewObject(...)
// class methods
_init_ = Method( {radius, x, y}, // called by NewObject(bubble(r,x,y)) below to initialize an instance
this:m_xpos = x; // this: is the namespace of an instance
m_ypos = y; // you don't need "this:" unless the member name is
m_radius = radius; // the same as a parameter name
);
drift = Method( {}, // a method to move the bubble
m_xpos = m_xpos * 0.995;// tentatively drift to center
m_ypos = m_ypos * 0.995;
);
draw = Method( {}, // a method to draw the bubble
Circle( {m_xpos, m_ypos}, m_radius )
);
);
// make a bunch of instances of the bubble class, keep them in a list
bubblelist = {};
For( i = 1, i <= 61, i += 1,
Insert Into( bubblelist,
// each instance of bubble is initialized with a random radius and position
New Object( bubble( Random Uniform( .03, .8 ), Random Uniform( -.9, .9 ), Random Uniform( -.9, .9 ) ) )
)
);
picno = 0; // need an ID for each picture
// make a window to display all the instances of the bubble class
New Window( "bubble class instances",
gb = Graph Box( // the graphic script for the graph box scales and sizes the axes and...
xaxis( {Add Ref Line( 0, "Solid", "Black", "", 1 )} ),
yaxis( {Add Ref Line( 0, "Solid", "Black", "", 1 )} ),
framesize( 600, 600 ),
X Scale( -1, 1 ),
Y Scale( -1, 1 ),
// the graphic script tells each bubble to draw itself
For( i = 1, i <= N Items( bubblelist ), i += 1,
bubblelist[i]:draw()
);
text({-.9,-.9},char(picno)),
<<ONCLOSE></ONCLOSE>我对其工作原理的心智模型:在幕后,DefineClass() 保留了所有 DefineClass() 原型的隐藏列表,它们看起来很多像实例一样。 稍后会详细介绍;上面的示例通过 NewObject() 函数访问它们。 NewObject() 克隆原型对象的新实例并运行 _init_ 方法。
什么是这: ?
在上面的示例中,我将成员变量命名为米_字首。 这不是必需的,但它对于记住哪些变量是成员变量以及防止与参数名称和局部变量名称发生冲突非常有用。 但我也用过这个:m_xpos在 _init_() 方法分配之一上。 这这:命名空间的意思是“当前实例的成员变量”。 所以这个:x = x;如果您希望成员变量 x 和参数 x 具有相同的名称,则可以使用。米_对于下一个维护你的代码的人来说会更容易。
什么是气泡: ?
还有另一个命名空间你大多不需要这将说明为什么我说隐藏列表中的 DefineClass() 原型看起来很多类似实例——类名是一个命名空间。 使用气泡:m_半径将访问原型的 m_radius,即使是从实例方法。 如果你认为你需要一个静态成员变量,这是一种制作静态成员变量的方法......如果你小心的话。
使用静态值的原型
下一个示例使用静态 m_ID 变量。 正如评论所暗示的那样,我可能应该做出不同的变化,但是这个仍然有用,因为它解释了“如果你小心的话”警告。 发生的事情是这样的(再次是我的思维模型):
当下面的 _init_() 运行时, bubble2 类已经将所有原型内容复制到新实例中。 新实例具有原型 m_id 的副本。 _init_() 然后更改原型中的 m_id()。 下一个实例将获取更改后的值。 其他成员函数必须在原型中的 m_id (bubble2:m_id) 和 this:m_id (或者只是没有命名空间的 m_id)之间进行选择。 在这里,他们使用 m_id,它是实例唯一整数的副本。
在视频中,最后一块拼图在 5 点 15 分出现在屏幕底部边缘。 黑色圆圈是显示其 m_id 值的 bubble2。 他们使用 m_id 确保在研究所有近邻时忽略自己。
// twist is a scaling divisor for a shearing rotation that moves
// the outer edge faster.
// this value needs to get bigger with more beehive cells.
global:twist = 600;
// choose the size to make them fit, but it also interacts with twist
// because the distance from the center grows
global:bubblesize = 0.057;
// use a defined class to represent each cell in the beehive.
Define Class("bubble2",
m_radius = .; // in the video, all beehive cells have the same radius
m_xpos = .; // the beehive cell's position
m_ypos = .;
//
// this is answering the question "can a JSL Class have a static member variable?"
// this could have been done more intuitively for this example by passing the
// index of the creation loop to the _init_ code, but here it is...initialize the
// "static" value to 1...
m_id = 1; // keep reading to understand the two-faced nature of this variable...
// What actually happens: when newObject(bubble2(...)) makes a new instance, the _init_(...)
// is called.
// First, the m_id is cloned from the prototype to the instance. The first instance gets m_id==1.
// Second, the bubble2: namespace causes the prototype class to increment the
// prototype's m_id. The next instance gets the new value from the prototype, m_id==2.
// this is a little odd, coming from a C++ -like language; in the drift method below,
// "other:m_id != m_id" both m_id values are regular non-static instance members.
// if you want the static behaviour, use the bubble2:m_id approach (bubble2 is the class
// name and access the singleton in the prototype class.)
// Normally you *never* want to use the class name as a name space qualifier because
// you set up the class correctly and initialize via _init_( parameters ). And normally
// a static value is also a constant value, so using a copy is just fine. But if
// multiple instances need to communicate via a singleton static, AND you always use
// the namespace, this is a reasonable place to keep the static.
// Having written all that, I now dislike this particular example because it is using
// a confusing combination of bubble2:m_id and instance values of m_id.
_init_ = Method( {radius, x, y},
m_radius = radius;
this:m_xpos = x; // this: (or nothing) operates on an instance
m_ypos = y;
bubble2:m_id++; // bubble2: operates on the prototype. The next newobject(bubble2(...)) uses the modified prototype
);
distanceBetweenCenters = Method( {other},
/*return*/ Sqrt( (m_xpos - other:m_xpos) ^ 2 + (m_ypos - other:m_ypos) ^ 2 )
);
distanceBetweenEdges = Method( {other}, // negative: overlaps
/*return*/ distanceBetweenCenters( other ) - (m_radius + other:m_radius)
);
drift = Method( {},
// bubbles should pull to 0,0 AND move away from near bubbles
angle = ATan( m_ypos, m_xpos );
radius = Sqrt( m_ypos ^ 2 + m_xpos ^ 2 );
shear = global:twist; // also depends on number of circles
shrink = .99950;
// radius/shear creates a tiny amount of rotation, more on the edges. That rotational
// shearing helps the circles bounce about. /400 is not enough and /100 is too much.
// radius*shrink pulls all the circles to the origin. It is balanced by push, below.
x = Cos( angle + radius / shear ) * radius * shrink;
y = Sin( angle + radius / shear ) * radius * shrink;
// check neighbors
{neighbors, distances} = kdt << kNearestRows( 7, m_id );
tension = 0;
ntension = 0; // calculate a tension, or stress, for contour to use as a color
For( iN = 1, iN <= N Items( neighbors ), iN += 1,
i = neighbors[iN];
other = bubblelist[i];
If( other:m_id != m_id, // <<<<<<<<<<<<<<<< NOT the bubble2:m_id value !!!
dist = distanceBetweenEdges( other );
// if the dist is zero, it is perfect. if the other guy is far away, ignore him.
If( Abs( dist ) < global:bubblesize * 0.8,
tension += dist ^ 2; // this will be a RMS value at the bottom
ntension += 1;
);
If( dist < 0, //push
angle = ATan( m_ypos - other:m_ypos, m_xpos - other:m_xpos );
x += Cos( angle ) * Abs( dist ) ^ .4 * .05;
y += Sin( angle ) * Abs( dist ) ^ .4 * .05;
);//
, //
Beep()
);
);
m_xpos = x;
m_ypos = y;
If( ntension == 0,
ntension = 1;
tension += (global:bubblesize / 2) ^ 2;
);
Sqrt( tension / ntension ); // RMS
);
draw = Method( {},
Transparency( .8 );
Circle( {m_xpos, m_ypos}, m_radius, "FILL" );
Transparency( 1 );
Text( center justified, {m_xpos, m_ypos - m_radius * .2}, Char( m_id ) );
);
);
bubblelist = {};
n = 12; // how many "rings" of hex cells
m = -26 + 21 * n + 3 * (n - 3) ^ 2; // yes, I used JMP to get this formula
For( i = 1, i <= m, i += 1,
Insert Into(
bubblelist,
New Object( bubble2( Random Uniform( global:bubblesize, global:bubblesize ), Random Uniform( -.9, .9 ), Random Uniform( -.9, .9 ) ) )
)
);
// need a table for contour plot, and it will include 100 values in a circle around the hive
ring = 100;
dt = New Table( "tension",
Add Rows( m + ring ),
New Column( "x", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) ),
New Column( "y", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) ),
New Column( "tension", Numeric, "Continuous", Format( "Best", 12 ), Set Values( [] ) )
);
For( i = 1, i <= ring, i += 1,
angle = 2 * Pi() * i / ring;
dt:x[m + i] = 1.25 * Sin( angle );
dt:y[m + i] = 1.25 * Cos( angle );
dt:tension[m + i] = 0;
);
cplot = dt << Contour Plot(
X( :x, :y ),
Y( :tension ),
Show Data Points( 0 ),
Fill Areas( 1 ),
Label Contours( 0 ),
Transform( "Range Normalized" ),
Color Theme( "Green Yellow Red" ),
Specify Contours( Min( 0 ), Max( .01 ), N( 50 ) )
);
Report( cplot )[framebox( 1 )] << Frame Size( 1920, 1080 ) << maximizewindow( 1 ) << backgroundcolor( "black" );
Report( cplot )[framebox( 1 )] << xaxis(
{Format( "Fixed Dec", 12, 3 ), Min( -1.4*1920/1080 ), Max( 1.4*1920/1080 ), Inc( 0.5 ), Label Row(
{Show Major Grid( 0 ), Show Major Labels( 0 ), Show Major Ticks( 0 ), Show Minor Grid( 0 ), Show Minor Labels( 0 ), Show Minor Ticks( 0 )}
)}
);
Report( cplot )[framebox( 1 )] << yaxis(
{Format( "Fixed Dec", 12, 3 ), Min( -1.4 ), Max( 1.4 ), Inc( 0.5 ), Label Row(
{Show Major Grid( 0 ), Show Major Labels( 0 ), Show Major Ticks( 0 ), Show Minor Grid( 0 ), Show Minor Labels( 0 ), Show Minor Ticks( 0 )}
)}
);
// here is the script that makes the bubbles draw
Report( cplot )[framebox( 1 )] << addgraphicsscript(
Fill Color( "black" );
text Color( "white" );
pen Color( rgbcolor(64,64,64) );
For( i = 1, i <= N Items( bubblelist ), i += 1,
bubblelist[i]:draw()
);
hline(0);
vline(0);
);
cplot << onclose(
keepRunning = 0;
Close( dt, nosave );
1;
);
Wait( 0 );
//dir = "$temp\ClassWithStatic" || Char( global:twist ) || "\";
//Delete Directory( dir );
//Create Directory( dir );
//Write( Convert File Path( dir, "windows" ) );
picno = 0;
keepRunning = 1;
While( keepRunning,
xy = J( N Items( bubblelist ), 2, . );
For( i = 1, i <= N Items( bubblelist ), i += 1,
xy[i, 1] = bubblelist[i]:m_xpos;
xy[i, 2] = bubblelist[i]:m_ypos;
);
// the kdtable makes it faster to find near neighbors
kdt = KDTable( xy );
For( i = 1, i <= N Items( bubblelist ), i += 1,
dt:tension[i] = bubblelist[i]:drift();
dt:x[i] = bubblelist[i]:m_xpos;
dt:y[i] = bubblelist[i]:m_ypos;
);
Report( cplot )[framebox( 1 )] << inval << updatewindow;
picno += 1;
// Report( cplot )[framebox( 1 )] << save picture( dir || Char( (picno) + 1e6 ) || ".png" );
Wait( .01 );
);
上面的两个示例都可以轻松地(也许更容易)使用数据表的行来表示对象,使用列来表示方法的成员变量和 JSL 用户函数。 类的使用,尤其是没有任何子类化的情况下,如果其他人对 JSL 中的类感到不舒服,可能会使其更难遵循您的 JSL。
说到函数,DefineClass 的文档也提到了它们。 据我所知,它们几乎没有任何好处,应该避免。 我认为类的每个实例可能都会获得每个方法和每个成员变量的单独副本。 如果您定义一个 Function() 并将其存储在成员变量中,那么您只是在制作其他内容来复制该内容(也许)可能位于更全局的命名空间中,并且只有一个副本。 但是,如果只有一个(或很少)实例,并且您确实需要一个除了参数之外不知道任何内容的类函数,那么也许类中的函数就是您所需要的。
继续最后一个示例,让我们进行一些子类化并查看最后一个命名空间,极好的: ,我必须寻找它(请参阅 JSL 中的评论)。
直观的例子:基类可能是车辆,子类(或派生类)是卡车和汽车。 卡车可能会用知道避开住宅区街道的导航方法来取代基类导航方法。 汽车可能会利用 HOV 车道。 当被告知从 A 导航到 B 时,一系列车辆将执行正确的操作。
Define Class 有一个有趣的论点:基类(...) 。 在下面的例子中,形状将是基类并且正方形和圆圈将从形状导出。 该示例创建一个正方形和圆形列表并显示它们。
two squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filledtwo squares, two circles, two outlined, two filled两个正方形,两个圆形,两个轮廓,两个填充
以下描述基于反复试验;我相信它足够正确,可以成为预测某些事物如何运作的有用模型。
什么是极好的: ?
创建派生类时,JMP 运行到派生类的末尾基类链并返回顶部,首先创建基类,并在它们自己的命名空间中。 命名空间在内部链接在一起,以便派生类可以通过查找自己的命名空间来引用成员变量,然后沿着命名空间链向下到达基类,直到找到该变量。 这极好的:namespace 只是表示在名称空间链中向前跳一次,而不查找当前名称空间。 这就是为什么下面的 JSL 说“如果不需要,不要使用 super: 来调用基本方法”;基方法无法访问派生命名空间,因此它无法调用派生函数。
多重继承:如果您使用“接口”并避免重复名称,它可能会起作用。 祝你好运。 我不确定同级基类适合上面模型中的命名空间链。
// demonstrates JMP class inheritance and overriding a method
// and calling the super class. A circle and a square class
// are derived from a shape class. Explains when to NOT use super:.
//
// I found super: in https://github.com/sassoftware/jsl-hamcrest/blob/master/Source/Core/Matchers/EqualTo.jsl
// it isn't documented anywhere else, but works great for calling base class _init_
// Also, https://github.com/sassoftware/jsl-hamcrest/blob/master/Source/Core/Utils.jsl
// shows a "self" implementation idea. This is a bad idea because it creates a circular reference
// that must be cleared before the object is lost/deleted...or it will leak.
// avoid making members and methods in base and derived classes with the same name.
// _init_, _to string_, _show_ are exceptions, but they must not try to use a derived
// class member or method. (you would not normally want to, so no problem.)
//
// avoid needless upcast (super:); it cripples the base class because there is no downcast or
// virtual mechanism. Without the upcast, a baseclass function runs with access to the
// derived class members and methods. (see square's draw method calling docolor calling record.)
//
// it appears that the derived chain is searched *after* the current class; in a two class
// (base,derived) example that inverts the search order for members and methods when a
// derived class uses "super:" to call a base class method. If the base class calls a "virtual"
// function that exists in both base and derived, it won't get the derived one if "super:" was
// used but will if no scope is used. You only need "super:" typically when you want to call
// the base class method from an overridden method of the same name. If the base class method
// isn't calling back to derived via a virtual, everything is good.
// deep details
//
// the output shows lines like this:
// record square=square(shape=(s= x=30 y=30 r=5 s=4 c=yellow f=1))
// record circle=circle(shape=(s= x=30 y=60 r=10 s=99 c=green f=0))
//
// the square and the circle both get the right answer via different
// paths. Because of way draw() is overridden by square and not by circle,
// the initial call, objects[window:i] << draw(), is calling either a
// derived member or a base member. In both cases, "this" is for the
// derived member. in square's draw(), I made an error, now commented
// out: /*super:*/docolor(), that caused docolor to run with "this"
// for the base class, not the derived class. docolor then calls record().
// all three classes have record() members. The intent is to override the
// base class version ("never seen"). The error caused the base class
// version to run for square because "this" of docolor() was for the base class.
Define Class( "shape", // base class for both shapes
m_Xcenter = .;
m_Ycenter = .;
m_radius = .;
m_sides = .;
m_color = .;
m_filled = .;
_init_ = Method( {x, y, radius, sides, color, fill},
m_Xcenter = x;
m_Ycenter = y;
m_radius = radius;
m_sides = sides;
m_color = color;
m_filled = fill;
);
_to string_ = Method( {},
"shape=(s=" ||
" x=" || Char( m_Xcenter ) || " y=" || Char( m_Ycenter ) ||
" r=" || Char( m_radius ) || " s=" || Char( m_sides ) ||
" c=" || Char( m_color ) || " f=" || Char( m_filled ) || ")"
);
_show_ = _to string_;
// the following should NOT ever get called because square and circle override it
// try changing square to call super:docolor and then you'll see this
record=method({},show("never seen, overridden by square and circle"));
// both shapes understand filled vs outlined
// see note in square:draw method about calling docolor without super:
docolor = Method( {},
this:record(); // this: doesn't change anything,
If( this:m_filled, // it is mostly for disambiguating
Fill Color( /*this:*/m_color );// parameter of same name
, //
Pen Color( this:m_color )
)
);
// the base class has a draw method that circle uses;
// square provides an override because this draws a
// diamond-oriented square
draw = Method( {},
docolor();
p = J( m_sides, 3, 2 );//2==draw
p[1, 3] = 1;//1==move
p[m_sides, 3] = -2;//-2==draw and close
For( i = 1, i <= m_sides, i += 1,
p[i, 1] = m_Xcenter + m_radius * Cos( 2 * Pi() * i / m_sides );
p[i, 2] = m_Ycenter + m_radius * Sin( 2 * Pi() * i / m_sides );
);
Path( p, m_filled );
);
);
Define Class( "circle", baseclass( shape ),
_init_ = Method( {x, y, radius, color, fill},
super:_init_( x, y, radius, 99/*sides*/, color, fill )
);
_to string_ = Method( {},
"circle(" || super:_tostring_() || ")"
);
_show_ = _to string_;
record=method({},write("\!nrecord circle=",_show_()));
// circle has no override for draw()
);
Define Class( "square", baseclass( shape ),
_init_ = Method( {x, y, radius, color, fill},
super:_init_( x, y, radius, 4/*sides*/, color, fill )
);
_to string_ = Method( {},
"square(" || super:_tostring_() || ")"
);
_show_ = _to string_;
record=method({},write("\!nrecord square=",_show_()));
// square overrides the base class draw
draw = Method( {},
/*super:*/docolor();// don't up-cast, docolor can't down-cast and gets base class record()
Rect(
m_Xcenter - m_radius,
m_Ycenter - m_radius,
m_Xcenter + m_radius,
m_Ycenter + m_radius,
m_filled
);
);
);
objects = {};
Insert Into( objects, New Object( square( 30, 30, 5, "yellow", 1 ) ) );
Insert Into( objects, New Object( Circle( 30, 60, 10, "green", 0 ) ) );
Insert Into( objects, New Object( square( 60, 30, 15, "blue", 0 ) ) );
Insert Into( objects, New Object( Circle( 60, 60, 20, "red", 1 ) ) );
New Window( "objects",
gb = Graph Box( framesize( 500, 500 ),
For( window:i = 1, window:i <= N Items( objects ), window:i += 1,
//Show( objects[i] );
objects[window:i] << draw(); // uses the base class or the derived class draw
)
)
);
//stop();
// animate the color of object 4, the big red circle
For( i = 0, i <= 1, i += (1 / 256), //show(i);
objects[4]:m_color = HLS Color( i, .5, 1 );
gb << inval << updatewindow;
Wait( 0 );
);
关于什么这?
C++ 有一个名为“this”的值(其他语言使用“self”和“me”),有时对象需要它来引用自身。 与此不同:名称空间前缀。 我不认为 JSL 类在类中提供了“this”,您可能会想要创建这样的一个:
x = newObject(myClass(17));
x::self = x;
或类似的东西。 然后 myClass 方法可以使用“self”来执行需要类实例的操作,例如克隆。
Scripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instanceScripting index showing messages that can be sent to a class instance显示可以发送到类实例的消息的脚本索引
在包含类实例(指向)的类实例中保留成员变量可能会导致内存泄漏。 这给我们带来了一个新的话题……实际上是话题。
什么时候删除实例?
当没有对实例的引用时,该实例将被删除。 在上面的部分中,实例内的“self”引用将阻止实例被删除,即使您丢失了该实例的所有句柄。 除了关闭 JMP 之外,没有其他方法可以清理它。
什么时候复制实例?
这是最好的部分:只有使用 <这意味着实例是通过引用传递的。耶! 创建一个简单的类来包装大型矩阵并将该类的实例传递给用户函数,而不是让 JMP 将矩阵参数的副本复制到参数值中,并可能返回另一个副本,这样的方式就不那么笨拙。