DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: sprblck
今日帖子: 14
在线用户: 14
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 0:12:00
标题:
TList<T> 对象引用的不彻底性困惑 浏览:2821
加入我的收藏
楼主: procedure TForm2.Button2Click(Sender: TObject);
var
  BTN1, BTN2: TBUTTON;
begin
  BTN1 := TBUTTON.CREATE(SELF);
  BTN1.Name := '123';
  BTN2 := BTN1;


  ShowMessage(btn2.Name );   // 等于123, btn2不需要创建,直接引用 btn1;


  FreeAndNil(BTN2);
  if BTN1 = NIL then       // 这句话不执行,说明 btn1 没有被 free;
    ShowMessage('NIL BTN1');

  ShowMessage(BTN1.Name);  //但是这里 显示空,说明 btn1 本身虽然没有被消除,但是他的属性全部清除了,成了光杆司令
end;

end.


用下面指针应用的方法就彻底:

procedure TForm2.Button1Click(Sender: TObject);


var
  BTN1: TBUTTON;
  pBTN1:Pointer;
begin
 BTN1 := TBUTTON.CREATE(SELF);
  BTN1.Name := 'bbb123';


  pBTN1 := @BTN1;

  ShowMessage(TBUTTON(pBTN1^).Name );   // 等于123, pBTN1 作为指针 ,彻底引用;
  FreeAndNil(pBTN1^);
  if BTN1 = NIL then       // 这句话执行了,说明 PBTN1^ 彻底 和 BTN1 一样了;
    ShowMessage('NIL BTN1');

  ShowMessage(BTN1.Name);  // 报错,说明 btn1 真的被释放了;
end;
----------------------------------------------
青云论坛
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 0:35:54
1楼: 实际上我困惑的问题是下面的处理方法:


procedure TForm2.Button3Click(Sender: TObject);
var
   ListBtn:TList<TButton>;
   tmpBtn:TButton;
   i:Integer;
begin
   ListBtn:=TList<TButton>.Create;

   //加载100个按钮
   for i := 1 to 100 do
   begin
     tmpBtn:=TButton.Create(nil);
     ListBtn.Add(tmpBtn)
   end;

   //list释放100个按钮
   for i := 1 to 100 do
   begin
      tmpBtn:=ListBtn[0];
      FreeAndNil(tmpBtn); //这个不能算彻底清除吧?
      if ListBtn[0]=nil then
       begin
         ShowMessage('ok' );   //根本不执行
       end;
          ListBtn.Delete(0); // 由于ListBt[0] 不是 nil   ,所以清除不彻底
   end;


end;

我有个服务程序,7*24小时跑的,里面大量使用了TList ; 我就用了这个方法做delete,怀疑释放的不彻底,虽然我观察内存没变化,但是发现运行几天后,关闭整个程序很慢;

  直接free Tlist里的对象是不可以的;
  比如执行: FreeAndNil(ListBtn[i]); 这个会报错的;
  所以要remove 掉 Tlist的某一个对象,Tlist本身是不可以的,
  必须通过变量中转,用指针我也试过,tlsit的对象,不可以用指针中转;
  

当然,有人说,如果是 TList<class> ,要变成  TObjectList<class> ;
但是我看介绍, TObjectList是在整个 list自身free的时候,
通过notify ,全部释放里面对象;

对于list的单个 delete 或 remove ,TObjectList并没有释放里面对象;
----------------------------------------------
青云论坛
作者:
男 bdl1 (bdl1) ▲▲▲▲▲ -
普通会员
2016/7/21 6:07:35
2楼: 我只能说,Notify机制,是线程不安全的。
----------------------------------------------
-我的博客
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 6:29:46
3楼: bdl1兄说的不错
(您的新浪博客不错,对线程池很感兴趣,
http://blog.sina.com.cn/s/blog_44fa172f0102w4j4.html
这个感觉太简单了,打算好好研究一下,不过一直用d10up1版本,不知道可用否 )
我也觉得,自己控制好,没有必要用 TObjectList<class>;
就用原生的TList<class>,感觉好控制;

