Empty Functions - Placeholder? - delphi

so I was just looking through the Code of our inventory management system
and I saw some Snippet's of one of my Coworkers, all he does in some Functions is
simply open them and insert a command in there for example
procedure TWerkStF.TBBtnStatiClick(Sender: TObject);
begin
inherited;
//
end;
so i am wondering when you should do something like that and when is it usefull, are there some benefits?

This is not useful, and has no benefits. Such a function can, and should, be removed.
The function in the question, by using inherited, simply searches in the super classes for a function of the same name, and if one is found calls it. If one is not found then no action is performed. As a rule, such a function, one that only calls inherited, does not modify the behaviour of the program. You can remove it without changing behaviour.

We use such functions/procedures most times to tell other Co-Workers where they should not change anything, the reason for that is because some events are just not fitting the needs of some Modules, therefore we "warn" each other with empty Functions.
Sounds wiered, i know.

Related

How to check if unit and function exists?

I'm using Delphi 11. Is there a way to run a specific function inside a TDataModule, but only if this datamodule exists on my project ?
I need a way to check if the datamodule exists and call the function as string, because some projects will have this datamodule added, others not.
Instead of this :
myDataModule.myfunction(param);
Would be something like this imaginary code :
if TDataModule(findcomponent('myDataModule')) <> nil then
TDataModule(findcomponent('myDataModule')).call('myfunction','param');
A lightweight solution could be Messages (those from System.Messaging).
Declare a new TMessage descendant holding the parameters needed for the call
When the datamodule is created it Subscribes to this message type with a handler doing the actions.
Instead of searching for the datamodule just Send the message.
This may or may not be applicable to your situation... But that seems like the kind of situation I would solve with a multicast event.
Your code would call Event.Execute('Param') instead of myDataModule.myfunction(param).
The part of your code responsible for "importing" and creating the datamodule would register to the event. Something like :
EventClient := TEventClient.create(
procedure (const AParam : ParamType)
begin
myDataModule.myfunction(AParam)
end);
EventClient.Observe(Event);
Now, this approach makes sense if the reasons to call your function is "event-like", but this might make less sense in other contexts.
As for library implementing multicast event, I can't recommend any as I always used a proprietary one. If you need inspiration to implement your own, you can look at TMultiCaster in unit Vcl.AppEvnts which implements more or less the same concept.

How to debug a (possible) RTL problem?

