在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 使 Mac 應(yīng)用數(shù)據(jù)腳本化
與四軸無人機的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學(xué)無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機工作原理
Build 過程

使 Mac 應(yīng)用數(shù)據(jù)腳本化

當(dāng)為應(yīng)用添加 AppleScript 支持的時候 - OS X 10.10 中也可以是 JavaScript 支持(譯者注:10.10 中我們可以使用 JavaScript 作為腳本語言了),最好以應(yīng)用的數(shù)據(jù)作為開始。這里的腳本并不是說自動按鈕點擊什么的;而是在說將你的 model 層暴露給那些會在自己的工作流程中使用你的應(yīng)用的人。

有的用戶會向朋友和家人推薦應(yīng)用,雖然通常像這樣的用戶極少,但是他們是超級用戶。他們的博客和 twitter 上有關(guān)于應(yīng)用的內(nèi)容,人們關(guān)注了他們。他們會成為你的應(yīng)用的最大傳播者。

總體而言,添加腳本支持最重要的原因是它使得應(yīng)用更加專業(yè),而這所能得到的回報是值得我們努力的。

Noteland

Noteland 是一個除了空白窗口之外沒有任何 UI 的應(yīng)用,但是它有 model 層,并且可以腳本化。你可以在 GitHub 上找到它。

Noteland 支持 AppleScript(10.10上還支持 JavaScript)。它是在 Xcode 5.1.1 中用 Objective-C 寫的。我們最初試圖使用 Swift 和 Xcode 6 Beta 2,但是出現(xiàn)了困難。這完全可能是我們自己的錯誤,因為畢竟我們?nèi)匀辉趯W(xué)習(xí) Swift。

Noteland 的對象模型

有兩個類,notes(筆記) 和 tags(標(biāo)簽)??赡苡卸鄠€筆記,而且一個筆記也許有多個標(biāo)簽。

NLNote.h 聲明了幾個屬性: uniqueID,text,creationDate,archivedtags 和一個只讀的 title 屬性。

Tags 類更加簡單。NLTag.h 聲明了兩個可腳本屬性: uniqueIDname。

我們希望用戶能夠創(chuàng)建,編輯和刪除筆記和標(biāo)簽,并且能夠訪問和改變除了只讀以外的屬性。

腳本定義文件 (.sdef)

第一個步驟是定義腳本接口,概念上可以理解為為腳本創(chuàng)建一個 .h 文件,但是是以 AppleScript 能夠識別的格式進行創(chuàng)建。

過去,我們需要創(chuàng)建和編輯 aete 資源(“aete” 代表 Apple Event Terminology)?,F(xiàn)在容易了很多:我們可以創(chuàng)建一個 sdef(scripting definition 腳本定義)XML 文件。

你可能更傾向于使用 JSON 或者 plist,但是 XML 在這里會更加合適,至少它毫無疑問戰(zhàn)勝了 aete 資源。事實上,曾有一段時間有 plist 版本,但是它要求你保持 兩個 不同的 plist 同步,這非常痛苦。

原來的資源的名字 (aete,Apple Event Terminology) 其實沒什么特別的意思。Apple event 是由 AppleScript 生成,發(fā)送和接受的低級別消息。這本身是一種很有趣的技術(shù),而且有腳本支持以外的用途。而且實際上,它從 90 年代初的 System 7 開始就一直存在,而且在過渡到 OS X 的過程中存活了下來。

(猜測:Apple event 的存活是由于很多印刷出版商依賴于 AppleScript,在 90 年代中后期的 '黑暗日子' 中,出版商們是 Apple 最忠實的用戶。)

一個 sdef 文件總是以同樣的頭部作為開始:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">

頂級項是字典 (dictionary),“字典” 是 AppleScript 中專指一個腳本接口的詞。在字典中你會發(fā)現(xiàn)一個或多個套件 (suite)。

(提示:打開 AppleScript Editor,然后選擇 File > Open Dictionary...你會看到有腳本字典的應(yīng)用列表。如果你選擇 iTunes 作為例子,你會看到類,屬性和 iTunes 能識別的命令。)

<dictionary title="Noteland Terminology">

標(biāo)準(zhǔn)套件

標(biāo)準(zhǔn)套件定義了應(yīng)用應(yīng)該支持的所有類和操作。其中包括退出,關(guān)閉窗口,創(chuàng)建和刪除對象,查詢對象等等。

將它添加到你的 sdef 文件,從位于 /System/Library/ScriptingDefinitions/CocoaStandard.sdef 的標(biāo)準(zhǔn)套件中復(fù)制和粘貼。

<suite name="Standard Suite", 從頭到尾且包括結(jié)尾 </suite> 復(fù)制所有東西。

將它粘貼到你的 sdef 文件中 dictionary 元素的正下方。

