Sunday, December 4, 2022

#1 2021-06-19 08:28:48 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1518

Saving a mutable dictionary to a property list file

I'm optimizing an old script and have encountered an issue which I've resolved but not necessarily well. The script is long, so I'll summarize.

I create a mutable dictionary using:

Applescript:

set bookmarkData to (current application's NSMutableDictionary's new())

I then add items to the dictionary using:

Applescript:

bookmarkData's setObject:currentPath forKey:currentName

Next I save the dictionary to a plist file using:

Applescript:

on writePlist(thePlist, theDictionary)
   set theDefaults to current application's NSUserDefaults's alloc()'s initWithSuiteName:thePlist
   set theDefaults's theKey to theDictionary
end writePlist

The next time I run the script the dictionary is read with:

Applescript:

on readPlist(thePlist)
   set theDefaults to current application's NSUserDefaults's alloc()'s initWithSuiteName:thePlist
   set theDictionary to theDefaults's theKey
   return theDictionary
end readPlist

The issue I've encountered is that the dictionary returned by the readPlist handler is no longer mutable. I say that because I get an unrecognized selector error from the setObject method. I fixed this by rewriting the readPlist handler as shown below.

Applescript:

on readPlist(thePlist)
   set theDefaults to current application's NSUserDefaults's alloc()'s initWithSuiteName:thePlist
   set theDictionary to theDefaults's theKey
   return (theDictionary's mutableCopy())
end readPlist

My question is a simple one--why doesn't the first-above readPlist handler return a mutable dictionary. The script with the second-above readPlist handler works fine and I guess I should accept that as good, but I couldn't help but wonder. Thanks for the help.

Last edited by peavine (2021-06-19 09:02:58 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

#2 2021-06-19 09:03:00 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1090

Re: Saving a mutable dictionary to a property list file

I believe it has to do with:

Important
Don’t try to access the preferences subsystem directly. Modifying preference property list files may result in loss of changes, delay of reflecting changes, and app crashes. To configure preferences, use the defaults command-line utility in macOS instead.


You could read more here: https://developer.apple.com/library/arc … mmary.html
I also believe Immutability state is more thread safe... and the opposite...

Ask yourself have you ever had a bad preference configuration file for any application ??
In other words every time you start a application it crash... meaning the solution could be to
delete the preference file...

Last edited by Fredrik71 (2021-06-19 09:09:02 am)


Node-RED makes it easy to automate IoT

Offline

 

#3 2021-06-19 09:18:08 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1518

Re: Saving a mutable dictionary to a property list file

Fredrik71. Thanks for looking at my thread.

In his book, Shane has a whole chapter dedicated to "Persisting Preferences" and this chapter appears to discuss doing exactly what I am doing. The dictionary saved to the plist file is used in my script only and has nothing to do with any other apps. I probably should have been clearer on this point.

BTW, the actual script I am working to optimize will be found in the last post of the following thread. In my testing, I have determined that NSMutableDictionary's dictionaryWithContentsOfFile is appreciably slower than NSUserDefaults at this particular task.

https://macscripter.net/viewtopic.php?id=46437

Last edited by peavine (2021-06-19 09:40:08 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

#4 2021-06-19 10:38:22 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1090

Re: Saving a mutable dictionary to a property list file

@peavine, maybe I was not clear...
My respond to you was only about immutable vs mutable of NSDictionary. As you could read from the link I send you talks about thread safe vs thread-unsafe classes in cocoa. I was not speaking about if you could do it or not. I'm sure Shanes know what he is talking about in his book.

If I understand you question you was wonder why it was immutable... to only read a instance is safe but to read a instance and also change it maybe is not safe.

That was my approach to it, simple way to crash Script Editor is to cast wrong type to a method.


Node-RED makes it easy to automate IoT

Offline

 

#5 2021-06-19 10:43:48 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1518

Re: Saving a mutable dictionary to a property list file

Fredrik71. I see what you're saying now, and that could be the answer. Many thanks.

Shane doesn't address this issue directly in his book but he does state:

A disadvantage of property lists is that they can only store a small range of classes: text, reals, integers, booleans, Cocoa dates and Cocoa data, as well as lists/arrays and records/dictionaries made up of the same.



Perhaps mutable dictionaries cannot be saved.

Last edited by peavine (2021-06-19 10:49:16 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

#6 2021-06-19 11:30:16 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1090

Re: Saving a mutable dictionary to a property list file

@peavine...
This is question for Shane, but its a interesting topic.


Node-RED makes it easy to automate IoT

Offline

 

#7 2021-06-19 06:24:52 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1518

Re: Saving a mutable dictionary to a property list file

I did a little addition research, and the documentation for NSUserDefaults contains the following paragraph, which appears to confirm the above:

Values returned from NSUserDefaults are immutable, even if you set a mutable object as the value. For example, if you set a mutable string as the value for “MyStringDefault”, the string you later retrieve using the stringForKey: method will be immutable. If you set a mutable string as a default value and later mutate the string, the default value won’t reflect the mutated string value unless you call setObject:forKey: again.



Also, Shane's book contains the following example, which seems to confirm that mutableCopy is the correct method to use to make the dictionary mutable after retrieval by NSUserDefaults:

Applescript:

use framework "Foundation"

on testIt()
   set anNSArray to current application's NSArray's arrayWithArray:{1, 2, 3}
   set newArray to anNSArray's mutableCopy()
   newArray's addObject:4
   return newArray as list
end testIt

testIt()

Last edited by peavine (2021-06-19 06:38:19 pm)


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

#8 2021-06-19 09:00:51 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6799

Re: Saving a mutable dictionary to a property list file

It looks like you've answered your own question, but to go a little further, you can use NSPropertyListSerialization when reading property lists (not via defaults) to control mutability.

For example:

Applescript:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theData to current application's NSData's dataWithContentsOfFile:thePath
set {theDict, theError} to current application's NSPropertyListSerialization's propertyListWithData:theData options:(current application's NSPropertyListMutableContainersAndLeaves) format:(missing value) |error|:(reference)


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#9 2021-06-20 08:57:13 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1518

Re: Saving a mutable dictionary to a property list file

Thanks Shane. I placed the your code in my script and it worked fine. I'm not familiar with NSData or NSPropertyListSerialization, and I'll work to learn those.

Just by fumbling around a bit, I got the following to work to write the plist file. Other than error correction, which I'll add, does this approach look OK? I use this script a lot and want to make sure. Thanks again.

Applescript:

on writePlist(thePath, theDictionary) -- theDictionary is a mutable dictionary and thePath is POSIX path
   set {theData, theError} to current application's NSPropertyListSerialization's dataWithPropertyList:theDictionary format:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(reference)
   set theResult to theData's writeToFile:thePath atomically:true
end writePlist

Last edited by peavine (2021-06-20 10:47:11 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

#10 2021-06-20 11:21:53 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1090

Re: Saving a mutable dictionary to a property list file

@peavine I see that you use reference for the error... I think this is more correct.

Applescript:

on writePlist(thePath, theDictionary) -- theDictionary is a mutable dictionary and thePath is POSIX path
   set {theData, theError} to current application's NSPropertyListSerialization's dataWithPropertyList:theDictionary format:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(reference)
   if theData is missing value then error (theError's localizedDescription() as text)
   set theResult to theData's writeToFile:thePath atomically:true
end writePlist


Node-RED makes it easy to automate IoT

Offline

 

#11 2021-06-20 05:30:58 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6799

Re: Saving a mutable dictionary to a property list file

peavine wrote:

does this approach look OK?



Sure. You can also use -writeToURL:error: (since 10.13) or the older -writeToFile:atomically:/writeToURL:atomically: directly from the dictionary.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#12 2021-06-20 06:38:35 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1518

Re: Saving a mutable dictionary to a property list file

Thanks Shane. I appreciate the help.


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

#13 2021-06-21 08:04:58 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 1518

Re: Saving a mutable dictionary to a property list file

All of my questions have been answered and I thought I would add a postscript.

I ran timing tests with three approaches that can be used to write a mutable dictionary to a property list file and to read it afterwards. The test dictionary contained 300 keys (which is a worse-case scenario for me) and the tested options were:

1) NSMutableDictionary
2) NSUserDefaults
3) NSData (NSPropertyListSerialization)

The write times were measured with a timing script and the read times with Script Geek. The end result was that all three options were reasonably quick with write times of 12 milliseconds or less and read times of 8 milliseconds or less. The read times, which were of greatest interest to me, generally varied by no more than 2 milliseconds.

I state in an earlier post that NSMutableDictionary was significantly slower than NSUserDefaults. That is not correct and was the result of an error in my testing.

Last edited by peavine (2021-06-22 07:35:43 am)


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

#14 2021-07-08 02:39:17 am

technomorph
Member
Registered: 2017-12-14
Posts: 304

Re: Saving a mutable dictionary to a property list file

Just wanted to say that on some versions of SDKs and Applescripts
I’ve had issues trying to save NSURL’s in a dict or Using Serialization and NSArchiver.
I had to get the path (NSString) then store that as a value in the dictionary.
And on the reverse side, recreate the NSURL’s from the “saved” NSString.

I believe it may have been in OS10.11 that this wasn’t an issue.

So if you ever have other issues when trying to save.
Remember the basic NSObjects that it can encode.
Things like enums, NSPoint, NSRect etc need to be converted to NSValue
And then stored in the Dictionary as a value for Key.
Then also when un archiving the reverse must be done.
Some of the Classes have convince methods for creating the NSValue and also for i it the Class with a NSValue. (Rather than found thru NSData).

+ (NSValue *)valueWithRect:(NSRect)rect;

property(readonly) NSRect rectValue;

Offline

 

#15 2021-07-08 06:30:22 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6799

Re: Saving a mutable dictionary to a property list file

The bridging of NSURL and files/aliases was introduced in 10.11 (as was bridging between NSDate and AS dates).

Points and rects are probably better stored as strings. The functions to use are: NSStringFromRect(), NSRectFromString(), NSStringFromPoint() and NSPointFromString(). The result is a lot more human-readable.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)