现在的问题很简单,就是在TList<class>释放某一个的时候,

不能直接 TList<class>.Delete(3) ;要先把这个class作为变量free掉,
才能删除,否者这个class就不释放;

但是这个class没有办法彻底的让它free掉;
----------------------------------------------
青云论坛
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 7:04:45
4楼: 看了 TObjectList<class>的Notify的源码:

procedure TObjectList<T>.Notify(const Value: T; Action: TCollectionNotification);
begin
  inherited;
  if OwnsObjects and (Action = cnRemoved) then
    Value.DisposeOf; //这个源码有点复杂,看不懂;
end;

其实我就一个目的;如何在remove掉TList<class>某个值的时候,把里面的class
彻底释放掉;

就算用TObjectList<class>能解决,也有局限性;


比如TList<record>;就不能变成TObjectList<record>

这个record里某个成员是class,比如 record.A 是个class;

你还是没法测底释放。
----------------------------------------------
青云论坛
作者:
男 wzwcn (wzw) ★☆☆☆☆ -
普通会员
2016/7/21 9:00:13
5楼: 只能说楼说对FreeAndNil及指针或对象变量(也是指针)赋nil理解不清。其实freeandnil所作的操作,只是先free,再把变量赋nil而已。第一个例子:
var btn1,btn2:TObject;

btn1:=Txx.create;
btn2:=btn1;
freeandnil(btn1);
为什么btn2不会一起变成nil?因为btn1和btn2是两个独立的变量,你可以把它们理解为两个整型变量,而它们原来保存同样的数字(即对象实例的地址),freeandnil(btn1)后,对象实例地址的对象被释放了,btn1也被赋为了0(nil),而这时btn2还保存原来的对象实例的地址呢(不为0,即不为nil)...你下面的例子也一样。
   另外,不要认为对象变量或指针变量不为nil就不是彻底释放,你只需要把对象或指针释放就就是彻底了,至于变量赋不赋nil,那只是方便你自己,以防自己用错而已。因为你定义的变量本身过了作用域就自动释放了
----------------------------------------------
-
作者:
男 go_on (go_on) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 9:22:54
6楼: 5楼讲的很好,支持,很少看到技术讨论帖了。楼主担心内存溢出,可以打开溢出监控,看看是否有溢出,或者用内存监控工具。
----------------------------------------------
-
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 9:24:41
6楼: to: wzwcn;我也跟你理解差不多;

但是有些细节还是有点费解.既然free了,为什么另一个还能访问属性;
按你的例子:
 var btn1,btn2:TObject;

btn1:=Txx.create;
btn2:=btn1;
freeandnil(btn1);

这里btn1 和btn2 可以理解成对象的地址;
无论freeandnil掉其中之一,实际的对象都被释放了;

但是 你用 showmessage(btn1.name)不会报错;
用showmessage(btn2.name)会报错;

也就是说btn2作为一个handle整数值变量,他不会被释放;

在做TList处理的时候,频繁的插入删除;
就相当于定义了无数个整型handle地址变量;
虽然指向的对象为空,但他本身是暂用空间的;

或者这么说:
 var
    btn1:Tbutton;
begin
    showmessage(btn1.name) ; 这个会报错,说明btn1不占用空间
end;

换成:
 var
    btn1,btn2:Tbutton;
begin
   btn2:=Tbutton.create(nil);
    btn1:=btn2;
     btn2.name:='123';
    freeandnil(btn2);
    showmessage(btn1.name) ; 这个不报错,说明btn1 占用空间,当然这里btn1.name显示为空
end;
----------------------------------------------
青云论坛
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 9:27:57
7楼: to:go_on

7*24小时,运行1个月,内存没有明显变化;

tList不停的高速插入删除对象;

