3 votes

La suppression de nombreuses entrées "page d'accueil" de Contacts.app à l'aide d'AppleScript est extrêmement lente

Un problème de synchronisation avec Outlook m'a laissé avec des centaines, voire des milliers de contacts en double. Après avoir réussi à fusionner les doublons sans que Contacts ne se bloque, je me suis retrouvé avec 177 contacts, dont la plupart comportaient de nombreuses entrées répétées sur la page d'accueil. Plutôt que de mourir d'ennui en les supprimant à la main, j'ai mis au point un AppleScript pour le faire à ma place, pensant que cela ne prendrait que quelques minutes. Cela fait maintenant une semaine - le script démarre assez bien mais ralentit rapidement et prend de plus en plus de mémoire au système, jusqu'à ce que la boule de neige apparaisse et arrête le script. L'un des problèmes est qu'il semble que je ne puisse supprimer les URL d'un contact que l'une après l'autre, dans l'ordre, au lieu de toutes en même temps.

La question est donc de savoir ce que j'ai fait de mal pour rendre ce script presque inutile. Cela pourrait-il avoir un rapport avec la synchronisation iCloud ? Ou est-ce qu'AppleScript est intrinsèquement inefficace ? (La sauvegarde constante est là à cause des moments aléatoires où le script cesserait de fonctionner) :

tell application "Contacts"
    activate
    with timeout of 72000 seconds
        set myPeople to people
        set numPeople to (count of myPeople)
        repeat with i from 1 to numPeople
            set myGuy to item i of myPeople
            set myGuyName to get name of myGuy
            set personUrls to (the urls of myGuy whose value contains "outlook")
            set urlNum to count of personUrls
            if urlNum > 0 then
                repeat with j from urlNum to 1 by -1
                    log ((time string of (current date)) & " – [" & i & "/" & numPeople & "] " & myGuyName & " (" & j & "/" & urlNum & "): " & (the label of item j of personUrls))
                    delete item j of personUrls
                    save
                end repeat
            else
                log "No problematic URLs found for " & myGuyName
            end if
            if note of myGuy is not missing value then set note of myGuy to ""
        end repeat
        save
        log "Final save"
    end timeout
    return
end tell

1voto

OnePablo Points 1

Il est audacieux d'affirmer que l'inefficacité (terme vague) d'AppleScript en est la cause. Bien sûr, cela pourrait très bien être un facteur, mais il est un peu gênant de dire que votre script est inefficace, et terriblement inefficace. Je ne sais pas si c'est le seulement La raison pour laquelle le script fonctionne lentement, mais c'est un bon endroit pour commencer à faire des corrections, que je vais décrire en extrayant les lignes problématiques dans votre code :

 set myPeople to people

Redondant. Il n'est pas utile d'attribuer une valeur à une variable que vous n'avez pas l'intention d'utiliser de manière significative (par exemple, pour manipuler des données sans en modifier la source ou, si vous en avez vraiment besoin, pour rendre les scripts plus faciles à lire ou à déboguer). Nulle part ailleurs dans votre scripts vous ne faites référence à myPeople à l'exception d'une autre ligne qui est également redondante. Par conséquent, ne gaspillez pas une opération (et potentiellement de la mémoire, mais pas vraiment dans ce cas particulier) en créant une variable dont vous n'avez pas besoin.

 set numPeople to (count of myPeople)

