More info in the FAQ: http://bbs.applescript.net/viewforum.php?id=23
Thanks JJ,
I did not realise that a global variable could retain its value after running. Hummm. . . I could see where that could cause a lot of problems and head scratching if you donât know this and donât reset the variable every time.
PreTech
Youâve given a very good explanation, PreTech.
I see the threadâs moved on a little while Iâve been writing this, but I may as well go ahead and post it. I enjoy a little expound now and then.
Properties of scripts are created and set to their initial values when the scripts are compiled. If a property value is declared indirectly as executable code â such as âpath to desktop as textâ â the compiler runs the code and initialises the property to the result. The code isnât run by the script itself, so on another machine, the propertyâs value will still be that obtained on the compiling machine. However, itâs possible to change property values at run-time, using the âsetâ command. Whether or not the values are changed, properties and their final values are saved back into the script file when the script finishes running and the properties will have those values the next time the scriptâs run. (This isnât reliable in Tiger, especially if youâre running the script from Script Menu.) Properties are global in scope to the scripts in which theyâre declared.
Other sorts of variables come into existence the first time theyâre given a value when the script runs. Itâs a rather complex situation and I find it helps to divide them primarily into âtop levelâ variables (ie. variables that are first set in the implicit or explicit ârunâ handler) and variables that are used in handlers.
If a âtop levelâ variable is declared as global, itâs accessible from anywhere in the script â except of course in areas where itâs explicitly been declared local. If itâs declared as local, its scope is confined to the ârunâ handler. If itâs simply used without its scope being declared at all, itâs a little of both! In this case, its scope is normally confined to the top level of the script; but if itâs declared as global in any of the other handlers, itâll be common to both the ârunâ handler and the handler(s) containing the global declaration(s). But it wonât be accessible from any other handlers where itâs not explicitly declared as global.
Variables in a handler are local to that handler unless declared otherwise, either in the handler itself or at the top level of the script. If theyâre declared global in the handler but not at the top level, they can only be shared with âtop levelâ variables of the same names and/or with similarly named variables that have been declared global in other handlers. (So its possible to have variables that are âglobalâ within just a small group of handlers.)
Like properties, globals and âtop levelâ variables (except those explicitly declared local) and their values are saved back into the script file when the script finishes. Thus theyâll already exist the next time the script is run. There doesnât appear to be any practical use for this, but itâs useful to know that it happens. People have often been caught out because the final value of one of their âtop levelâ variables has been something bulky (like a large text) and the systemâs choked while trying to save it back into the script file. Local variables, on the other hand, cease to exist the moment the handler theyâre in exits. What happens to their values depends on whether or not these have been passed to globals first or returned by the handler.
The properties of a script object â as opposed to those of a compiled script â are created and set only when the script object itself comes into being during the course of a run. They persist as long as the script object remains in existence as a piece of data. Theyâre not saved back into the script file unless the objectâs been assigned to a persistent variable in the compiled script.
As I go farther and farther towards the deep end of this pool we call AppleScript, I want to get into good habits and conventions.
Today, egads!, Iâve had some time to actually do some reading on MacScripters and the two reference books my employer let me buy (âAppleScript: The Definitive Guideâ by Matt Neuburg, and âAppleScriptâ by Hanann Rosenthal).
More often than not itâs âShock DevelopmentââŚI get interrupted alot, too many hats, and so âplanningâ sometimes takes a back seat to just feverishly writing code before the next interruption. Slowly Iâm learning to write more handlers and less âmegascriptsâ and try to get more time to actually make quicky flowcharts to figure out at least a few of the handlers ahead of time rather than while Iâm writing.
One thing Iâm still a bit confused on is the property attribute, and I havenât used them outside of access properties for other files and applications.
Coming from years of programming (BASIC, C, Pascal, etc.) I didnât have a problem wrapping my head around local and global variables, and the basics of parent-child instances of variables, and knowing when I stupidly stepped on my own toes in those regards.
But how do I know when I should be using a property versus a Global variable, since they seem very similar in behavior? Is it proper âformâ to use one rather than the other for certain situations? Iâve been pretty much using globals declared in the implicit run handler, top of the script.
I also ask this from the perspective of writing code to be moved to FaceSpan (or AS Studio), if that makes any difference. Iâve already bumped into a few nuances where a perfectly running script does not always mean a perfectly running Project, and Iâd like to set-up the habits to avoid those issues.
Also, my plug for Nigel, Adam, waltr, PreTech and others who seem to solve these problems I run into with relative ease. Iâm outa practice with programming.
All right, whatâs the deal with this?
set a to "test"
set c to b(a)
on b(a)
global a
-- set d to ...
--
return d
end b
â Syntax Error: âCanât declare a as both a local and global variable.â
It doesnât like the handlerâs global variable a as the argument to the same handler, b( ). Evidently a variable is declared local within a handler argument, no matter what. Even adding a global a to the top level implicit run handler doesnât please AppleScript. It only successfully compiles if the global a is solely declared at the top level. Easy enough to solve the immediate problem, but, hey, I gots to know why!
AppleScript: 2.1.2
Operating System: Mac OS X (10.6.8)
You declare a global variable in the file to instruct the compiler how to handle the variable further in the script. Therefore you donât declare globals inside the handlers.
The explicit local variable is to make sure that the class (blueprint of the object) is compiled in such a way that it doesnât make a call to any global variable is exists. Itâs not true that variables in handlers are local by default. A better term is that theyâre greedy for being global. When it can be global it will be global otherwise just local, so to make sure it is local you can explicitly declare them as local as the following example proves:
global a
set a to 1
handlerWithoutLocal()
log a
handlerWithLocal()
log a
handlerWithoutLocal()
log a
on handlerWithLocal()
local a
set a to 5
end handlerWithLocal
on handlerWithoutLocal()
set a to 10
end handlerWithoutLocal
As expected the the log result will show 3 times the value 10.
Thatâs a bit misleading â or at least confusing. Variables in handlers are local by default. Theyâre only global if theyâve been declared thus in a scope which includes that handler. So if you declare âaâ to be global at the top of the script, itâll be global inside the handler as well. The exception to this is if you use âaâ as a parameter variable as well, in which case itâll be local inside the handler:
global a
set a to 10
aHandler(5)
return a --> 10
on aHandler(a)
set a to a + 1
end aHandler
But you can still use the global by putting âmyâ in front of it, since globals, like properties, âbelongâ to the script:
global a
set a to 10
aHandler(5)
return a --> 6
on aHandler(a)
set my a to a + 1
end aHandler
You can also declare globals inside handlers, in which case theyâre global between the handlers in which theyâre declared and the ârunâ handler:
set a to 10
handler1()
handler2()
log a --> 13
handler3() --> error: The variable a is not defined.
on handler1()
global a
set a to a + 1
end handler1
on handler2()
global a
set a to a + 2
end handler2
on handler3()
set a to a + 5
end handler3
What you canât do, as Schmye found, is declare a global inside a handler where the variable name already belongs to a local. In Schmyeâs case, the local is the parameter variable, but the compiler will also complain if the nameâs used in the handler before the global declaration:
on aHandler()
set a to 5
global a
end handler3
Because an parameter is by definition local when copied and passed trough the stack. You cannot declare global parameters so the compiler will handle the variable always as local with no exception. Maybe greedy is not the right word (I have no doubt your English is way better than mine ) but AppleScript will use global when it possible, so I thought that âdefaultâ is not the right term either.
So the take-away to my direct question is that I get the error because:
- A variable is auto-declared local in a handler unless a global declaration precedes it.
- My handler argument variable (I guess I should be calling it âparameterâ) was local because the argument (parameter) preceded my global declaration in the handler, notwithstanding it being the very first line within the handler.
But then why did I get the error also when I additionally had a global declaration as the first line of the top-level run handler, which preceded the handler call? It sounds like I was right the first time, surmising:
âŚbecause as:
So the only reason that the variable set in the run handler is recognized by the subhandler is solely that it was passed to it because it was the argument (parameter), and the global declaration in the run handler was otherwise unnecessary. Right?
Itâs possibly a little ambiguous. Understanding âdefaultâ to mean âa preselected option adopted by a computer program or other mechanism when no alternative is specified by the user or programmerâ
[quote from Oxford Dictionary of English (British English) in Appleâs Dictionary.app]
, variables in handlers are local unless explicitly declared global or properties at the top of the script. If, on the other hand, you start with the existence of global or property declarations at the top of a script, those variables are global in handlers too unless explicitly declared local in individual handlers.
More accurately, itâs the value (or a pointer to it in memory) thatâs passed. The parameter variable that receives it in the handler is local to the handler and isnât the one declared global at the top of the script, even though they may have the same name and contain the same value. In that case, the global declaration would have been unnecessary, yes. On the other hand, as one of my examples above shows, changing the value of one of the variables doesnât change the value of the other, so if you want the effect to be visible outside the handler, you have to code accordingly. Either:
- Declare a global and use a different name for the local in the handler; or
- Declare a global, use the same name for the local, and address the global specifically by putting âmyâ in front of the variable name; or
- Donât use a global at all but return a value from the handler and set the external variable to the result. This is usually the best idea.
Hi everyone! I hope I can revive this post.
Can someone explain why LIST variables behave differently than REGULAR variables in this regard?
Below see two examples. In the first one, the LIST variable is affected by the handler without being a GLOBAL variable. The second example behaves as expected. How can I avoid the first example from happening?
set myList to {"AA", "BB", "CC"}
sampleHandler(myList)
item 2 of myList -->> RETURNS "XX"
--
on sampleHandler(aTemporalList)
set item 2 of aTemporalList to "XX"
end sampleHandler
set myVariable to "AA"
sampleHandler(myVariable)
myVariable -->> RETURNS "AA"
--
on sampleHandler(aTemporalVariable)
set aTemporalVariable to "XX"
end sampleHandler
Model: iMac
AppleScript: 2.10 (194)
Operating System: macOS 10.13
When you set one variable to another, or pass a variable as a parameter to a handler, both the original and receiving variables contain the same object. In your first example, youâre changing the contents of the list which is that object. In the second, youâre changing the contents of the receiving variable to a different object.
Lists, records, and dates are sometimes called âshared dataâ when theyâre contained by two different variables.
Hi NG! Thanks for your reply. I understand the concept of an object in memory that could be referenced by different variables but how can I just âcopyâ the LIST to a new variable instead of referencing that object in memory? Is it possible?
I just realized that we can take the handler out of the equation and get the same result. Here are both examples without the handler.
set myList to {"AA", "BB", "CC"}
set aTemporalList to myList
set item 2 of aTemporalList to "XX"
item 2 of myList -->> RETURNS "XX"
set myVariable to "AA"
set aTemporalVariable to myVariable
set aTemporalVariable to "XX"
myVariable -->> RETURNS "AA"
To avoid referencing 2 variables to same object in memory you should do 1 thing:
instead of set Variable_2 to Variable_1
use copy Variable_1 to Variable_2
The standard copy command is mostly used as an AppleScript command, although it can also operate as various application commands under some conditions. Like the set command, AppleScriptâs copy command is commonly used to assign values to variables. Here is its syntax:
copy some_expression to variable_or_reference
There is one important difference between using set and copy, however. Although set always puts the original value you give it directly into the variable, when used with certain classes of AppleScript values, the copy command makes an identical copy of the original value and puts this in the variable instead.
This duplicating behavior applies only to values that can have editable properties and elements: lists, records, dates, script objects, and AppleScript references created with the a reference to operator. Simpler values such as numbers and strings arenât affected. For example, the following,
set variable_1 to "John"
set variable_2 to variable_1
and the following,
set variable_1 to "John"
copy variable_1 to variable_2
do the same thing. The first line assigns a string value, âJohnâ, to variable_1. The second line assigns the value of variable_1 to variable_2. Both variables contain the same object.
Similarly, if you use the set command with a list object, like this,
set variable_1 to {"John", "Paul", "George", "Pete"}
set variable_2 to variable_1
both variables contain the same object.
You can check this by changing one of the items in the list as follows:
set variable_1 to {"John", "Paul", "George", "Pete"}
set variable_2 to variable_1
set last item of variable_2 to "Ringo"
If you look at the value of variable_2, itâs just what youâd expect it to be:
variable_2
--> {"John", "Paul", "George", "Ringo"}
If you next check the value of variable_1, youâll find itâs the same list object:
variable_1
--> {"John", "Paul", "George", "Ringo"}
This ability to put the same value into multiple variables can be useful in some situations, although it can also easily catch you out if youâre not careful! For example, what if you wanted to keep your original list around so you could use it again later in a different part of the script? Obviously, using set is no good for this.
One solution is to make a perfect copy of the original list by using the copy command instead:
set variable_1 to {"John", "Paul", "George", "Pete"}
copy variable_1 to variable_2
Now each variable contains a different list object. At first, they still look identical,
variable_1
--> {"John", "Paul", "George", "Pete"}
variable_2
--> {"John", "Paul", "George", "Pete"}
but you can now safely alter the contents of the second list without affecting the first one:
set last item of variable_2 to "Ringo"
variable_2
--> {"John", "Paul", "George", "Ringo"} -- Our new list
variable_1
--> {"John", "Paul", "George", "Pete"} -- Our original list
Thanks, KniazidisR! That answers my question!! Iâve used the COPY keyword in the past for copying files to a new location or for adding an item to the end of a list but never in that manner. So now I knowâŚ
For everyone elseâs reference here is the working code:
set myList to {"AA", "BB", "CC"}
--set aTemporalList to myList
copy myList to aTemporalList
set item 2 of aTemporalList to "XX"
item 2 of myList -->> RETURNS "BB"
Itâs still a bit confusing why the âlist objectâ behaves differently than a âsingle value objectâ when using the SET keyword in AppleScript. This second example works the same using COPY or SET
set myVariable to "AA"
--set aTemporalVariable to myVariable
copy myVariable to aTemporalVariable
set aTemporalVariable to "XX"
myVariable -->> RETURNS "AA"
Itâs unclear to me how to make myVariable and aTemporalVariable reference the same value in memory. I canât think of any practical uses for that but would be interesting to know, if anyone has the answer
You can create few instances (that is, variables) of same object in memory, then kill some of variables and leave others. That gives you ability to manage memory (init, release), used by script, similar to C or Swift. One user here asked help to play VLC (that is, 1 same object) on 4 screensâŚ
As KniazidisR says, you can use copy, which duplicates the contents of the source variable (if itâs a list, record, date, or other âstructureâ object) to the receiving variable. Itâs a âdeepâ copy, so if any of the objectâs contents are lists, records, or dates, theyâre duplicated too.
If the object in the source variableâs a list, it can sometimes be useful to set the receiving variable to the sourceâs items instead:
set Variable_1 to Variable_2's items
In this case, Variable_1 is a different list object from Variable_2, but containing the same items â just as like both variables initially containing the same lists in the earlier discussion. So you can change the items in one list without affecting the other, but if any of the items they both contain are lists, records, or dates, changing their contents will be visible in both lists!
If a handlerâs expecting a âstructureâ object as a parameter, and you want to change its contents in the handler without affecting the original outside the handler, you can simply copy it at the top of the handler. In fact, since the parameter variable is local to the handler, you can even copy it to itself!
on aHandler(myList)
copy myList to myList -- Set myList to a duplicate of what it already contains.
-- etc.
end aHandler
You should use duplicate for that.
Itâs more efficient to use set in that case, unless you definitely need the added item to be a duplicate of an original.
To understand what is one object in memory - simply byte sequence. The variable is address to first byte of that sequence
Guys, this is great stuff, Iâm going to chew this up tomorrow morning at my best.
I thought Swift had automatic memory management (garbage collectionâŚ?) I remember it from when it was released, along with advertised simplicity and the removal of some âannoyingnessâ of common programming languages. I would like to learn Swift but I was recommended to get into .NET better since it is open to more platforms. What would you recommend???
My longest AppleScript script is close to 2000 lines, Iâm probably pushing the limits of it (or not optimizing my code enough LOL) but I find it simple and useful. I like how it integrates with other programs like the Finder, Adobe apps, Excel, etc., and even the use of shell scripts seals the gap of missing tools. I have few scripts that run on a schedule fetching info from the internet, moving files and folders around and saving me a bunch of time on daily tasks. I just counted 335 script files in my Script Editor folder LOL With that said, I think its time for me to learn how to build standalone apps
The question raised by sergiorbp has been answered, but I thought I might add a quick note. In discussing this broad issue, the AppleScript Language Guide uses the term mutable, and, simply stated, mutable objects (such as a list) can share data but nonmutable objects (such as a text string) cannot. Thus, the language guide states:
âIf multiple variables refer to a mutable objectâŚ, changes to the object are observable through any of the variables [emphasis mine].â
For the reason noted above, this is not directly possible, but it can be accomplished by making the text object into a mutable object:
set myVariable to {"AA"}
set aTemporalVariable to myVariable
set item 1 of aTemporalVariable to "XX"
item 1 of myVariable -->> retunrs "XX"