但是引进对象居然能否被访问属性,这个理解不了;
应该报错才对,说明引用对象不存在;
----------------------------------------------
青云论坛
作者:
男 wzwcn (wzw) ★☆☆☆☆ -
普通会员
2016/7/21 9:35:14
8楼: 不报错并不说明那个对象还在,读一块释放了的内存的结果是未定的,可能报错也可能不报错。free后,就是释放了对那块内存的所有权,但如果还没有被别的地方写,那数据还在。就是硬盘一样,删除一个文件只是把那地方标记为可写,还没被覆盖之前数据还在
----------------------------------------------
-
作者:
男 bdl1 (bdl1) ▲▲▲▲▲ -
普通会员
2016/7/21 9:46:24
9楼: tList不停的高速插入删除对象;
我怀疑问题就出在这里的高速:
确认两个问题:
1.这个对象是什么对象?是不是基于TComponent来的?
2.他的Owner是谁?

如果是,那么就出在Notify机制上。然后的的解决办法就是在删除这个对象时,加锁来保护。
----------------------------------------------
-我的博客
作者:
男 drroc (mvcxe) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 10:19:32
10楼: delphi没有java/c#那样的完整回收机制,有时有些东西是要手工回收,但大部份对象出了作用域,delphi又会回收那些资源,有时真的是比较困惑的。

特别是要写7*24不间断运行的程序,我的意见是,不管delphi回不回收,你能想到要回收的都自己回收,只要是create过的,都要free,别图省事,大量使用try finally释放

如果一般的小程序,就让delphi自己的机制处理,完全不做回收其实也没啥事:)
----------------------------------------------
MVCXE中国首个DELPHI MVC WEB框架:https://www.mvcxe.com/
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 10:21:43
10楼: to wzwcn: 这个说法虽然有点道理,但是能够访问的空对象,没有被彻底去掉,怪怪的;

to bdl1 :是自己class,没有继承; 您就以上面的button class作为例子来分析就行了,一样的;


to drroc : 作为服务程序,肯定是都会手动回收的,现在问题,就是对于Tlist<class>里的每个item,没法直接回收,你用 freeandnil(tlist[1])会报错的, 只有先定义一个class的变量等于tlsit[1],然后free这个变量;但是tlist[1] 不会变成nil ;
----------------------------------------------
青云论坛
作者:
男 hs_kill (lzl_17948876) ★☆☆☆☆ -
普通会员
2016/7/21 10:30:02
11楼: 关于btn2 := btn1 然后btn2.free以后btn1能访问的问题 这个涉及到类变量本质是什么

实际上你可以简单的吧btn2和btn1理解成2个指针 实例实际数据是另外一段地址, 2个btn指针都指向的同一个地址
{这是简单的说法, 方便理解, 实际上类变量与实例的关系比这个要复杂一些, 但是原理是一样的}
而btn2 := btn1的操作实际上是值赋值 也就是说 btn2和btn1只是2个完全无关的指针, 他们的指针值是相同的
这样freeandnill(btn2)的时候实际上释放的是实际数据地址, 然后再吧指针本身置空
所以btn2被释放了 而btn1这个指针仍然存在, 指向的实际数据的地址值仍然存在

在访问的时候, 没有报错是因为delphi在内存释放的时候, 并不是直接释放给windows而是记录该内存不使用, 也就是说 对于系统来说, 这段内存没释放, 只是程序记录可以被其他地方使用了而已, 另外, 指针值的访问是不做检查的, 所以再用btn1.name访问的时候自然不会报错(btn1.name的方式实际上相当于从btn1的内存起始地址加上name位置的偏移量位置来读内容), 因为内存地址仍然是程序本身拥有

至于读不出内容, 是因为释放的时候内存块中的值被清空了

而pbtn1 := @btn1 相当于你做了一个btn1这个指针的指针 而你freeandnil的是pbtn1^这样其实就是freeandnil(btn1) btn1指针本身被释放了, 那么你再访问自然就报空指针错了
----------------------------------------------
http://www.cnblogs.com/lzl_17948876/
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 10:43:29
12楼: TO:  hs_kill 