然后,在你的 sdef 文件中,遍歷并刪除所有沒有用到的東西。Noteland 不基于文檔且無需打印,所以我們?nèi)サ袅舜蜷_和保存命令,文件類,以及與打印有關(guān)的一切。

(建議:Xcode 在 XML 的縮進方面做得很好,為了重新縮進,選中所有文本并且選擇 Editor > Structure > Re-Indent。)

當(dāng)你完成編輯后,使用命令行 xmllint 程序 xmllint path/to/noteland.sdef 以確保 XML 是正常的。如果它只顯示了 XML,沒有錯誤和警告,那么就是正確的。(記住你可以在 Xcode 的窗口標(biāo)題欄拖拽文件的代理圖標(biāo)到終端,然后會粘貼文件的路徑。)

Noteland 套件

一個單一的應(yīng)用定義套件通常是最好的,雖然并不強制:當(dāng)確實合情合理的時候,你可以有超過一個的套件。Noteland 只定義一個,下面是 Noteland 套件:

<suite name="Noteland Suite" code="Note" description="Noteland-specific classes.">

腳本字典所期望的是某些部件被包含在其他東西中。頂級容器是應(yīng)用程序?qū)ο蟊旧怼?/p>

在 Noteland 中,它的類名是 NLApplication。對于應(yīng)用的類你應(yīng)該總是使用 capp 作為編碼 (code) 值:這是一個標(biāo)準(zhǔn)的 Apple event 編碼。(注意它也存在于標(biāo)準(zhǔn)套件中。)

<class name="application" code="capp" description="Noteland’s top level scripting object." plural="applications" inherits="application">
    <cocoa class="NLApplication"/>

該應(yīng)用包含一個筆記的數(shù)組。區(qū)分元素(這里可以有不止一項)和屬性非常重要。換句話說,編碼中的數(shù)據(jù)應(yīng)該作為你字典中的一個元素。

<element type="note" access="rw">
    <cocoa key="notes"/>
</element>`

Cocoa 腳本使用 KVC,字典用來指定鍵的名稱。

Note 類

<class name="note" code="NOTE" description="A note" inherits="item" plural="notes">
    <cocoa class="NLNote"/>`

上面的編碼是 NOTE。這幾乎可以是任何東西,但是請注意,Apple 保留所有的小寫編碼供自己使用,所以 note 是不被允許的。它可以是 NOT*, 或 NoTe, 或 XYzy,或者任何你想要的。(理想情況下自己的編碼不會與其他應(yīng)用的編碼沖突。但是我們沒有辦法確保這一點,所以我們只能夠 猜測。也就是說, 猜想 NOTE 可能并不是一個很好的選擇。)

你的類應(yīng)該繼承自 item。(理論上,你可以讓一個類繼承自你的另一個類,不過我們沒有做過這個嘗試。)

note 類有多個屬性:

<property name="id" code="ID  " type="text" access="r" description="The unique identifier of the note.">
    <cocoa key="uniqueID"/>
</property>
<property name="name" code="pnam" type="text" description="The name of the note — the first line of the text." access="r">
    <cocoa key="title"/>
</property>
<property name="body" code="body" description="The plain text content of the note, including first line and subsequent lines." type="text" access="rw">
    <cocoa key="text"/>
</property>
<property name="creationDate" code="CRdt" description="The date the note was created." type="date" access="r"/>
<property name="archived" code="ARcv" description="Whether or not the note has been archived." type="boolean" access="rw"/>

如果可能,最好為你的對象提供獨一無二的 ID。否則,腳本不得不依賴于可能發(fā)生改變的名字和位置。對唯一的 ID 使用編碼 'ID '。(注意有兩個空格;編碼應(yīng)該是四個字符。)而這個唯一 ID 的名字必須是 id。

只要有意義,提供 name 屬性就是標(biāo)準(zhǔn)的做法,編碼應(yīng)該是 pnam。在 Noteland 中它是一個只讀屬性,因為名稱只是筆記中文本的第一行,而且筆記的文本通過可讀寫的 body 屬性編輯。

對于 creationDatearchived,我們并不需要提供 Cocoa 的鍵元素,因為鍵和屬性名字相同。

注意類型:text, date 和 boolean。AppleScript 支持它們和其它幾個,詳細(xì)地在本文檔中列出。

筆記可以有標(biāo)簽,下面是一個標(biāo)簽元素:

<element type="tag" access="rw">
    <cocoa key="tags"/>
</element>
</class>`

Tag 類

Tags 是 NLTap 對象:

<class name="tag" code="TAG*" description="A tag" inherits="item" plural="tags">
    <cocoa class="NLTag"/>`

Tags 只有兩個屬性,idname

<property name="id" code="ID  " type="text" access="r" description="The unique identifier of the tag.">
    <cocoa key="uniqueID"/>