Redondant en principe (je note que vous enregistrez la valeur de numPeople bien qu'il ne soit utilisé que pour vous donner une référence d'index, que vous n'avez pas besoin de connaître ; voir le commentaire suivant).

 repeat with i from 1 to numPeople
       set myGuy to item i of myPeople

Ignorant pour un moment l'appel au journal qui fait référence à numPeople Dans ce cas, l'objectif de la déclaration d'intention de l'Union européenne n'est pas atteint. numPeople est de permettre l'itération dans une liste au moyen d'une variable compteur ( i dans votre cas) qui est utilisé pour accéder à chaque élément par le biais de son index (position), c'est-à-dire item i of myPeople . Il y a de nombreux cas où cela serait très approprié, mais c'est plus lent que de laisser AppleScript se préoccuper de la manière dont il accède aux éléments de la liste, ce qu'il peut faire en utilisant cette syntaxe : repeat with myGuy in people

 set urlNum to count of personUrls

Redondant, pour la même raison que ci-dessus. Par ailleurs, je choisirais personnellement d'évaluer la taille d'une liste à l'aide de la fonction length propriété. Cela ne s'applique pas aux listes imbriquées de listes pour lesquelles vous souhaitez inclure des éléments profondément imbriqués dans le nombre final, mais ce n'est pas le cas ici.

Dès qu'un script a évalué (récupéré) un objet, ses propriétés ont été récupérées dans le cadre de cette évaluation. length est une propriété d'un objet liste, et sa valeur est simple, unaire (un integer ), de sorte que l'accès à cette valeur sera toujours rapide. count est une commande. Elle effectue une ou plusieurs opérations non divulguées et renvoie une valeur. Je ne sais pas quelles sont ces opérations, et elles seront effectuées au niveau du langage C, donc probablement (presque certainement) ne ralentissent pas du tout ce script. Mais, en principe, c'est quelque chose à garder à l'esprit car il y a d'autres situations † une commande et une propriété font apparemment la même chose, mais la propriété est manifestement plus rapide.

† Je ne m'en souviens pas pour l'instant.

 if urlNum > 0 then

Redondant, en principe. Il existe un else Cette clause peut être conservée avec insistance, mais elle ne sert qu'à enregistrer le fait que rien n'a été fait. Si quelqu'un me demandait comment ralentir intentionnellement un script parce qu'il est tout simplement trop efficace, ce serait l'une de mes réponses.

 repeat with j from urlNum to 1 by -1

Cette situation est signalée à la fois par l'utilisation d'une variable compteur, j et pour sa position au sein de l'Union européenne. if bloc. Si le urlNum était fixé à 0, le cœur de la boucle de répétition ne serait jamais saisi et le script continuerait d'exécuter le code qui le suit. Mais, comme nous le verrons plus loin, l'ensemble du repeat est redondant.

 log ... (the label of item j of personUrls))
   delete item j of personUrls