您说的比较深入浅出,我也想用指针的方法处理Tlist<class>的彻底释放;
但是不行,就下面这个例子;有什么好的方法测底释放;

虽然,不做彻底释放,好像也没事;但是最好安稳一些,彻底释放最安全;

procedure TForm2.Button3Click(Sender: TObject);
var
   ListBtn:TList<TButton>;
   tmpBtn:TButton;
   i:Integer;
begin
   ListBtn:=TList<TButton>.Create;

   //加载100个按钮
   for i := 1 to 100 do
   begin
     tmpBtn:=TButton.Create(nil);
     ListBtn.Add(tmpBtn)
   end;

   //list释放100个按钮
   for i := 1 to 100 do
   begin
      tmpBtn:=ListBtn[0];
      FreeAndNil(tmpBtn); //这个不能算彻底清除吧?
      if ListBtn[0]=nil then
       begin
         ShowMessage('ok' );   //根本不执行
       end;
          ListBtn.Delete(0); // 由于ListBt[0] 不是 nil   ,所以清除不彻底
   end;


end;
----------------------------------------------
青云论坛
作者:
男 drroc (mvcxe) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 10:56:18
12楼: 我个人会这样写,没楼主这样的深究精神,我的习惯是尽量保证所有Create过的东西都Free过

var
   ListBtn:TList<TButton>;
   tmpBtn:TButton;
   i:Integer;
begin
   ListBtn:=TList<TButton>.Create;
   try
    //加载100个按钮
    for i := 1 to 100 do
    begin
      tmpBtn:=TButton.Create(nil);
      ListBtn.Add(tmpBtn)
    end;

    //list释放100个按钮
    for i := 1 to 100 do
    begin
      ListBtn[i-1].Free;
    end;
   finally
     ListBtn.Free;
   end;
end;
----------------------------------------------
MVCXE中国首个DELPHI MVC WEB框架:https://www.mvcxe.com/
作者:
男 drroc (mvcxe) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 10:58:28
13楼: 大部份直接.Free,有时也会用FreeAndNil,心情好的时候Free完再:=nil

例如上面的ListBtn.Free;,按理他应该能将他自已100个节点所占用的资源free掉的,也就你所说的delete(i),如果TList<T>自己的Free有bug或写得不完善,那我也不可能什么事都自己来一遍
----------------------------------------------
MVCXE中国首个DELPHI MVC WEB框架:https://www.mvcxe.com/
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 11:24:52
14楼: to drroc:

 直接TList<T>.delete(i) 肯定不会free掉item的;
 就相当于一个人中箭了,你就把外面的部分剪掉;
 不过据说用 :TObjectList 就可以了,不过 TObjectList <T>中的T必须是class;



 
网上有个高手提供的检查方法,等会试试:
 

DebugHook 与 ReportMemoryLeaksOnShutdown 都是 System 下的变量.

DebugHook: 程序中母体中运行时, 此值是 1; 独立运行时, 它是 0.



在程序的任何地方加上一句 ReportMemoryLeaksOnShutdown := True; 
这样程序在

关闭时可检测是否有内存泄漏.

如果这样使用: 
ReportMemoryLeaksOnShutdown := Boolean(DebugHook);
则程序只会在调试时报内存泄漏.
----------------------------------------------
青云论坛
作者:
男 simple8848 (jack) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 11:37:20
15楼:    //list释放100个按钮
   for i := 0 to ListBtn.Count-1  do
   begin
      ListBtn[i].Free;
      ListBtn[i] := nil;//不做也行,前面已经释放了对象
   end;
----------------------------------------------
-
作者:
男 drroc (mvcxe) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 11:48:54
16楼: @qingyun

一,请看12楼我的代码,我没做delete,13楼说的意思是ListBtn.Free就做了delete了,正因为delete不能释放他所带的对象,所以才有上面的100个循环Free