</property>
<property name="name" code="pnam" type="text" access="rw">
    <cocoa key="name"/>
</property>
</class>

下面的代碼是 Noteland 套件和整個字典的結(jié)束:

    </suite>
</dictionary>

應(yīng)用程序配置

應(yīng)用不是默認(rèn)就能腳本化的。我們在 Xcode 中,需要編輯應(yīng)用的 Info.plist。

因為應(yīng)用使用了一個自定義的 NSApplication 子類,用來提供頂級容器,我們編輯主體類 (NSPrincipalClass) 來聲明 NLApplication (Noteland 的 NSApplication 子類名字)。

我們還添加了一個腳本化的鍵(OSAScriptingDefinition)并且設(shè)置它為 YES。最后,我們添加一個名為(OSAScriptingDefinition) 的鍵來表示腳本定義文件的名字,并將它設(shè)置為 sdef 的文件命名為:noteland.sdef。

代碼

NSApplication 子類

你可能會驚訝竟然只需要寫那么少的代碼。

參見 Noteland 工程中的 NLApplication.m 文件。它惰性地創(chuàng)建了一個筆記數(shù)組且提供了一些 dummy 數(shù)據(jù)。說惰性只是因為它沒有連接腳本支持。

(注意這里沒有對象持久化,因為我想讓 Noteland 盡可能自由,而不僅僅是腳本支持。你可以使用 Core Data 或 archiever(歸檔)或者其它東西來保存數(shù)據(jù)。)

它也可以跳過 dummy 數(shù)據(jù)并提供一個數(shù)組。

在本例中,數(shù)組是 NSMutableArray 類型的。它可以不必是 NSMutableArray,而是一個 NSArray,但這樣的話 Cocoa 腳本在筆記數(shù)組發(fā)生改變時將會替換整個數(shù)組。但是如果我們讓它作為 NSMutableArray 數(shù)組 提供下面兩個方法的話,這個數(shù)組就不必被替換。取而代之,對象將會被添加到可變數(shù)組中,以及從中移除。

- (void)insertObject:(NLNote *)object inNotesAtIndex:(NSUInteger)index {
    [self.notes insertObject:object atIndex:index];
}

- (void)removeObjectFromNotesAtIndex:(NSUInteger)index {
    [self.notes removeObjectAtIndex:index];
}

另外需要注意,筆記數(shù)組在類擴展的 .m 文件中被聲明。不需要將它放到 .h 文件中。因為 Cocoa 腳本使用 KVC,而且不關(guān)心你的header,它會找到這個屬性的。

NLNote 類

NLNote.h 聲明了筆記的各個屬性:uniqueID,textcreationDate,archivedtitletags。

init 方法中設(shè)置 uniqueIDcreationDate,以及將標(biāo)簽數(shù)組設(shè)為空的 NSArray。這次我們使用 NSArray 而不是 NSMutableArray,僅僅為了說明它也可以達(dá)到目的。

tilte 方法返回一個計算后的值:筆記中文本的第一行。(回想一下,這會成為腳本字典的 name。)

要注意 objectSpecifier 方法。這是你的類的關(guān)鍵;腳本支持需要這個使其能夠理解你的對象。

幸運的是,這個方法很容易實現(xiàn)。雖然對象說明符 (object specifiers) 有不同類型,通常情況下最好使用 NSUniqueIDSpecifier,因為它很穩(wěn)定。(其它選項包括:NSNameSpecifier, NSPositionalSpecifier 等。)

對象說明符需要了解容器相關(guān)的東西,而且容器是頂級應(yīng)用的對象。

代碼如下所示:

NSScriptClassDescription *appDescription = (NSScriptClassDescription *)[NSApp classDescription];
return [[NSUniqueIDSpecifier alloc] initWithContainerClassDescription:appDescription containerSpecifier:nil key:@"notes" uniqueID:self.uniqueID];

NSApp 是全局應(yīng)用的對象;我們獲取它的 classDescription。鍵為 @"notes"containerSpecifier 為 nil 指的是頂級(應(yīng)用)的容器, uniqueID 是筆記的 uniqueID

Note 作為容器

我們需要超前考慮一點。標(biāo)簽也會需要 objectSpecifier,而且標(biāo)簽是包含在筆記中的,所以標(biāo)簽需要引用包含它的筆記。

Cocoa 腳本處理標(biāo)簽的創(chuàng)建,但是我們可以重寫讓自己自定義行為的方法。

NSObjectScripting.h 定義了 -newScriptingObjectOfClass:forValueForKey: withContentsValue:properties:。這正是我們需要的。在 NLNote.m 中,它看起來是這樣的:

NLTag *tag = (NLTag *)[super newScriptingObjectOfClass:objectClass forValueForKey:key withContentsValue:contentsValue properties:properties];
tag.note = self;
return tag;