Je m'interroge sur la nécessité de cette mesure log dans son ensemble. Il n'est certainement pas aussi autodestructeur que celui que j'ai mentionné plus haut, mais il remplit une fonction d'information et de sensibilisation. current date et une recherche dans la base de données personUrls objet de la liste.

  • Dans les cas où vous avez besoin d'une variable compteur pour parcourir une liste, faites comme vous l'avez fait ci-dessus, et déclarez une variable à laquelle vous pouvez assigner la valeur de l'élément de la liste en cours, c'est-à-dire set hisURL to item j of personalUrls . En termes simples, chaque fois que vous demandez à AppleScript [la valeur de] item j of... il doit accéder à l'objet liste et effectuer une recherche, ce qui est une opération relativement coûteuse. La déclaration d'une variable signifie que la recherche n'est effectuée qu'une seule fois, puis que la valeur (une copie de l'original) est stockée en mémoire, ce qui permet de la retrouver rapidement et facilement en termes de calcul.

Pour en revenir à la valeur enregistrée, sa valeur semble annulée par la suppression immédiate des données de l'URL. Je me demande si vous n'auriez pas simplement voulu un moyen de suivre où votre script était arrivé dans son exécution, ce qui n'a pas besoin d'être aussi compliqué. En utilisant vos variables de compteur, vous pourriez simplement : log [j, i] (il suffit d'enregistrer leurs bornes supérieures une seule fois, puisque ces valeurs ne changent pas au cours d'une boucle).

En delete dans le contexte de la boucle de répétition dans laquelle elle est appelée, va ralentir considérablement les choses. Vous itérez à travers tous dans une collection afin de la supprimer...

 save

... puis vous enregistrez vos modifications. Comment puis-je faire en sorte que mon script s'exécute aussi lentement que possible ? J'effectuerais une opération de sauvegarde sur l'ensemble du carnet d'adresses un nombre de fois égal à numPeople * urlNum . Cette valeur est au moins égale à 177, mais il s'agit en fait d'un multiple de cette valeur. Le nombre total de fois où vous devez exécuter la fonction save J'imagine que l'opération serait 1.

Les effets d'entraînement :

Nous savons maintenant que l'itération sur PersonUrls n'était pas nécessaire, l'ensemble de la repeat peut être remplacé par la ligne : delete every url of myGuy whose... .

  • Il convient de garder à l'esprit que tout script comportant des éléments imbriqués de type repeat seront inefficaces : le nombre d'opérations effectuées est un produit de la taille de chaque liste.

Je note que vous mentionnez avoir tenté de supprimer en masse les URL d'un contact, ce qui n'a pas fonctionné pour vous, vous obligeant à le faire de manière itérative. Cependant, comme vous n'avez pas fourni de code montrant les méthodes que vous avez essayées pour effectuer la suppression en masse, il n'est pas possible de donner un aperçu de la raison pour laquelle cela a échoué pour vous.

Retrait de la repeat a l'avantage cumulatif d'annuler le bloc parent if indépendamment de mes commentaires antérieurs à ce sujet.

  • Les conditionnelles peuvent être des expressions coûteuses à évaluer, en particulier si l'on en exécute 177 qui n'ont jamais été nécessaires.

Le code remanié :

En remontant le script, les déclarations de variables précédentes deviennent toutes redondantes, ce qui conduit à la conclusion finale que votre script entier est fonctionnellement équivalent à :

use application "Contacts"

tell (a reference to every person)
    delete (its urls where the value contains "outlook")
    set its note to missing value
end tell

save

Informations sur le système : Version AppleScript : 2.7 Version du système : 10.13.6

Et maintenant ?

Votre objectif ultime était-il de supprimer tous URL contenant "outlook", ou avez-vous prévu de conserver une URL "outlook" pour les contacts qui en disposent ?

1voto

Nerdilicious Points 41

La réponse de @CJK n'a pas fonctionné pour moi, mais la réponse donnée dans la rubrique Evénements de script Editor m'a fourni la base d'une alternative qui semble fonctionner comme prévu :

tell application "Contacts"
    delete (every url of every person whose value contains "outlook")
    set note of every person to missing value
    save
end tell

<strong>Ajouté par <a href="https://apple.stackexchange.com/users/266364/cjk"><em>@CJK </em></a>le 2019-04-03 :</strong>

Plutôt que d'obliger AppleScript à énumérer des every person dans votre carnet d'adresses deux fois, ce qui est, en général, une opération coûteuse, stockez une référence à la collection dans une variable, que vous pouvez ensuite utiliser de manière répétée avec beaucoup moins de frais généraux :

tell application "Contacts"
    set _everyone to a reference to every person
    set _homepages to a reference to _everyone's urls

    delete the _homepages where the value contains "outlook"
    set _everyone's note to missing value

    save
end tell

Informations sur le système : Version AppleScript : 2.7 Version du système : 10.13.6

C'est ce qui est renvoyé par la fonction Evénements o Réponses lors de l'exécution du code original remanié :

tell application "Contacts"
    delete every url of every person whose value contains "outlook"
    set note of every person to missing value
end tell
tell application "Script Editor"
    save current application
end tell

Je suppose que rien ne semble se passer parce que (je pense) les modifications apportées aux contacts ne fonctionnent qu'une fois sauvegardées. La nouvelle version de @CJK ci-dessus fonctionne cependant comme prévu.

Est-il aussi facile de réduire le nombre de doublons à un seul que de les supprimer tous ?

LesApples.com

LesApples est une communauté de Apple où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres utilisateurs d'appareils Apple, poser vos propres questions ou résoudre celles des autres.

Powered by:

X