二,FastMM是个delphi老手都知道的东东。
----------------------------------------------
MVCXE中国首个DELPHI MVC WEB框架:https://www.mvcxe.com/
作者:
男 drroc (mvcxe) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 11:50:51
17楼: @simple8848 李维有一篇博文提倡用FreeAndNil,不能用FreeAndNil的地方也要用.Free然后:=nil
----------------------------------------------
MVCXE中国首个DELPHI MVC WEB框架:https://www.mvcxe.com/
作者:
男 qingyun (qingyun) ★☆☆☆☆ -
盒子活跃会员
2016/7/21 12:16:28
18楼: @drroc  多谢,之前没细看

主要是list 不能用 freeandnil;
但是可以 .free 却可以;自从用了freeandnil 后,就想不起来用free了;



 //list释放100个按钮
   for i := 0 to ListBtn.Count-1  do
   begin
      ListBtn[i].Free;
      ListBtn[i] := nil;//不做也行,前面已经释放了对象
   end;

我稍微修改一下,list本身也要释放的;

   for i := 0 to ListBtn.Count-1  do
   begin
      ListBtn[0].Free;
     //  ListBtn[0] := nil;//不做也行,前面已经释放了对象
      if ListBtn[0]=nil  //上面这句不写,不知道会不会是nil,等会试试
          showmessage('free ok'); 
      ListBtn.delete(0);  //删除第一个,第二个就变成第一个;
   end;
----------------------------------------------
青云论坛
作者:
男 bmsr (白忙剩人) ★☆☆☆☆ -
普通会员
2016/7/21 14:33:04
19楼:
   for i := 0 to ListBtn.Count-1  do     freeandnil(ListBtn[i]);
   ListBtn.clear;

----------------------------------------------
http://blog.sina.com.cn/bmsrnote
作者:
男 hs_kill (lzl_17948876) ★☆☆☆☆ -
普通会员
2016/7/21 16:14:13
20楼: To qingyun:
   for i := 1 to 100 do
   begin
      tmpBtn:=ListBtn[0];
      FreeAndNil(tmpBtn); //这个不能算彻底清除吧?
      if ListBtn[0]=nil then
       begin
         ShowMessage('ok' );   //根本不执行
       end;
          ListBtn.Delete(0); // 由于ListBt[0] 不是 nil   ,所以清除不彻底
   end;

这种释放是没问题的, 已经全部都释放掉了
至于你担心的ListBtn[0]<>nil的问题也不是问题 因为ListBtn自身释放的时候会吧内部元素占用的空间释放掉

FreeAndNil(tmpBtn)释放的是button数据空间ListBtn.free释放的是元素占用的空间, 至于为什么不会有泄漏, 参考我之前回复的信息

最后的ListBtn.Delete(0);不要放到循环里调用, 或者你使用倒序循环, 否则循环到一半的时候会报越界错误

一般调用模式:
   for i := 0 to ListBtn.count - 1 do
   begin
      tmpBtn:=ListBtn[i];
      tmpBtn.free;
   end;
   FreeAndNil(ListBtn);
----------------------------------------------
http://www.cnblogs.com/lzl_17948876/
作者:
男 wk_knife (wk_knife) ★☆☆☆☆ -
盒子活跃会员
2016/7/22 8:49:56
21楼: >>直接free Tlist里的对象是不可以的;
>>比如执行: FreeAndNil(ListBtn[i]); 这个会报错的;
>>所以要remove 掉 Tlist的某一个对象,Tlist本身是不可以的,
>>必须通过变量中转,用指针我也试过,tlsit的对象,不可以用指针中转;

难道非要用FreeAndNil, FreeAndNil是编译期报错吧?!
Listbtn[i].free;
listbtn[i]:=nil;
listbtn.delete(i);
分三句写不就没事了。
----------------------------------------------
-
作者:
男 wk_knife (wk_knife) ★☆☆☆☆ -
盒子活跃会员
2016/7/22 8:54:09
22楼: >>20楼
>>最后的ListBtn.Delete(0);不要放到循环里调用, 或者你使用倒序循环, 否则循>>>环到一半的时候会报越界错误