I'm asking this because I'm out of good ideas...hoping for someone else's fresh perspective.
I have a user running our 32-bit Delphi application (compiled with BDS 2006) on a Windows 7 64-bit system. Our software was "working fine" until a couple weeks ago. Now suddenly it isn't: it throws an Access Violation while initializing (instancing objects).
We've had him reinstall all our software--starting all over from scratch. Same AV error. We disabled his anti-virus software; same error.
Our stack tracing code (madExcept) for some reason wasn't able to provide a stack trace to the line of the error, so we've sent a couple error logging versions for the user to install and run, to isolate the line which generates the error...
Turns out, it's a line which instances a simple TStringList descendant (there's no overridden Create constructor, etc.--basically the Create is just instancing a TStringList which has a few custom methods associated with the descendant class.)
I'm tempted to send the user yet another test .EXE; one which just instances a plain-vanilla TStringList, to see what happens. But at this point I feel like I'm flailing at windmills, and risk wearing out the user's patience if I send too many more "things to try".
Any fresh ideas on a better approach to debugging this user's problem? (I don't like bailing out on a user's problems...those tend to be the ones which, if ignored, suddenly become an epidemic that 5 other users suddenly "find".)
EDIT, as Lasse requested:
procedure T_fmMain.AfterConstruction;
begin
inherited;
//Logging shows that we return from the Inherited call above,
//then AV in the following line...
FActionList := TAActionList.Create;
...other code here...
end;
And here's the definition of the object being created...
type
TAActionList = class(TStringList)
private
FShadowList: TStringList; //UPPERCASE shadow list
FIsDataLoaded : boolean;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
procedure DataLoaded;
function Add(const S: string): Integer; override;
procedure Delete(Index : integer); override;
function IndexOf(const S : string) : Integer; override;
end;
implementation
procedure TAActionList.AfterConstruction;
begin
Sorted := False; //until we're done loading
FShadowList := TStringList.Create;
end;
I hate these kind of problems, but I reckon you should focus on what's happening recently BEFORE the object tries to get constructed.
The symptoms you describe sound like typical heap corruption, so maybe you have something like...
An array being written to outside bounds? (turn bounds checking on, if you have it off)
Code trying to access an object which has been deleted?
Since my answer above, you've posted code snippets. This does raise a couple of possible issues that I can see.
a: AfterConstruction vs. modified constructor:
As others have mentioned, using AfterConstruction in this way is at best not idiomatic. I don't think it's truly "wrong", but it's a possible smell. There's a good intro to these methods on Dr. Bob's site here.
b: overridden methods Add, Delete, IndexOf
I'm guessing these methods use the FshadowList item in some way. Is it remotely possible that these methods are being invoked (and thus using FShadowList) before the FShadowList is created? This seems possible because you're using the AfterConstruction methods above, by which time virtual methods should 'work'. Hopefully this is easy to check with a debugger by setting some breakpoints and seeing the order they get hit in.
You should never override AfterConstruction and BeforeDestruction methods in your programs. They are not meant for what you're doing with them, but for low-level VCL hacking (like reference adding, custom memory handling or such).
You should override the Create constructor and Destroy destructor instead and put your initialization code here, like such:
constructor TAActionList.Create;
begin
inherited;
// Sorted := False; // not necessary IMHO
FShadowList := TStringList.Create;
end;
Take a look at the VCL code, and all serious published Delphi code, and you'll see that AfterConstruction and BeforeDestruction methods are never used. I guess this is the root cause of your problem, and your code must be modified in consequence. It could be even worse in future version of Delphi.
Clearly there is nothing suspicious about what TAActionList is doing at time of construction. Even considering ancestor constructors and possible side-effects of setting Sorted := False indicate there shouldn't be a problem. I'm more interested in what's happening inside T_fmMain.
Basically something is happening that causes FActionList := TAActionList.Create; to fail, even though there is nothing wrong in the implementation of TAActionList.Create (a possibility is that the form may have been unexpectedly destroyed).
I suggest you try changing T_fmMain.AfterConstruction as follows:
procedure T_fmMain.AfterConstruction;
begin
//This is safe because the object created has no form dependencies
//that might otherwise need to be initialised first.
FActionList := TAActionList.Create;
//Now, if the ancestor's AfterConstruction is causing the problem,
//the above line will work fine, and...
inherited AfterConstruction;
//... your error will have shifted to one of these lines here.
//other code here
end;
If an environment issue with a component used by your form is causing it destroy the form during AfterConstruction, then it's the assignment of the new TAActionList.Create instance to FActionList that's actually causing the AV. Another way to test would be to first create the object to a local variable, then assign it to the class field: FActionList := LActionList.
Environment problems can be subtle. E.g. We use a reporting component which we discovered requires that a printer driver is installed, otherwise it prevents our application from starting up.
You can confirm the destruction theory by setting a global variable in the form's destructor. Also you may be able to output a stack trace from the destructor to confirm the exact sequence leading to the destruction of the form.
Our software was "working fine" until a couple weeks ago... suddenly become an epidemic that 5 other users suddenly "find".) :
Sounds like you need to do some forensic analysis, not debugging: You need to discover what changed in that user's environment to trigger the error. All the more so if you have other users with the same deployment that don't have the problem (sounds like that's your situation). Sending a user 'things to try' is one of the best ways to erode user confidence very quickly! (If there is IT support at the user site, get them involved, not the user).
For starters, explore these options:
*) If possible, I'd check the Windows Event Log for events that may have occurred on that machine around the time the problem arose.
*) Is there some kind of IT support person on the user's side that you can talk to about possible changes/problems in that user's environment?
*) Was there some kind of support issue/incident with that user around the time the error surfaced that may be connected to it, and/or caused some kind of data or file corruption particular to them?
(As for the code itself, I agree with #Warran P about decoupling etc)
Things to do when MadExcept is NOT Enough (which is rare, I must say):
Try Jedi JCL's JCLDEBUG instead. You might get a stack traceback with it, if you change out MadExcept for JCLDEBUG, and write directly the stack trace to the disk without ANY UI interaction.
Run a debug-viewer like MS/SysInternals debugview, and trace output things like the Self pointers of the objects where the problems are happening. I suspect that somehow an INVALID instance pointer is ending up in there.
Decouple things and refactor things, and write unit tests, until you find the really ugly thing that's trashing you. (Someone suggested heap corruption. I often find heap corruption goes hand in hand with unsafe ugly untested code, and deeply bound UI+model cascading failures.)

What is the correct way to dynamically create/release runtime forms?

I always try to create my Applications with memory usage in mind, if you dont need it then don't create it is the way I look at it.
Anyway, take the following as an example:
Form2:= TForm2.Create(nil);
try
Form2.ShowModal;
finally
Form2.FreeOnRelease;
end;
I actually think Form2.Destroy is probably the better option, which brings me to my question..
What is the difference between calling:
Form2.Destroy;
Form2.Free;
Form2.FreeOnRelease;
They all do the same or similar job, unless I am missing something.
And also when should any of the above be used? Obviously when freeing an Object I understand that, but in some situations is Destroy better suited than Free for example?
Form2:= TForm2.Create(nil);
This is a code-smell, because Form2 is probably the global, IDE-generated variable that would normally hold an IDE-created TForm2. You most likely want to use a local variable, and one with a better name. This is not necessary an error, just a code-smell.
Form2.Destroy vs Form2.Free
Use Form2.Free, because it calls Destroy anyway. You can CTRL+Click on the name (Free) to see it's implementation. Essentially Free calls Destroy if Self is not nil.
Form2.FreeOnRelease
As the documentation says, "It should not be necessary to call FreeOnRelease directly."
I've never actually heard of FreeOnRelease before. A quick Google search turned up the reason why. From the official documentation:
FreeOnRelease is called when an
interface implemented by the component
is released. FreeOnRelease is used
internally and calls the corresponding
interface method. It should not be
necessary to call FreeOnRelease
directly.
As for Free vs. Destroy, Free is a safety feature. It's basically implemented as if self <> nil then self.Destroy;, and it was created to make constructors and destructors safe to use. Here's the basic idea:
If you're constructing an object and an unhandled exception is raised, the destructor gets called. If your object contains other objects, they may or may not have been created yet by the time the error occurred, so you can't just try to call Destroy on all of them. But you need a way to make sure that the ones that have been created do get destroyed.
Since Delphi zeros out the address space of an object before calling the constructor, anything that hasn't been created yet is guaranteed to be nil at this point. So you could say if FSubObject <> nil then FSubObject.Destroy again and again for all the sub-objects, (and if you forget that you're going to get access violations,) or you can use the Free method, which does it for you. (This is a huge improvement over C++, where the memory space is not zeroed before the constructor is called, which requires you to wrap all your sub-objects in smart pointers and use RAII to maintain exception safety!)
It's useful in other places as well, and there's really no reason not to use it. I've never noticed that Free imposes any measurable performance penalty, and it improves the safety of your code, so it's a good idea to use it in all cases.
Having said that, when dealing with forms specifically, there's an additional variable to factor into the equation: the Windows message queue. You don't know if there are still pending messages for the form you're about to free, so it's not always safe to call Free on a form. For that, there's the Release method. It posts a message to the queue that causes the form to free itself once it's got no more messages to handle, so it's generally the best way to free a form you no longer need.
The canonical form is:
Form := TMyForm.Create(nil);
try
Form.ShowModal;
finally
Form.Free;
end;
Never call Destroy, always call Free instead.
FreeOnRelease is a total red herring. Sometimes, if there are queued messages destined for your form or its children, then you might elect to call Release although often that's indicative of design problems.
The idiomatic usage is
procedure SomeProc;
var
frm: TForm2;
begin
frm := TForm2.Create(nil);
try
frm.ShowModal;
finally
frm.Free;
end;
end;
or, unless you hate the with construct,
with TForm2.Create(nil) do
try
ShowModal;
finally
Free;
end;
You should never call Destroy, according to the documentation. In fact, Free is exactly equivalent to if Self <> nil then Destroy;. That is, it is a 'safe' version of Destroy. It doesn't crash totally if the pointer happens to be nil. [To test this, add a private field FBitmap: TBitmap to your form class, and then OnCreate (for instance), try FBitmap.Free vs. FBitmap.Destroy.]
If you create the form using the approach above, Free is perfectly safe, unless you do some strange things in the form class.
However, if you use CreateForm(TForm2, Form2) to create the form and store the form object in the global instance variable Form2 and you don't free it immediately [for instance, if you want the window to stick around next to the main form in a non-modal way for a few minutes], you should probably use Release instead of Free. From the documentation,
Release does not destroy the form
until all event handlers of the form
and event handlers of components on
the form have finished executing.
Release also guarantees that all
messages in the form's event queue are
processed before the form is released.
Any event handlers for the form or its
children should use Release instead of
Free (Delphi) or delete (C++). Failing
to do so can cause a memory access
error.
FreeOnRelease has nothing in particular do to with forms. From the docs:
It should not be necessary to call
FreeOnRelease directly.
the other way is passing caFree to Action of formonclose
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end