我們使用父類的實現(xiàn)來創(chuàng)建標(biāo)簽,然后設(shè)置標(biāo)簽的 note 屬性為該筆記。為了避免可能的循環(huán)引用,NLTag.h 的 note 是 weak 屬性。

(你可能認(rèn)為這并不太不優(yōu)雅,我們同意這么說。我們希望取代那種為了子類的 objectSpecifiers 而需要存在的容器。像是 objectSpecifierForScriptingObject: 這樣可能會更好。我們提出了一個 bug rdar://17473124。)

NLTag 類

NLTaguniqueID, name, 和 note 屬性。

NLTagobjectSpecifier 在概念上和 NLNote中的代碼相同,除了容器是筆記而不是頂級應(yīng)用類。

它看起來像下面這樣:

NSScriptClassDescription *noteClassDescription = (NSScriptClassDescription *)[self.note classDescription];
NSUniqueIDSpecifier *noteSpecifier = (NSUniqueIDSpecifier *)[self.note objectSpecifier];
return [[NSUniqueIDSpecifier alloc] initWithContainerClassDescription:noteClassDescription containerSpecifier:noteSpecifier key:@"tags" uniqueID:self.uniqueID];

就是這樣。完成了。并沒有太多代碼,大量的工作都是設(shè)計接口和編輯 sdef 文件。

在過去,你需要編寫 Apple event 處理程序,并與 Apple event 描述符和各種一團亂麻的玩意兒一起工作。換句話說,要完成這些你需要走很長的路。值得慶幸的是,現(xiàn)在已經(jīng)不是過去的日子了。

接下來才是有趣的東西。

AppleScript Editor

啟動 Noteland。啟動 /Applications/Utilities/AppleScript Editor.app。

運行下面的腳本:

tell application "Noteland"
    every note
end tell

在底部的結(jié)果窗口中,你會看到下面這樣的信息:

{note id "0B0A6DAD-A4C8-42A0-9CB9-FC95F9CB2D53" of application "Noteland", note id "F138AE98-14B0-4469-8A8E-D328B23C67A9" of application "Noteland"}

當(dāng)然,ID 會有所不同,但是這些跡象表明,它在工作。

試一試這個腳本:

tell application "Noteland"
    name of every note
end tell

你會在結(jié)果窗中看到 {"Note 0", "Note 1"}。

再試一下這個腳本:

tell application "Noteland"
    name of every tag of note 2
end tell

結(jié)果:{"Tiger Swallowtails", "Steak-frites"}

(請注意 AppleScript 數(shù)組是基于 1 的,所以 2 指的是第二個筆記。當(dāng)我們明白這個以后,就一點也不奇怪了)

你也可以創(chuàng)建筆記:

tell application "Noteland"
    set newNote to make new note with properties {body:"New Note" & linefeed & "Some text.", archived:true}
    properties of newNote
end tell

結(jié)果將會是類似這樣的(詳細(xì)信息有相應(yīng)改變):

{creationDate:date "Thursday, June 26, 2014 at 1:42:08 PM", archived:true, name:"New Note", class:note, id:"49D5EE93-655A-446C-BB52-88774925FC62", body:"New Note\nSome text."}`

你還可以創(chuàng)建新的標(biāo)簽:

tell application "Noteland"
    set newNote to make new note with properties {body:"New Note" & linefeed & "Some text.", archived:true}
    set newTag to make new tag with properties {name:"New Tag"} at end of tags of newNote
    name of every tag of newNote
end tell

結(jié)果會是:{"New Tag"}

完美工作!

擴展學(xué)習(xí)

將對象模型腳本化只是添加腳本支持的一部分;你也可以為命令添加支持。例如,Noteland 可以有一個將筆記寫到硬盤文件的導(dǎo)出命令。RSS 閱讀器可能有一個刷新命令,郵件應(yīng)用可能有下載郵件命令,等等。

Matt Neuburg 的 AppleScript 權(quán)威指南 值得一讀,盡管它是 2006 年出版的,但是從那以后并沒有發(fā)生太大的改變。Matt 還寫有一篇 Cocoa 應(yīng)用添加腳本支持的教程。該教程絕對值得一讀,它比這篇文章更加詳細(xì)。

這有一個 WWDC 2014 Session 的視頻,是關(guān)于 JavaScript 的自動化的,其中談到了新的 JavaScript OSA 語言。(多年以前 Apple 曾提出,總有一天會出現(xiàn) AppleScript 的程序員的特有語言,因為自然語言對寫 C 和 C 類語言的人說略有一點怪。JavaScript 可以被認(rèn)為是程序員的特有語言。)

當(dāng)然,Apple 有關(guān)于這些技術(shù)的文檔:

此外,請參閱 Apple 的 Sketch 應(yīng)用,它實現(xiàn)了腳本化。