ARC(iOS)下的Delphi TThread没有被释放

在ARCpipe理下使用Delphi for iOS终止线程的正确方法是什么?

以这个简单的例子:

TMyThread = class(TThread) protected procedure Execute; override; public destructor Destroy; override; end; TForm2 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private FThread: TMyThread; public end; { TMyThread } destructor TMyThread.Destroy; begin ShowMessage('Destroy'); inherited Destroy; end; procedure TMyThread.Execute; begin Sleep(5000); end; { TForm2 } procedure TForm2.Button1Click(Sender: TObject); begin FThread := TMyThread.Create(TRUE); FThread.FreeOnTerminate := TRUE; FThread.Start; end; procedure TForm2.Button2Click(Sender: TObject); begin ShowMessage(FThread.RefCount.ToString); end; procedure TForm2.Button3Click(Sender: TObject); begin FThread := nil; end; 

好的,按Button1会产生一个线程。 线程启动后,如果你点击Button2,它会显示3的RefCount值! 那么,1是我的FThreadvariables的引用,有2个额外的引用TThread在内部创build…我已经挖掘到源代码,发现在这里增加了RefCount:

 constructor TThread.Create(CreateSuspended: Boolean); ErrCode := BeginThread(nil, @ThreadProc, Pointer(Self), FThreadID); if ErrCode <> 0 then raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(ErrCode)]); {$ENDIF POSIX} 

和这里:

 function ThreadProc(Thread: TThread): Integer; var FreeThread: Boolean; begin TThread.FCurrentThread := Thread; 

那么…线程完成后(在我的情况下,5秒后),RefCount将减less到2(因为我已经将FreeOnTerminate设置为TRUE,但是如果我不将FreeOnTerminate设置为TRUE,则RefCount仍然是3 )。

看到问题? 如果我调用FThread := nil ,那么线程永远不会被调用,析构函数也不会被调用,那么RefCount应该从2减less到1(或者在FreeOnTerminate = FALSE情况下从3减less到2),线程永远不会被释放。 。

也许我错过了一些东西,因为我习惯了没有ARC的线程…所以,我在这里错过了什么? 或者在ARC下的TThread实现中有错误?

也许这个TThread的定义

 private class threadvar FCurrentThread: TThread; 

应该是这样的

 private class threadvar [Weak] FCurrentThread: TThread; 

经过一些挖掘qc以下问题和解决方法显示:

线程参数应该作为const传递

 function ThreadProc(Thread: TThread): Integer; <<-- pass_by_reference pushes var up the ref_count. FreeThread: Boolean; begin TThread.FCurrentThread := Thread; 

如果你将它作为const传递,那么ref_count将不会达到3.通常这不是问题,因为函数退出时ref_count减less了,但在这里:

函数epilog从不执行,因为pthread_exit()跳出代码 。

这只是解决scheme的一部分,但还需要做更多的工作。

由Dave Nottage完全解决方法

经过很多的摆弄之后,我想出了这个潜在的解决方法:

使这些MODS的类单位:更改:

 function ThreadProc(Thread: TThread): Integer; 

至:

 function ThreadProc(const Thread: TThread): Integer; 

并添加:

 TThread.FCurrentThread := nil; 

在这一行之后:

 if FreeThread then Thread.Free; 

覆盖TThread后裔中的DoTerminate ,因此:

 procedure TMyThread.DoTerminate; begin try inherited; finally __ObjRelease; end; end; 

这样调用线程:

 FMyThread.Free; // This will do nothing the first time around, since the reference will be nil FMyThread := TMyThread.Create(True); // DO NOT SET FreeOnTerminate FMyThread.OnTerminate := ThreadTerminate; FMyThread.Resume; 

这(至less对我来说,在设备上)导致线程在随后的调用中被销毁。

注意:在ARC条件下, 永远不要在本地声明对线程的引用,因为当它超出作用域时,线程被销毁,Execute方法永远不会被调用,更不用提其它的问题了。