>>一般调用模式:
   for i := 0 to ListBtn.count - 1 do
   begin
      tmpBtn:=ListBtn[i];
      tmpBtn.free;
>>   end;

用While循环最省。

  while listbtn.count>0 do
  begin
    listbtn[0].free;
    listbtn.delete(0);
  end;
----------------------------------------------
-
作者:
男 vmao (毛小毛) ★☆☆☆☆ -
盒子活跃会员
2016/7/22 9:35:18
23楼: 楼主也是论坛老人了,真是一个很“搞”的人。不过人总有“灯下黑”的时候。

告诉你一个秘密:
对象即指针,free执行后,会释放掉对象占用的空间,但不会立即释放这个对象指针本身,所以这个指针并不为空,那么平常我们直接写free,不赋值nil会不会造成内存泄漏呢?其实是不会的,如下面的代码,Delphi编译器在过程结束的时候会自动释放基本类型(指针,数值,字符串,结构等),字符串什么的基本类型,我们直接定义就能用,从来不释放,这些基本类型的释放编译器就处理掉了,我们不用关心,对象指针也是基本类型,楼主的问题是你在过程还没结束的时候,就判断过程内的指针当然还是存在的哦,如果你很着急,当然就需要手工赋nil。

至于list里的内容,里面存的是啥,它只占用了一个指针空间,不管有没有free,不管有没有nil,改咋处理还是咋处理就是了:
var
  BTN1: TBUTTON;
begin
 BTN1 := TBUTTON.CREATE(SELF);
 BTN1.free;
end;
----------------------------------------------
-
作者:
男 akay (akay) ★☆☆☆☆ -
普通会员
2016/7/22 10:13:18
24楼: hs_kill的解释是最清晰的。
12楼drroc是正确的写法。
----------------------------------------------
-
作者:
男 hexi (Hexi) ★☆☆☆☆ -
盒子活跃会员
2016/7/24 16:05:47
25楼: var
  F:TObjectList<TButton>;
begin
  F:=TObjectList<TButton>.Create(True);
  F.Add(Button1);
  F.Free; //同时释放Button1
end;
----------------------------------------------
-
作者:
男 hexi (Hexi) ★☆☆☆☆ -
盒子活跃会员
2016/7/24 16:13:53
26楼: var
  [weak]B:IInterfaceComponentReference;
begin
  B:=Button1;
  TButton(B.GetComponent).Caption:='Hello';
  Button1.Free;  //B同时也变为nil
  if assigned(B) then
    ShowMessage('B is nil');

end;

上面程序需要在Delphi 10.1 Berlin及之后版本下才会输出"B is nil"
----------------------------------------------
-
作者:
男 hexi (Hexi) ★☆☆☆☆ -
盒子活跃会员
2016/7/24 16:28:34
27楼: type
  TInterfaceItem=class
    [weak]Value:IInterfaceComponentReference;
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  BA:array[0..99] of TButton;
  Item:TInterfaceItem;
  L:TObjectList<TInterfaceItem>;
  i: Integer;
  B:Boolean;
begin
  L:=TObjectList<TInterfaceItem>.Create(True);
  for i := 0 to 99 do
  begin
    BA[i]:=TButton.Create(Self);
    Item:=TInterfaceItem.Create;
    Item.Value:=Ba[i];
    L.Add(Item)
  end;

  for i := 0 to 99 do
    BA[i].Free;

  B:=False;

  for i := 0 to 99 do
    B:=B or Assigned(L[i].Value);

  if not B then
    ShowMessage('All Buttons in L are nil');
  L.Free;
end;

上面程序需要在Delphi 10.1 Berlin及之后版本下才会输出"'All Buttons in L are nil"
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行93.75毫秒 RSS