内容简介:http://stackoverflow.com/questions/29073867/how-to-check-if-a-reference-to-procedure-is-nil
在以下示例代码中,对AssertTestObj()的调用会导致访问冲突.
Project InvokeTest2.exe raised exception class $C0000005 with message ‘access violation at 0x00000000: read of address 0x00000000’.
调试时,我可以看到TSafeCall<T> .Invoke()中的Assigned(NotifyProc)测试不能按预期的方式运行 – 所以Invoke()会尝试执行NotifyProc,否则会导致访问冲突.
任何想法为什么会失败,如何解决?
program InvokeTest2; {$APPTYPE CONSOLE} uses System.SysUtils; type TSafeCall<T> = class public type TNotifyProc = reference to procedure (Item: T); class procedure Invoke(NotifyProc: TNotifyProc; Item: T); overload; end; TOnObj = procedure (Value: String) of object; { TSafeCall<T> } class procedure TSafeCall<T>.Invoke(NotifyProc: TNotifyProc; Item: T); begin if Assigned(NotifyProc) then NotifyProc(Item); end; procedure AssertTestObj(OnExceptionObj_: TOnObj; Value_: String); begin TSafeCall<String>.Invoke(OnExceptionObj_, Value_); end; begin try TSafeCall<String>.Invoke(nil, 'works as expected'); AssertTestObj(nil, 'this causes an access violation!'); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
这是一个编译器错误.这是我简化的复制品:
{$APPTYPE CONSOLE} type TProc = reference to procedure; TOnObject = procedure of object; procedure Invoke(Proc: TProc); begin if Assigned(Proc) then Proc(); end; procedure CallInvokeOnObject(OnObject: TOnObject); begin Invoke(OnObject); end; begin Invoke(nil); // succeeds CallInvokeOnObject(nil); // results in AV end.
你可能会想我为什么简化.你的代码是一个很好的复制问题.不过,我想让它绝对尽可能简单,所以我真的可以确定问题是我认为的.所以我删除了泛型和类.
现在,使用Assigned的测试是正确的.你是对的,期望它会按照你的意图行事.问题是当编译器生成从CallInvokeOnObject调用Invoke的代码时,它需要在引用过程接口中包装对象的方法.为了正确地做到这一点,需要测试对象的方法是否被分配.如果没有,那么不应该创建包装器接口,并且Invoke应该被传递为零.
编译器无法做到这一点.它无条件地将对象的方法包装在引用过程接口中.您可以在为CallInvokeOnObject发出的代码中看到这一点.
Project1.dpr.16: begin // this is the beginning of CallInvokeOnObject 004064D8 55 push ebp 004064D9 8BEC mov ebp,esp 004064DB 6A00 push $00 004064DD 53 push ebx 004064DE 33C0 xor eax,eax 004064E0 55 push ebp 004064E1 683B654000 push $0040653b 004064E6 64FF30 push dword ptr fs:[eax] 004064E9 648920 mov fs:[eax],esp 004064EC B201 mov dl,$01 004064EE A1F4634000 mov eax,[$004063f4] 004064F3 E8DCDAFFFF call TObject.Create 004064F8 8BD8 mov ebx,eax 004064FA 8D45FC lea eax,[ebp-$04] 004064FD 8BD3 mov edx,ebx 004064FF 85D2 test edx,edx 00406501 7403 jz $00406506 00406503 83EAF8 sub edx,-$08 00406506 E881F2FFFF call @IntfCopy 0040650B 8B4508 mov eax,[ebp+$08] 0040650E 894310 mov [ebx+$10],eax 00406511 8B450C mov eax,[ebp+$0c] 00406514 894314 mov [ebx+$14],eax Project18.dpr.17: Invoke(OnObject); 00406517 8BC3 mov eax,ebx 00406519 85C0 test eax,eax 0040651B 7403 jz $00406520 0040651D 83E8E8 sub eax,-$18 00406520 E8DFFDFFFF call Invoke
对TObject.Create的调用是在引用过程接口中包含对象的方法.请注意,该接口是无条件创建的,然后传递给Invoke.
没有办法在Invoke内部解决这个问题.到代码到达那里的时候太晚了.您无法检测到该方法未分配.这应该报告给Embarcadero作为一个错误.
您唯一可行的解决方法是在CallInvokeOnObject中添加额外的分配检查.
http://stackoverflow.com/questions/29073867/how-to-check-if-a-reference-to-procedure-is-nil
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 强引用、软引用、弱引用、虚引用
- java的强引用、软引用、弱引用、幻象引用,引用队列总结
- Java 对象引用方式 —— 强引用、软引用、弱引用和虚引用
- 强引用、软引用、弱引用、幻象引用有什么区别?
- 你确定真的了解 Java 四种引用(强引用、弱引用、软引用、虚引用)了吗?
- java:强引用,软引用,弱引用和虚引用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。