Is Delphi "with" keyword a bad practice?

I been reading bad things about the with keyword in delphi but, in my opinion, if you don't over use it. It can make your code look simple.
I often put all my TClientDataSets and TFields in TDataModules. So in my forms I had code like this
procedure TMyForm.AddButtonClick(Sender: TObject);
begin
with LongNameDataModule do
begin
LongNameTable1.Insert;
LongNameTable1_Field1.Value := "some value";
LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value;
LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value;
LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value;
LongNameTable1.Post;
end
end;
without the with keyword I have to write the code like this
procedure TMyForm.AddButtonClick(Sender: TObject);
begin
LongNameDataModule.LongNameTable1.Insert;
LongNameDataModule.LongNameTable1_LongNameField1.Value := "some value";
LongNameDataModule.LongNameTable1_LongNameField2.Value :=
LongNameDataModule.LongNameTable2_LongNameField1.Value;
LongNameDataModule.LongNameTable1_LongNameField3.Value :=
LongNameDataModule.LongNameTable3_LongNameField1.Value;
LongNameDataModule.LongNameTable1_LongNameField4.Value :=
LongNameDataModule.LongNameTable4_LongNameField1.Value;
LongNameDataModule.LongNameTable1.Post;
end;
I think is easier to read using the with keyword.
Should I avoid using the with keyword?
The biggest danger of with, outside of pathological conditions like "with A, B, C, D" is that your code can silently change meaning with no notice to you. Consider this example:
with TFoo.Create
try
Bar := Baz;
DoSomething();
finally
Free;
end;
You write this code knowing that Bar is a property of TFoo, and Baz is a property of the type containing the method which has this code.
Now, two years later, some well-meaning developer comes in adds a Baz property to TFoo. Your code has silently changed meaning. The compiler won't complain, but the code is now broken.
The with keyword is a nice feature for making your code more readable but there are some pitfalls.
Debugging:
When using code like this:
with TMyClass.Create do
try
Add('foo');
finally
Free;
end;
There is no way to inspect the properties of this class, so always declare a variable and use the with keyword on that.
Interfaces:
When creating an interface in the with clause it lives till the end of your method:
procedure MemoryHog;
begin
with GetInterfaceThatTakes50MBOfMemory do
Whatever;
ShowMessage('I''m still using 50MB of memory!');
end;
Clarity
When using a class in a with clause that has properties or method names that already exists within the scope, it can fool you easily.
with TMyForm.Create do
Width := Width + 2; //which width in this with is width?
Of course when having duplicate names, you're using the properties and methods of the class declared in your with statement (TMyForm).
The with statement has its place but I have to agree that overuse can lead to ambiguous code. A good rule of thumb is to make sure the code is "more" readable and maintainable after adding the with statement. If you feel you need to add comments to explain the code after adding the statement then it is probably a bad idea. If the code is more readable as in your example then use it.
btw: this was always one of my favorite patterns in Delphi for showing a modal window
with TForm.Create(nil) do
try
ShowModal;
finally
Free;
end
I tend towards baning the with-statement altogether. As previously stated, it can make things complicated, and my experience is that it will. Many times the debugger want evaluate values because of withs, and all to often I find nested withs that lead to code that hard to read.
Brian's code seems readable and nice, but the code would be shorter if you just typecast the sender directly, and you remove all doubt about that component you enable:
TAction(Sender).Enabled := Something;
If you are concerned about typing to much, I prefare to make a temporary referance to the long-named object:
var
t: TTable;
begin
t := theLongNamedDataModule.WithItsLongNamedTable;
t.FieldByName(' ');
end;
I can't se why typing should bother you, though. We Are Typists First, Programmers Second, and code-completion, copy-paste and key-recording can help you be a more effective typist.
update:
Just stumbled over an long article with a little section on with-statements: he with keyword. The most hideous, dangerous, blow-your-own-feet-off feature in the language. :-)
When I first began pascal programming (with TurboPascal!) and learnt as I went, WITH seemed wonderful. As you say, the answer to tedious typing and ideal for those long records. Since Delphi arrived, I've been removing it and encouraging other to drop it - neatly summed-up by Verity at the register
Apart from a reduction in readability there are two main reasons why I'd avoid it:
If you use a class then you dont need it anyway - only records 'seem' to benefit from it.
Using the debugger to follow the code to the declaration with Ctrl-Enter doesnt work.
That said, for readability I still use the syntax:
procedure ActionOnUpdate( Sender : TObject )
begin
With Sender as TAction do
Enabled := Something
end;
I've not seen a better construct.
Your example, of a datamodule access within a button click, is a poorly contrived example in my opinion. The whole need for WITH goes away if you move this code into the data module where it should be. The OnClick then just calls LongNameDataModule.InsertStuff and there is no with needed.
With is a poor device, and you should look at your code to see why you are needing it. You probably did something wrong, or could do it a better way.
As Vegar mentioned, it's just as neat and much more readable, easier to debug, and less prone to stealth issues to use a temporary reference.
So far, I have never found a need to use with. I used to be ambivalent over it, until I took over a project that used the mind bending double with frequently. Questioning whether the original developer intended to reference items in the first with or second, if that ambiguous reference was a with-slip or clumsy code, the torment of trying to debug it, and the knock on effects of extending or modifying classes that use these abominations is just not worth anyone's time.
Explicit code is simply more readable. This way you can have your cake and enjoy eating it.
procedure TMyForm.AddButtonClick(Sender: TObject);
var
dm: TLongNameDataModuleType
begin
dm:=LongNameDataModule;
dm.LongNameTable1.Insert;
dm.LongNameTable1_Field1.Value := "some value";
dm.LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value;
dm.LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value;
dm.LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value;
dm.LongNameTable1.Post;
end;
I'm a firm believer of removing WITH support in Delphi. Your example usage of using a datamodule with named fields is about the only instance I could see it working out. Otherwise the best argument against it was given by Craig Stuntz - which I voted up.
I just like to point out that over time you may eventually (should) rmeove all coding in OnClick events and your code will also eventually migrate away from named fields on datamodules into using classes that wrap this data and the reason to use WITH will go away.
Your question is an excellent example of 'a hammer is not always the solution'.
In this case, 'with' is not your solution: You should move this business logic out of your form into your datamodule.
Not doing so violates Law of Demeter like mghie (Michael Hieke) already commented.
Maybe your example was just illustrative, but if you are actually using code like that in your projects, this is what you should do in stead:
procedure TLongNameDataModule.AddToLongNameTable1(const NewField1Value: string);
begin
LongNameTable1.Insert;
LongNameTable1_Field1.Value := NewField1Value;
LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value;
LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value;
LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value;
LongNameTable1.Post;
end;
And then call it from your form like this:
procedure TMyForm.AddButtonClick(Sender: TObject);
begin
LongNameDataModule.AddToLongNameTable1('some value');
end;
This effectively gets rid of your with statement, and makes your code more maintainable at the same time.
Of course surrounding Delphi strings with single quotes will help making it compile as well ;-)
The main problem with "with" is that you don't know where its scope ends, and you could have multiple overlapping with statements.
I don't think you should avoid using it, as long as your code is readable.
One of the proposals to make it more readable (and less confusing in longer code) was if codegear added the option to allow for aliases in with, and probably allowing multiple withs in one:
procedure TMyForm.AddButtonClick(Sender: TObject);
begin
with LongNameDataModule as dm, dm.LongNameTable1 as t1, dm.LongNameTable2 as t2 do
begin
t1.Insert;
t1.FieldByName('Field1').AsString := 'some value';
t1.FieldByName('Field2').AsString := t2.FieldByName('Field2').AsString;
t1.Post;
dm.Connection.Commit;
end
end;
As far as I'm concerned, With is quite acceptable in the case you give. It certainly improves the code clarity.
The real evil is when you have multiple with's open at once.
Also, my opinion is that what you are using the with on makes a big difference. If it's a truly different object then the with is probably a bad idea. However, I dislike having a lot of variables at one level even when this makes sense--generally data objects that hold an entire very complex data item--generally the entire piece of work the program is designed to work with. (I do not think this case would occur in an app that didn't have such an item.) To make the world clearer I often use records to group related items. I find that almost all withs I use are for accessing such subgroups.
There are many excelent answers here as to why the with statement is bad, so I'll try not to repeat them. I've been using the with statement for years and I'm very much starting to shy away from it. This is partially beause it can be difficult to work out scope, but I've been starting to get into refactoring lately, and none of the automated refactorings work withing a with statement - and automated refactoring is awesome.
Also some time ago I made a video on why the with statement is bad, it's not one of my best works but Here it is
Use with only temporarily (just as you comment-out temporarily).
It helps you write code sketches to get something compiled and running fast. If you consolidate the solution, clean it up! Remove with as you move code to the right place.
The current With statement is "dangerous", but it can be substantially improved:
With TForm1.Create (Nil) Do // New TForm1 instance
Try
LogForm ("); // That same instance as parameter to an outer method
"ShowModal; // Instance.ShowModal
Finally
"Free; // Instance.Free
End;
My proposal is:
No more than one object/record per With header.
Nested Withs not allowed.
Usage of " to indicate the object/record (double quotes are similar
to the ditto mark: http://en.wikipedia.org/wiki/Ditto_mark).

Is it safe to remove and reassign events this way? If not, why?

A.Event1 := nil;
A.Event2 := nil;
try
...
finally
A.Event1 := MyEvent1;
A.Event2 := MyEvent2;
end;
Can something go wrong with it?
EDIT:
I've accepted Barry's answer because it answered exactly what I asked, but Vegar's answer is also correct depending on the scenario, sadly I can't accept both.
This sounds like an event-nightmare I have seen before :-)
Instead of removing the events, I usually set a flag that I check in the event. I often use a integer rather than boolean so that the same flag can be set multiple times in one processing.
Something like this:
procedure TMyObject.Traverse;
begin
inc(FTraverseFlag);
try
...
finally
dec(FTracerseFlag);
end;
end;
procedure TMyObject.OnBefore( ... );
begin
if FTraverseFlag > 0 then
exit;
...
end;
I guess this easily could be made thread-safe to solve Barrys concerns.
It entirely depends on what happens in the bit of code marked '...'. If it e.g. starts up a background thread and tries to invoke Event1 or Event2 after execution has continued into the finally block, you may get unexpected results.
If the code is entirely single-threaded, then yes, neither Event1 nor Event2 should be triggered while the code is between the try and finally.
However, that does assume that Event1 and Event2 properties (all Delphi events are properties of a method pointer type) do not do unusual things in their setters. A pathologically malicious event setter could squirrel away a method pointer, and still be able to invoke it, even after you later call the setter with 'nil' as the value.
But that would be highly unusual behaviour.
As Barry said, the only real concern is with multithreaded concerns - other than that is perfectly normal. As VCL events setters just assign the event, nothing need to be worried.
If it is multithreaded you need to make operation atomic. Disabling several event handlers opens possibility that they are running at the moment then they are being disabled. Or Event2 starts when Event1 is being set to nil. All kinds of malfunctions may happen if these events called often. Possible solution to use semaphores instead. But then you may need to add extra code to event handlers as well.
Any way setting event handler temporarily to nil looks like a bad design. It will be incomprehensible very soon and hard to make do what it have to do any way. So you’d better spend some time and develop something more sophisticated.

Resources