xcode擴展_如何將Xcode插件轉換為Xcode擴展名

xcode擴展

by Khoa Pham

通過Khoa Pham

如何將Xcode插件轉換為Xcode擴展名 (How to convert your Xcode plugins to Xcode extensions)

Xcode is an indispensable IDE for iOS and macOS developers. From the early days, the ability to build and install custom plugins had given us a huge boost in productivity. It was not long before Apple introduced Xcode extension due to privacy concerns.

Xcode是iOS和macOS開發人員必不可少的IDE。 從早期開始,構建和安裝自定義插件的能力就極大地提高了我們的生產率。 由于隱私方面的考慮,不久之后蘋果推出了Xcode擴展。

I have built a few Xcode plugins and extensions like XcodeWay, XcodeColorSense, XcodeColorSense2, and Xmas. It was a rewarding experience. I learned a lot, and the productivity I gained was considerable. In this post I walkthrough how I converted my Xcode plugins to extensions, and the experience I had in doing so.

我構建了一些Xcode插件和擴展,例如XcodeWay , XcodeColorSense , XcodeColorSense2和Xmas 。 這是一次有益的經歷。 我學到了很多東西,獲得的生產力相當可觀。 在這篇文章中,我將逐步介紹如何將Xcode插件轉換為擴展程序,以及這樣做的經驗。

我的第一個Xcode插件:XcodeWay (My first Xcode plugin: XcodeWay)

I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it
我選擇一個懶惰的人去努力。 因為一個懶惰的人會找到一種簡單的方法

I really like the above quote from Bill Gates. I try to avoid repetitive and boring tasks. Whenever I find myself doing the same tasks again, I write scripts and tools to automate that. Doing this takes some time, but I will be a bit lazier in the near future.

我真的很喜歡比爾·蓋茨的上述話 。 我盡量避免重復和無聊的任務。 每當我發現自己再次執行相同的任務時,我就會編寫腳本和工具來自動執行該任務。 這樣做需要一些時間,但在不久的將來我會變得有些懶惰。

Besides the interest in building open source frameworks and tools, I like to extend the IDE I’m using — mostly Xcode.

除了對構建開源框架和工具感興趣之外,我還喜歡擴展我正在使用的IDE(主要是Xcode)。

I first started iOS development in 2014. I wanted a quick way to navigate to many places right from Xcode with the context of the current project. There are many times we want to:

我于2014年首次開始iOS開發。我想要一種快速的方法,可以從Xcode到當前項目的上下文直接導航到許多地方。 我們想多次:

  • open the current project folder in “Finder” to change some files

    在“ Finder”中打開當前項目文件夾以更改某些文件
  • open Terminal to run some commands

    打開終端以運行一些命令
  • open the current file in GitHub to quickly give the link to a workmate

    在GitHub中打開當前文件,以快速將鏈接提供給同事
  • or to open other folders like themes, plugins, code snippets, device logs.

    或打開其他文件夾,例如主題,插件,代碼段,設備日志。

Every little bit of time we save each day counts.

我們每天節省的每一分時間都很重要。

I thought it would be cool idea to write an Xcode plugin that we can do all above things right inside Xcode. Instead of waiting for other people to do it, I pulled up my sleeve and wrote my first Xcode plugin — XcodeWay— and shared it as open source.

我認為編寫一個Xcode插件是一個很酷的主意,我們可以在Xcode內完成上述所有操作。 我沒有等著別人去做,而是袖手旁觀 ,寫了我的第一個Xcode插件XcodeWay ,并將其作為開源共享。

什么是Xcode插件? (What are Xcode plugins?)

Xcode plugins are not officially supported by Xcode or recommended by Apple. There are no documents about them. The best places we can learn about them are via existing plugins’ source code and a few tutorials.

Xcode官方不支持Xcode插件或Apple不推薦使用。 沒有關于它們的文件。 我們可以通過現有插件的源代碼和一些教程來了解它們的最佳位置。

An Xcode plugin is just a bundle of type xcplugin and is placed at ~/Library/Application Support/Developer/Shared/Xcode/Plug-ins . Xcode, when starting, will load any Xcode plugins present in this folder. Plugins are run in the same process as Xcode, so could do anything as Xcode. A bug in any plugin can cause Xcode to crash.

Xcode插件只是xcplugin類型的捆綁包,位于~/Library/Application Support/Developer/Shared/Xcode/Plug-ins 。 Xcode啟動時將加載此文件夾中存在的所有Xcode插件。 插件與Xcode的運行過程相同,因此可以像Xcode一樣執行任何操作。 任何插件中的錯誤都可能導致Xcode崩潰。

To make an Xcode plugin, create a macOS Bundle with one class that extends from NSObject , and have an initialiser that accepts NSBundle , for example in Xmas:

要制作Xcode插件,請創建一個macOS Bundle ,其中包含一個從NSObject擴展的類,并具有一個接受NSBundle的初始化程序,例如在Xmas中 :

class Xmas: NSObject {
var bundle: NSBundle
init(bundle: NSBundle) {    self.bundle = bundle    super.init()  }}

Inside Info.plist, we need to:

Info.plist內部,我們需要:

  • declare this class as the main entry class for the plugin, and

    將該類聲明為插件的主要入口類,然后
  • that this bundle has no UI, because we create UI controls and add to the Xcode interface during runtime

    該捆綁包沒有UI,因為我們在運行時創建了UI控件并將其添加到Xcode接口
<key>NSPrincipalClass</key><string>Xmas</string><key>XCPluginHasUI</key><false/>

Another problem with Xcode plugins is that we have to continuously update DVTPluginCompatibilityUUIDs . This changes every time a new version of Xcode comes out. Without updating, Xcode will refuse to load the plugin.

Xcode插件的另一個問題是,我們必須不斷更新DVTPluginCompatibilityUUIDs 。 每當新版本的Xcode出現時,這種情況都會改變。 如果不進行更新,Xcode將拒絕加載插件。

Xcode插件可以做什么 (What Xcode plugins can do)

Many developers build Xcode plugins because they miss specific features found in other IDEs like Sublime Text, AppCode, or Atom.

許多開發人員之所以構建Xcode插件,是因為它們缺少其他IDE(例如Sublime Text,AppCode或Atom)中的特定功能。

Since Xcode plugins are loaded in the same process as Xcode, they can do everything that Xcode can. The only limit is our imagination. We can leverage Objective C Runtime to discover private frameworks and functions. Then LLDB and Symbolic breakpoint can be used further to inspect running code and alter their behaviors. We can also use swizzling to change implementation of any running code. Writing Xcode plugins is hard — lots of guessing, and sometimes a good knowledge of assembly is required.

由于Xcode插件的加載過程與Xcode相同,因此它們可以完成Xcode可以執行的所有操作。 唯一的限制是我們的想象力。 我們可以利用Objective C運行時來發現私有框架和功能。 然后,可以進一步使用LLDB和符號斷點來檢查正在運行的代碼并更改其行為。 我們還可以使用swizzling更改任何正在運行的代碼的實現。 編寫Xcode插件很辛苦–猜測很多,有時還需要一定的匯編知識。

In the golden age of plugins, there was a popular plugin manager, which itself was a plugin, called Alcatraz. It could install other plugins, which basically just downloads the xcplugin file and moves this to the Plug Ins folder.

在插件的黃金時代,有一個流行的插件管理器,它本身就是一個名為Alcatraz的插件。 它可以安裝其他插件,這些插件基本上只下載xcplugin文件并將其移至Plug Ins文件夾。

To get a sense of what plugins can do, let’s take a look at some popular plugins.

為了了解插件可以做什么,讓我們看一些流行的插件。

Xvim (Xvim)

First in the list is Xvim, which adds Vim keybindings right inside Xcode. It supports mostly all of the keybindings that we used to have in Terminal.

列表中的第一個是Xvim ,它在Xcode內部添加了Vim綁定。 它幾乎支持我們過去在Terminal中擁有的所有鍵綁定。

SCXcodeMiniMap (SCXcodeMiniMap)

If you miss MiniMap mode in Sublime Text, you can use SCXcodeMiniMap to add a right map panel inside Xcode editor.

如果您錯過了Sublime Text中的MiniMap模式,則可以使用SCXcodeMiniMap在Xcode編輯器中添加一個右側地圖面板。

FuzzyAutocomplete插件 (FuzzyAutocompletePlugin)

Before version 9, Xcode didn’t have proper auto completion — it was just based on prefix. That was where FuzzyAutocompletePlugin shone. It performs fuzzy auto completion based on the hidden IDEOpenQuicklyPattern feature in Xcode.

在版本9之前,Xcode沒有適當的自動完成功能-它僅基于前綴。 那就是FuzzyAutocompletePlugin發光的地方。 它基于Xcode中隱藏的IDEOpenQuicklyPattern功能執行模糊自動完成。

KSImageNamed-Xcode (KSImageNamed-Xcode)

To display a bundle image inside UIImageView, we often use the imageNamed method. But remembering exactly the name of the image file is hard. KSImageNamed-Xcode is here to help. You will get a list of auto-suggested image names when you begin to type.

為了在UIImageView顯示捆綁圖像,我們經常使用imageNamed方法。 但是準確記住圖像文件的名稱很困難。 KSImageNamed-Xcode在這里可以提供幫助。 開始鍵入時,您將獲得一個自動建議的圖像名稱列表。

Xcode的ColorSense (ColorSense-for-Xcode)

Another itch during development is to work with UIColor , which uses RGBA color space. We don’t get a visual indicator of the color that we specify, and manually performing checking can be time consuming. Luckily there is ColorSense-for-Xcode which shows the color being used and the color picker panel to easily select the right color.

開發過程中的另一個難題是使用UIColor ,后者使用RGBA顏色空間。 我們沒有看到指定顏色的視覺指示器,并且手動執行檢查可能很耗時。 幸運的是,這里有ColorSense-for-Xcode ,它可以顯示正在使用的顏色,而顏色選擇器面板則可以輕松選擇正確的顏色。

鏈接控制臺 (LinkedConsole)

In AppCode, we can jump to a specific line in the file that is logged inside the console. If you miss this feature in Xcode, you can use LinkedConsole. This enables clickable links inside Xcode console so we can jump to that file instantly.

在AppCode中,我們可以跳到控制臺內部記錄的文件中的特定行。 如果您錯過了Xcode中的此功能,則可以使用LinkedConsole 。 這將在Xcode控制臺中啟用可單擊的鏈接,因此我們可以立即跳轉到該文件。

Xcode插件背后的艱苦工作 (The hard work behind Xcode plugins)

Making an Xcode plugin is not easy. Not only do we need to know macOS programming, but we also need to dive deep into Xcode view hierarchy. We need to explore private frameworks and APIs in order to inject the feature we want.

制作Xcode插件并不容易。 我們不僅需要了解macOS編程,而且還需要深入了解Xcode視圖層次結構。 我們需要探索私有框架和API,以便注入我們想要的功能。

There are very few tutorials on how to make plugins but, luckily, most plugins are open source so we can understand how they work. Since I have made a few plugins, I can give some technical details about them.

關于如何制作插件的教程很少,但幸運的是,大多數插件都是開源的,因此我們可以了解它們的工作原理。 由于我做了一些插件,因此我可以提供一些有關它們的技術細節。

Xcode plugins are done usually with two private frameworks: DVTKit and IDEKit . System frameworks are at /System/Library/PrivateFrameworks but the frameworks that Xcode uses exclusively are under /Applications/Xcode.app/Contents/ , there you can find Frameworks , OtherFrameworks and SharedFrameworks.

Xcode插件通常使用兩個私有框架完成: DVTKitIDEKit 。 系統框架位于/System/Library/PrivateFrameworks但Xcode專用的框架位于/Applications/Xcode.app/Contents/下,您可以在其中找到FrameworksOtherFrameworksSharedFrameworks

There is a tool class-dump that can generate headers from the Xcode app bundle. With the class names and methods, you can call NSClassFromString to get the class from the name.

有一個工具類轉儲 ,可以從Xcode應用程序捆綁包生成標頭。 使用類名和方法,可以調用NSClassFromString從名稱中獲取類。

Xmas中令人費解的DVTBezelAlertPanel框架 (Swizzling DVTBezelAlertPanel framework in Xmas)

Christmas has always given me a special feeling, so I decided to make Xmas, which shows a random Christmas picture instead of the default alert view. The class used to render that view is DVTBezelAlertPanel inside the DVTKit framework. My article on building that plugin is here.

圣誕節總是給我一種特殊的感覺,所以我決定制作Xmas ,它顯示隨機的圣誕節圖片而不是默認的警報視圖。 用于渲染視圖類是DVTBezelAlertPanel的DVTKit框架內。 我有關構建該插件的文章在這里。

With Objective C Runtime, there is a technique called swizzling, which can change and switch implementation and method signature of any running classes and methods.

使用Objective C Runtime,有一種稱為swizzling的技術,它可以更改和切換任何正在運行的類和方法的實現以及方法簽名。

Here, in order to change the content of that alert view, we need to swap the initialiser initWithIcon:message:parentWindow:duration: with our own method. We do that early by listening to NSApplicationDidFinishLaunchingNotification which is notified when a macOS plugin, in this case Xcode, launches.

在這里,為了更改該警報視圖的內容,我們需要使用我們自己的方法交換初始化程序 initWithIcon:message:parentWindow:duration: 我們通過偵聽NSApplicationDidFinishLaunchingNotification此操作,當macOS插件(在本例中為Xcode)啟動時會收到通知。

class func swizzleMethods() {    guard let originalClass = NSClassFromString("DVTBezelAlertPanel") as? NSObject.Type else {        return    }
do {        try originalClass.jr_swizzleMethod("initWithIcon:message:parentWindow:duration:",            withMethod: "xmas_initWithIcon:message:parentWindow:duration:")    }    catch {        Swift.print("Swizzling failed")    }}

I initially liked to do everything in Swift. But it’s tricky to use the swizzle init method in Swift, so the quickest way is to do that in Objective C. Then we simply traverse the view hierarchy to find the NSVisualEffectView inside NSPanel to update the image.

我最初喜歡在Swift中做所有事情。 但是在Swift中使用s wizzle init方法很棘手,因此最快的方法是在Objective C中做到這一點。 然后,我們只需遍歷視圖層次結構, NSVisualEffectViewNSPanel找到NSPanel來更新圖像。

與XcodeColorSense中的DVTSourceTextView進行交互 (Interacting with DVTSourceTextView in XcodeColorSense)

I work mostly with hex colors and I want a quick way to see the color. So I built XcodeColorSense — it supports hex color, RGBA, and named color.

我主要處理十六進制顏色,我想快速查看顏色。 因此,我構建了XcodeColorSense-它支持十六進制顏色,RGBA和命名的顏色。

The idea is simple. Parse the string to see if the user is typing something related to UIColor, and show a small overlay view with that color as background. The text view that Xcode uses is of type DVTSourceTextView in DVTKit framework. We also need to listen to NSTextViewDidChangeSelectionNotification which is triggered whenever any NSTextView content is changed.

這個想法很簡單。 解析字符串以查看用戶是否正在輸入與UIColor相關的內容,并顯示一個以該顏色為背景的小疊加視圖。 該Xcode使用文本視圖是一個類型的DVTSourceTextViewDVTKit框架。 我們還需要收聽NSTextViewDidChangeSelectionNotification ,只要更改任何NSTextView內容,就會觸發該事件。

func listenNotification() {  NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(handleSelectionChange(_:)), name: NSTextViewDidChangeSelectionNotification, object: nil)}
func handleSelectionChange(note: NSNotification) {  guard let DVTSourceTextView = NSClassFromString("DVTSourceTextView") as? NSObject.Type,    object = note.object where object.isKindOfClass(DVTSourceTextView.self),    let textView = object as? NSTextView  else { return }
self.textView = textView}

I had a Matcher architecture so we can detect different kinds of UIColor constructions — for example HexMatcher .

我有一個Matcher架構,因此我們可以檢測到不同種類的UIColor構造-例如HexMatcher

public struct HexMatcher: Matcher {
func check(line: String, selectedText: String) -> (color: NSColor, range: NSRange)? {    let pattern1 = "\"#?[A-Fa-f0-9]{6}\""    let pattern2 = "0x[A-Fa-f0-9]{6}"
let ranges = [pattern1, pattern2].flatMap {      return Regex.check(line, pattern: $0)    }
guard let range = ranges.first      else { return nil }
let text = (line as NSString).substringWithRange(range).replace("0x", with: "").replace("\"", with: "")    let color = NSColor.hex(text)
return (color: color, range: range)  }}

To render the overlay, we use NSColorWell which is good for showing a view with background. The position is determined by calling firstRectForCharacterRange and some point conversions with convertRectFromScreen and convertRect .

要渲染疊加層,我們使用NSColorWell ,它非常適合顯示帶有背景的視圖。 通過調用firstRectForCharacterRange并通過convertRectFromScreenconvertRect一些點轉換來確定位置。

在XcodeWay中使用NSTask和IDEWorkspaceWindowController (Using NSTask and IDEWorkspaceWindowController in XcodeWay)

Finally, my beloved XcodeWay.

最后,我心愛的XcodeWay 。

I found myself needing to go to different places from Xcode with the context of the current project. So I built XcodeWay as a plugin that adds lots of handy menu options under Window.

我發現自己需要根據當前項目的上下文從Xcode轉到其他地方。 因此,我將XcodeWay構建為插件,在Window下添加了許多方便的菜單選項。

Since the plugin runs in the same Xcode process, it has access to the main menu NSApp.mainMenu?.itemWithTitle(“Window”) . There we can alter the menu. XcodeWay is designed to easily extend functionalities through its Navigator protocol.

由于插件在相同的Xcode進程中運行,因此可以訪問主菜單NSApp.mainMenu?.itemWithTitle(“Window”) 。 在那里我們可以更改菜單。 XcodeWay旨在通過其Navigator協議輕松擴展功能。

@objc protocol Navigator: NSObjectProtocol {  func navigate()  var title: String { get }}

For folders with a static path like Provisioning Profile ~/Library/MobileDevice/Provisioning Profiles or User data Developer/Xcode/UserData , we can just construct the URL and call NSWorkspace.sharedWorkspace().openURL . For dynamic folders that vary depending on the current project, more work needs to be done.

對于具有靜態路徑的文件夾,例如Provisioning ~/Library/MobileDevice/Provisioning Profiles或User data Developer/Xcode/UserData ,我們可以僅構造URL并調用NSWorkspace.sharedWorkspace().openURL 。 對于根據當前項目而變化的動態文件夾,需要做更多的工作。

How do we open the folder for the current project in Finder? The information for the current project path is kept inside IDEWorkspaceWindowController . This is a class that manages workspace windows in Xcode. Take a look at EnvironmentManager where we use objc_getClass to get the class definition from a string.

我們如何在Finder中打開當前項目的文件夾? 當前項目路徑的信息保存在IDEWorkspaceWindowController 。 這是一個管理Xcode中的工作區窗口的類。 看一下EnvironmentManager ,我們在其中使用objc_getClass從字符串獲取類定義。

self.IDEWorkspaceWindowControllerClass = objc_getClass("IDEWorkspaceWindowController");
NSArray *workspaceWindowControllers = [self.IDEWorkspaceWindowControllerClass valueForKey:@"workspaceWindowControllers"];
id workSpace = nil;
for (id controller in workspaceWindowControllers) {  if ([[controller valueForKey:@"window"] isEqual:[NSApp keyWindow]]) {    workSpace = [controller valueForKey:@"_workspace"];  }}
NSString * path = [[workSpace valueForKey:@"representingFilePath"] valueForKey:@"_pathString"];

Finally, we can utilise valueForKey to get the value for any property that we think exists. This way not only do we get the project path, we also get the path to the opening file. So we can call activateFileViewerSelectingURLs on NSWorkspace to open Finder with that file selected. This is handy as users don’t need to look for that file in Finder.

最后,我們可以利用valueForKey來獲取我們認為存在的任何屬性的值。 這樣,我們不僅可以獲取項目路徑 ,還可以獲取打開文件的路徑。 因此,我們可以在NSWorkspace上調用activateFileViewerSelectingURLs來打開選擇了該文件的Finder。 這很方便,因為用戶不需要在Finder中查找該文件。

Many times we want to execute some Terminal commands on the current project folder. To achieve that, we can use NSTask with launch pad /usr/bin/open and arguments [@”-a”, @”Terminal”, projectFolderPath] . iTerm, if configured probably, will open this in a new tab.

很多時候,我們想在當前項目文件夾中執行一些終端命令。 為此,我們可以將NSTask與啟動板/usr/bin/open和參數[@”-a”, @”Terminal”, projectFolderPath] 。 iTerm(如果已配置)將在新選項卡中將其打開。

The documents for iOS 7 apps are placed in the fixed location iPhone Simulator inside Application Support. But, from iOS 8, every app has a unique UUID and their document folders are hard to predict.

iOS 7應用程序的文檔放在應用程序支持內的固定位置iPhone Simulator 。 但是,從iOS 8開始,每個應用程序都具有唯一的UUID,并且其文檔文件夾很難預測。

~/Library/Developer/CoreSimulator/Devices/1A2FF360-B0A6-8127-95F3-68A6AB0BCC78/data/Container/Data/Application/

We can build a map and perform tracking to find the generated ID for the current project, or to check the plist inside each folder to compare the bundle identifier.

我們可以構建地圖并執行跟蹤以找到當前項目的生成ID,或者檢查每個文件夾中的plist以比較包標識符。

The quick solution that I came up with was to search for the most recent updated folder. Every time we build the project, or make changes inside the app, their document folder is updated. That is where we can make use of NSFileModificationDate to find the folder for the current project.

我想到的快速解決方案是搜索最近更新的文件夾。 每次我們構建項目或在應用程序中進行更改時,其文檔文件夾都會更新。 那是我們可以使用NSFileModificationDate查找當前項目的文件夾的地方。

There are many hacks when working with Xcode plugins, but the results are rewarding. Every few minutes we save each day end up saving a lot of time overall.

使用Xcode插件時有很多技巧,但結果是可喜的。 我們每天節省的每一分鐘最終節省了很多時間。

安全與自由 (Security and freedom)

With great power comes great responsibility. The fact that plugins can do whatever they want rings an alert to security. In late 2015, there was a malware attack by distributing a modified version of Xcode, called XcodeGhost, which injects malicious code into any apps built with Xcode Ghost. The malware is believed to use the plugin mechanism among other things.

擁有權利的同時也被賦予了重大的責任。 插件可以做任何他們想做的事情,這一事??實提醒了安全性。 在2015年末,通過分發Xcode的修改版本XcodeGhost進行了惡意軟件攻擊,該代碼將惡意代碼注入到使用Xcode Ghost構建的任何應用程序中。 據信該惡意軟件除其他外還使用了插件機制。

Like the iOS apps we download from the Appstore, macOS apps like Xcode are signed by Apple when we download them from the Mac Appstore or through official Apple download links.

就像我們從Appstore下載的iOS應用程序一樣,當我們從Mac Appstore或通過Apple官方下載鏈接下載macOS應用程序(如Xcode)時,它們也會由Apple 簽名 。

Code signing your app assures users that it is from a known source and the app hasn’t been modified since it was last signed. Before your app can integrate app services, be installed on a device, or be submitted to the App Store, it must be signed with a certificate issued by Apple

對您的應用程序進行代碼簽名可確保用戶來自已知來源,并且自上次簽名以來未對其進行過修改。 您的應用程序可以集成應用程序服務,安裝在設備上或提交到App Store之前,必須使用Apple發行的證書進行簽名

To avoid potential malware like this, at WWDC 2016 Apple announced the Xcode Source Editor Extension as the only way to load third party extensions into Xcode. This means that, from Xcode 8, plugins can’t be loaded.

為了避免此類潛在的惡意軟件,Apple在WWDC 2016上宣布了Xcode Source Editor Extension ,這是將第三方擴展加載到Xcode中的唯一方法。 這意味著無法從Xcode 8加載插件。

源代碼編輯器擴展 (Source Editor Extension)

Extension is the recommended approach to safely add functionalities in restricted ways.

建議使用擴展來以受限方式安全添加功能。

App extensions give users access to your app’s functionality and content throughout iOS and macOS. For example, your app can now appear as a widget on the Today screen, add new buttons in the Action sheet, offer photo filters within the Photos app, or display a new system-wide custom keyboard.

應用程序擴展使用戶可以在整個iOS和macOS中訪問您應用程序的功能和內容。 例如,您的應用現在可以在“今日”屏幕上顯示為小部件,在“操作”表中添加新按鈕,在“照片”應用中提供照片濾鏡或顯示新的系統范圍的自定義鍵盤。

For now, the only extension to Xcode is Source Editor, which allows us to read and modify contents of a source file, as well as read and modify the current text selection within the editor.

目前,Xcode的唯一擴展是Source Editor,它使我們能夠讀取和修改源文件的內容,以及讀取和修改編輯器中的當前文本選擇。

Extension is a new target and runs in a different process than Xcode. This is good in that it can’t alter Xcode in any ways other than conforming to XCSourceEditorCommand to modify the current document content.

擴展是一個新的目標,并且在與Xcode不同的過程中運行。 這樣做的好處是,除了遵循XCSourceEditorCommand來修改當前文檔內容外,它無法以其他任何方式更改Xcode。

protocol XCSourceEditorCommand {
func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -&gt; Void)}

Xcode 8 has lots of improvements like the new code completion features, Swift image and color literals, and snippets. This led to the deprecation of many Xcode plugins. For some indispensable plugins like XVim, this is unbearable for some people. Some old plugin features can’t be achieved with the current Source Editor Extension system.

Xcode 8進行了許多改進,例如新的代碼完成功能,Swift圖像和顏色文字以及代碼片段。 這導致了許多Xcode插件的棄用。 對于某些不可缺少的插件(例如XVim),這對于某些人來說是無法忍受的。 當前的Source Editor Extension系統無法實現某些舊的插件功能。

除非您辭職Xcode (Unless you resign Xcode)

A workaround to bypass the restriction from Xcode 8 for plugins, is to replace the existing Xcode signature by a technique called resign. Resigning is very easy — we just need to create a self-signed certificate and call the codesign command. After this, Xcode should be able to load plugins.

繞過插件的Xcode 8限制的一種解決方法是,使用稱為resign的技術替換現有的Xcode簽名。 簽名非常容易-我們只需要創建一個自簽名證書并調用codesign命令即可。 此后,Xcode應該能夠加載插件。

codesign -f -s MySelfSignedCertificate /Applications/Xcode.app

It is, however, not possible to submit apps built with resigned Xcode as the signature does not match the official version of Xcode. One way is to use two Xcodes: one official for distribution and one resigned for development.

但是,由于簽名與Xcode的正式版本不匹配, 因此無法提交使用已簽名的Xcode構建的應用程序。 一種方法是使用兩個Xcode:一個用于發布的官方文件,一個用于開發的辭職文件。

移至Xcode擴展 (Moving to Xcode extension)

Xcode extension is the way to go, so I started moving my plugins to extension. For Xmas, since it modifies view hierarchy, it can’t become an extension.

Xcode擴展是必經之路,因此我開始將插件移至擴展。 對于Xmas,由于它修改了視圖層次結構,因此不能成為擴展。

XcodeColorSense2中的顏色文字 (Color literal in XcodeColorSense2)

For the color sense, I rewrote the extension from scratch, and called it XcodeColorSense2. This, of course, can’t show an overlay over the current editor view. So I chose to utilize the new Color literal found in Xcode 8+.

對于顏色,我從頭開始重寫了擴展名,并將其命名為XcodeColorSense2 。 當然,這不能在當前編輯器視圖上顯示覆蓋。 因此,我選擇使用Xcode 8+中新的Color literal

The color is shown in a small box. It may be hard to distinguish similar colors, so that’s why I also include the name. The code is simply about inspecting selections and parsing to find the color declaration.

顏色顯示在一個小框中。 區分相似的顏色可能很困難,因此這就是為什么我也要包括該名稱的原因。 該代碼僅涉及檢查selections和解析以查找顏色聲明。

func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void {    guard let selection = invocation.buffer.selections.firstObject as? XCSourceTextRange else {      completionHandler(nil)      return    }
let lineNumber = selection.start.line
guard lineNumber < invocation.buffer.lines.count,      let line = invocation.buffer.lines[lineNumber] as? String else {      completionHandler(nil)      return    }
guard let hex = findHex(string: line) else {      completionHandler(nil)      return    }
let newLine = process(line: line, hex: hex)
invocation.buffer.lines.replaceObject(at: lineNumber, with: newLine)
completionHandler(nil)  }}

Most of the functionality is embedded inside my framework Farge, but I can’t find a way to use the framework inside Xcode extension.

大多數功能都嵌入在我的框架Farge中 ,但是我找不到在Xcode擴展中使用該框架的方法。

Since the extension feature is only accessible through the Editor menu, we can customise a key binding to invoke this menu item. For example I choose Cmd+Ctrl+S to show and hide color information.

由于只能通過“編輯器”菜單訪問擴展功能,因此我們可以自定義按鍵綁定來調用此菜單項。 例如,我選擇Cmd+Ctrl+S來顯示和隱藏顏色信息。

This is, of course, not intuitive compared to the original plugin, but it’s better than nothing.

與原始插件相比,這當然不直觀,但是總比沒有好。

如何調試Xcode擴展 (How to debug Xcode extensions)

Working and debugging extensions is straightforward. We can use Xcode to debug Xcode. The debugged version of Xcode has a gray icon.

工作和調試擴展很簡單。 我們可以使用Xcode調試Xcode。 Xcode的調試版本帶有灰色圖標。

如何安裝Xcode擴展 (How to install Xcode extensions)

The extension must have an accompanying macOS app. This can be distributed to Mac Appstore or self-signed. I’ve written an article on how to do this.

該擴展程序必須具有隨附的macOS應用程序。 它可以分發到Mac Appstore或自簽名。 我寫了一篇有關如何做到這一點的文章 。

All extensions for an app need to be explicitly enabled through “System Preferences”.

必須通過“系統偏好設置”顯式啟用應用程序的所有擴展。

The Xcode extension only works with editor for now, so we must open a source file for the Editor menu to have effect.

Xcode擴展名目前僅適用于編輯器,因此我們必須打開源文件以使“ Editor菜單生效。

XcodeWay中的AppleScript (AppleScript in XcodeWay)

In Xcode extensions, NSWorkspace, NSTask and private class construction don’t work anymore. Since I have used Finder Sync Extension in FinderGo, I thought I could try the same AppleScript scripting for Xcode extension.

在Xcode擴展中, NSWorkspaceNSTask和私有類構造不再起作用。 由于我已經在FinderGo中使用了Finder Sync Extension, 因此我認為我可以為Xcode擴展嘗試相同的AppleScript腳本。

AppleScript is a scripting language created by Apple. It allows users to directly control scriptable Macintosh applications, as well as parts of macOS itself. You can create scripts — sets of written instructions — to automate repetitive tasks, combine features from multiple scriptable applications, and create complex workflows.

AppleScript是Apple創建的一種腳本語言。 它允許用戶直接控制可編寫腳本的Macintosh應用程序以及macOS本身的一部分。 您可以創建腳本(一組書面說明)來自動化重復的任務,組合來自多個可編寫腳本的應用程序中的功能以及創建復雜的工作流程。

To try AppleScript, you can use the app Script Editor built inside macOS to write prototype functions. Function declaration starts with on and ends with end . To avoid potential conflicts with system functions, I usually use my as a prefix. Here is how I rely on System Events to get the home directory.

要嘗試AppleScript,可以使用macOS內置的應用程序腳本編輯器編寫原型函數。 函數聲明以on開頭,以end 。 為了避免與系統功能發生潛在沖突,我通常使用my作為前綴。 這是我依靠系統事件獲取主目錄的方式。

User interface scripting terminology is found in the “Processes Suite” of the “System Events” scripting dictionary. This suite includes terminology for interacting with most types of user interface elements, including:

用戶界面腳本術語可在“系統事件”腳本字典的“進程套件”中找到。 該套件包括用于與大多數類型的用戶界面元素進行交互的術語,包括:

  • windows

    視窗
  • buttons

    紐扣
  • checkboxes

    復選框
  • menus

    菜單
  • radio buttons

    單選按鈕
  • text fields.

    文本字段。

In System Events, the process class represents a running app.

在系統事件中, process類表示一個正在運行的應用程序。

Many good citizen apps support AppleScript by exposing some of their functionalities, so these can be used by other apps. Here is how I get the current song from Spotify in Lyrics.

許多優秀的公民應用程序通過公開其某些功能來支持AppleScript,因此它們可以被其他應用程序使用。 這是我從Spotify的Lyrics中獲取當前歌曲的方式。

tell application "Spotify"  set trackId to id of current track as string  set trackName to name of current track as string  set artworkUrl to artwork url of current track as string  set artistName to artist of current track as string  set albumName to album of current track as string  return trackId & "---" & trackName & "---" & artworkUrl & "---" & artistName & "---" & albumNameend tell

To get all the possible commands of a certain app, we can open the dictionary in Script Editor. There we can learn about which functions and parameters are supported.

要獲取某個應用程序的所有可能命令,我們可以在腳本編輯器中打開字典。 在這里,我們可以了解支持哪些功能和參數。

If you think Objective C is hard, AppleScript is much harder. The syntax is verbose and error-prone. For your reference, here is the whole script file that powers XcodeWay.

如果您認為Objective C很難,AppleScript就會困難得多。 語法冗長且容易出錯。 供您參考,這是為XcodeWay提供支持的整個腳本文件 。

To open a certain folder, tell Finder using POSIX file. I refactor every functionality into function for better code reuse.

要打開某個文件夾,請使用POSIX file告訴Finder 。 我將每個功能重構為功能,以實現更好的代碼重用。

on myOpenFolder(myPath)tell application "Finder"activateopen myPath as POSIX fileend tellend myOpenFolder

Then, to run AppleScript inside a macOS app or extension, we need to construct an AppleScript descriptor with the correct process serial number and event identifiers.

然后,要在macOS應用程序或擴展中運行AppleScript,我們需要使用正確的進程序列號和事件標識符構造一個AppleScript描述符。

func eventDescriptior(functionName: String) -> NSAppleEventDescriptor {  var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess))  let target = NSAppleEventDescriptor(    descriptorType: typeProcessSerialNumber,    bytes: &psn,    length: MemoryLayout<ProcessSerialNumber>.size  )
let event = NSAppleEventDescriptor(    eventClass: UInt32(kASAppleScriptSuite),    eventID: UInt32(kASSubroutineEvent),    targetDescriptor: target,    returnID: Int16(kAutoGenerateReturnID),    transactionID: Int32(kAnyTransactionID)  )
let function = NSAppleEventDescriptor(string: functionName)  event.setParam(function, forKeyword: AEKeyword(keyASSubroutineName))
return event}

Other tasks, like checking the current Git remote, are a bit trickier. Many times I want to share the link of the file I’m debugging to my remote teammate, so they know what file I’m referencing. This is doable by using shell script inside AppleScript .

其他任務,例如檢查當前的Git遙控器,則有些棘手。 很多時候,我想與遠程隊友共享正在調試的文件的鏈接,以便他們知道我正在引用的文件。 這可以通過在AppleScript使用shell script來實現。

on myGitHubURL()set myPath to myProjectPath()set myConsoleOutput to (do shell script "cd " & quoted form of myPath & "; git remote -v")set myRemote to myGetRemote(myConsoleOutput)set myUrl to (do shell script "cd " & quoted form of myPath & "; git config --get remote." & quoted form of myRemote & ".url")set myUrlWithOutDotGit to myRemoveSubString(myUrl, ".git")end myGitHubURL

We can use quoted and string concatenation to form strings. Luckily we can expose Foundation framework and certain classes. Here is how I expose NSString to take advantage of all existing functionalities. Writing string manipulation from scratch using plain AppleScript will take lots of time.

我們可以使用quoted和字符串串聯來形成字符串。 幸運的是,我們可以公開Foundation框架和某些類。 這是我公開NSString以利用所有現有功能的方式。 使用普通的AppleScript從頭開始編寫字符串操作將花費大量時間。

use scripting additionsuse framework "Foundation"property NSString : a reference to current application's NSString

With this we can build our other functions for string handling.

這樣我們就可以構建其他用于字符串處理的函數。

on myRemoveLastPath(myPath)set myString to NSString's stringWithString:myPathset removedLastPathString to myString's stringByDeletingLastPathComponentremovedLastPathString as textend myRemoveLastPath

One cool feature that XcodeWay supports is the ability to go to the document directory for the current app in the simulator. This is handy when we need to inspect a document to check saved or cached data. The directory is dynamic so it’s hard to detect. We can, however, sort the directory for the most recently updated. Below is how we chain multiple shell scripts commands to find the folder.

XcodeWay支持的一項很酷的功能是能夠轉到模擬器中當前應用程序的文檔目錄。 當我們需要檢查文檔以檢查保存或緩存的數據時,這非常方便。 該目錄是動態的,因此很難檢測到。 但是,我們可以對最新更新的目錄進行排序。 下面是我們如何鏈接多個shell scripts命令來查找文件夾的方法。

on myOpenDocument()set command1 to "cd ~/Library/Developer/CoreSimulator/Devices/;"set command2 to "cd `ls -t | head -n 1`/data/Containers/Data/Application;"set command3 to "cd `ls -t | head -n 1`/Documents;"set command4 to "open ."do shell script command1 & command2 & command3 & command4end myOpenDocument

This feature helped me a lot when developing Gallery to check whether videos and downloaded images are saved in the correct place.

開發Gallery來檢查視頻和下載的圖像是否保存在正確的位置時,此功能對我有很大幫助。

However, none of the scripts seem to work. Scripting has always been part of macOS since 1993. But, with the advent of the Mac Appstore and security concerns, AppleScript finally got restricted in mid 2012. That was when App Sandbox was enforced.

但是,這些腳本似乎都不起作用。 自1993年以來,腳本一直是macOS的一部分。但是,隨著Mac Appstore的問世和安全方面的考慮,AppleScript終于在2012年中期受到限制。

應用沙箱 (App Sandbox)

App Sandbox is an access control technology provided in macOS, enforced at the kernel level. It is designed to contain damage to the system and the user’s data if an app becomes compromised. Apps distributed through the Mac App Store must adopt App Sandbox.

App Sandbox是macOS中提供的一種訪問控制技術,在內核級別實施。 如果應用程序被盜用,它旨在包含對系統和用戶數據的破壞。 通過Mac App Store分發的應用程序必須采用App Sandbox 。

For an Xcode extension to be loaded by Xcode, it must also support App Sandbox.

對于要由Xcode加載的Xcode擴展,它還必須支持App Sandbox。

At the beginning of App Sandbox enforcement, we could use App Sandbox Temporary Exception to temporarily grant our app access to Apple Script.

在應用程序沙箱強制執行開始時,我們可以使用應用程序沙箱臨時異常臨時授予我們的應用程序對Apple Script的訪問權限。

This is now not possible.

現在這是不可能的。

The only way for AppleScript to run is if it resides inside ~/Library/Application Scripts folder.

AppleScript的唯一運行方式是駐留在~/Library/Application Scripts文件夾中。

如何安裝自定義腳本 (How to install custom scripts)

macOS apps or extensions can’t just install scripts into the Application Scripts by themselves. They need user consent.

macOS應用程序或擴展不能僅將腳本自身安裝到應用程序腳本中。 他們需要用戶的同意。

One possible way to do that is to enable Read/Write and show a dialog using NSOpenPanel to ask user to select the folder to install our scripts.

一種可行的方法是啟用“ Read/Write并使用NSOpenPanel顯示對話框,要求用戶選擇文件夾來安裝我們的腳本。

For XcodeWay, I choose to provide an install shell script so the user has a quick way to install scripts.

對于XcodeWay,我選擇提供安裝Shell腳本,以便用戶快速安裝腳本。

#!/bin/bash
set -euo pipefail
DOWNLOAD_URL=https://raw.githubusercontent.com/onmyway133/XcodeWay/master/XcodeWayExtensions/Script/XcodeWayScript.scptSCRIPT_DIR="${HOME}/Library/Application Scripts/com.fantageek.XcodeWayApp.XcodeWayExtensions"
mkdir -p "${SCRIPT_DIR}"curl $DOWNLOAD_URL -o "${SCRIPT_DIR}/XcodeWayScript.scpt"

AppleScript is very powerful. All of this is made explicit so the user has complete control over which things can be done.

AppleScript非常強大。 所有這些都是明確的,因此用戶可以完全控制哪些事情可以完成。

Like an extension, a script is done asynchronously in a different process using XPC for inter process communication. This enhances security as a script has no access to the address space to our app or extension.

像擴展一樣,使用XPC在進程間進行通信的不同進程中異步完成腳本。 由于腳本無法訪問我們的應用或擴展程序的地址空間,因此可以提高安全性。

macOS Mojave中的更高安全性 (More security in macOS Mojave)

This year, at WWDC 2018, Apple introduced macOS Mojave which focuses on lots of security enhancements. In the Your Apps and the Future of macOS Security we can learn more about new security requirement for macOS apps. One of them is the usage description for AppleEvents.

今年,在WWDC 2018上,Apple推出了macOS Mojave,它專注于許多安全性增強功能。 在您的應用程序和macOS安全性的未來中,我們可以了解有關macOS應用程序的新安全性要求的更多信息。 其中之一是AppleEvents的用法說明。

unable to load info.plist exceptions (egpu overrides)
無法加載info.plist異常(egpu覆蓋)

We used to declare usage description for many permissions in iOS, like photo library, camera, and push notifications. Now we need to declare the usage description for AppleEvents.

我們曾經在iOS中聲明許多權限的使用情況描述,例如照片庫,相機和推送通知。 現在我們需要聲明AppleEvents的用法描述。

The first time our extension tries to execute some AppleScript commands, the above dialog is shown to ask for user consent. User can grant or deny permission, but for Xcode please say yes ?

我們的擴展程序第一次嘗試執行某些AppleScript命令時,將顯示以上對話框,要求用戶同意。 用戶可以授予或拒絕權限,但是對于Xcode,請說是?

The fix for us is to declare NSAppleEventsUsageDescription in our app target. We only need to declare in the app target, not in the extension target.

我們的解決方法是在應用程序目標中聲明NSAppleEventsUsageDescription 。 我們只需要在應用程序目標中聲明,而無需在擴展目標中聲明。

<key>NSAppleEventsUsageDescription</key><string>Use AppleScript to open folders</string>

從這往哪兒走 (Where to go from here)

Huff huff, whew! Thanks for following such a long journey. Making frameworks and tools take lots of time, especially plugins and extensions — we have to continuously change to adapt them to new operating systems and security requirements. But it is a rewarding process, as we’ve learned more and have some tools to save our precious time.

呼呼,呼呼! 感謝您經過如此漫長的旅程。 制作框架和工具需要花費大量時間,尤其是插件和擴展-我們必須不斷進行更改以使它們適應新的操作系統和安全性要求。 但這是一個有益的過程,因為我們已經了解了更多,并擁有一些工具來節省寶貴的時間。

For your reference, here are my extensions which are fully open source.

供您參考,這是我的擴展程序,它們是完全開源的。

  • XcodeWay

    XcodeWay

  • XcodeColorSense2

    XcodeColorSense2

I hope you find something useful in the post. Here are some resources to help explore Xcode extensions further:

希望您在帖子中找到有用的信息。 以下是一些資源,可幫助您進一步探索Xcode擴展:

  • Xcode Plugins by NSHipster

    NSHipster的Xcode插件

  • Writing Xcode plugin in Swift

    在Swift中編寫Xcode插件

  • Xcode 8 Plugins (Alcatraz) — The end of an era

    Xcode 8插件(惡魔島)—時代的終結

  • Using and Extending the Xcode Source Editor

    使用和擴展Xcode源代碼編輯器

  • Why do I need to resign Xcode to use XVim2

    為什么我需要辭職Xcode才能使用XVim2

If you like this post, consider visiting my other articles and apps ?

如果您喜歡這篇文章,請考慮訪問我的其他文章和應用程序 ?

翻譯自: https://www.freecodecamp.org/news/how-to-convert-your-xcode-plugins-to-xcode-extensions-ac90f32ae0e3/

xcode擴展

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/392533.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/392533.shtml
英文地址,請注明出處:http://en.pswp.cn/news/392533.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode 861. 翻轉矩陣后的得分(貪心算法)

有一個二維矩陣 A 其中每個元素的值為 0 或 1 。 移動是指選擇任一行或列&#xff0c;并轉換該行或列中的每一個值&#xff1a;將所有 0 都更改為 1&#xff0c;將所有 1 都更改為 0。 在做出任意次數的移動后&#xff0c;將該矩陣的每一行都按照二進制數來解釋&#xff0c;矩…

數據分析團隊的價值_您的數據科學團隊的價值

數據分析團隊的價值This is the first article in a 2-part series!!這是分兩部分的系列文章中的第一篇&#xff01; 組織數據科學 (Organisational Data Science) Few would argue against the importance of data in today’s highly competitive corporate world. The tech…

mysql 保留5位小數_小猿圈分享-MySQL保留幾位小數的4種方法

今天小猿圈給大家分享的是MySQL使用中4種保留小數的方法&#xff0c;希望可以幫助到大家&#xff0c;讓大家的工作更加方便。1 round(x,d)用于數據x的四舍五入, round(x) ,其實就是round(x,0),也就是默認d為0&#xff1b;這里有個值得注意的地方是&#xff0c;d可以是負數&…

leetcode 842. 將數組拆分成斐波那契序列(回溯算法)

給定一個數字字符串 S&#xff0c;比如 S “123456579”&#xff0c;我們可以將它分成斐波那契式的序列 [123, 456, 579]。 形式上&#xff0c;斐波那契式序列是一個非負整數列表 F&#xff0c;且滿足&#xff1a; 0 < F[i] < 2^31 - 1&#xff0c;&#xff08;也就是…

博主簡介

面向各層次&#xff08;從中學到博士&#xff09;提供GIS和Python GIS案例實驗實習培訓&#xff0c;以解決問題為導向&#xff0c;以項目實戰為主線&#xff0c;以科學研究為思維&#xff0c;不講概念&#xff0c;不局限理論&#xff0c;簡單照做&#xff0c;即學即會。 研究背…

自定義Toast 很簡單就可以達到一些對話框的效果 使用起來很方便

自定義一個layout布局 通過toast.setView 設置布局彈出一些警示框 等一些不會改變的提示框 很方便public class CustomToast {public static void showUSBToast(Context context) {//加載Toast布局 View toastRoot LayoutInflater.from(context).inflate(R.layout.toas…

微信小程序阻止冒泡點擊_微信小程序bindtap事件與冒泡阻止詳解

bindtap就是點擊事件在.wxml文件綁定:cilck here在一個組件的屬性上添加bindtap并賦予一個值(一個函數名)當點擊該組件時, 會觸發相應的函數執行在后臺.js文件中定義tapMessage函數://index.jsPage({data: {mo: Hello World!!,userid : 1234,},// 定義函數tapMessage: function…

同情機器人_同情心如何幫助您建立更好的工作文化

同情機器人Empathy is one of those things that can help in any part of life whether it’s your family, friends, that special person and even also at work. Understanding what empathy is and how it effects people took me long time. I struggle with human inter…

數據庫課程設計結論_結論

數據庫課程設計結論When writing about learning or breaking into data science, I always advise building projects.在撰寫有關學習或涉足數據科學的文章時&#xff0c;我總是建議構建項目。 It is the best way to learn as well as showcase your skills.這是學習和展示技…

mongo基本使用方法

mongo與關系型數據庫的概念對比&#xff0c;區分大小寫&#xff0c;_id為主鍵。 1.數據庫操作 >show dbs #查看所有數據庫 >use dbname #創建和切換數據庫&#xff08;如果dbname存在則切換到該數據庫&#xff0c;不存在則創建并切換到該數據庫&#xff1b;新創建的…

leetcode 62. 不同路徑(dp)

一個機器人位于一個 m x n 網格的左上角 &#xff08;起始點在下圖中標記為“Start” &#xff09;。 機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角&#xff08;在下圖中標記為“Finish”&#xff09;。 問總共有多少條不同的路徑&#xff1f; 例如&…

第一名數據科學工作冠狀病毒醫生

背景 (Background) 3 years ago, I had just finished medical school and started working full-time as a doctor in the UK’s National Health Service (NHS). Now, I work full-time as a data scientist at dunnhumby, writing code for “Big Data” analytics with Pyt…

mysql時間區間效率_對于sql中使用to_timestamp判斷時間區間和不使用的效率對比及結論...

關于日期函數TO_TIMESTAMP拓展&#xff1a;date類型是Oracle常用的日期型變量&#xff0c;時間間隔是秒。兩個日期型相減得到是兩個時間的間隔&#xff0c;注意單位是“天”。timestamp是DATE類型的擴展&#xff0c;可以精確到小數秒(fractional_seconds_precision)&#xff0c…

ajax 賦值return

ajax 獲得結果后賦值無法成功&#xff0c; function grades(num){ var name"";   $.ajax({    type:"get",     url:"",     async:true,     success:function(result){     var grades result.grades;     …

JavaScript(ES6)傳播算子和rest參數簡介

by Joanna Gaudyn喬安娜高登(Joanna Gaudyn) JavaScript(ES6)傳播算子和rest參數簡介 (An intro to the spread operator and rest parameter in JavaScript (ES6)) 擴展運算符和rest參數都被寫為三個連續的點(…)。 他們還有其他共同點嗎&#xff1f; (Both the spread opera…

python爬蟲消費者與生產者_Condition版生產者與消費者模式

概述&#xff1a;在人工智能來臨的今天&#xff0c;數據顯得格外重要。在互聯網的浩瀚大海洋中&#xff0c;隱藏著無窮的數據和信息。因此學習網絡爬蟲是在今天立足的一項必備技能。本路線專門針對想要從事Python網絡爬蟲的同學而準備的&#xff0c;并且是嚴格按照企業的標準定…

【Python包】安裝teradatasql提示找不到pycryptodome模塊錯誤(pycrypto,pycryptodome和crypto加密庫)...

1.問題描述 安裝teradatasql時&#xff0c;出現錯誤Could not find a version that satisfies the requirement pycryptodome&#xff0c;具體如下&#xff1a; 2.解決方法 查看Python第三方庫目錄$PYTHON_HOME/lib/python3.6/site-packages目錄下沒有pycryptodome目錄&#xf…

leetcode 860. 檸檬水找零(貪心算法)

在檸檬水攤上&#xff0c;每一杯檸檬水的售價為 5 美元。 顧客排隊購買你的產品&#xff0c;&#xff08;按賬單 bills 支付的順序&#xff09;一次購買一杯。 每位顧客只買一杯檸檬水&#xff0c;然后向你付 5 美元、10 美元或 20 美元。你必須給每個顧客正確找零&#xff0…

簡述yolo1-yolo3_使用YOLO框架進行對象檢測的綜合指南-第二部分

簡述yolo1-yolo3In the last part, we understood what YOLO is and how it works. In this section, let us understand how to apply it using pre-trained weights and obtaining the results. This article is greatly inspired by Andrew Ng’s Deep Learning Specializat…

ubuntu配置JDK環境

>>>cd /usr/lib >>>mkdir java >>>cd java ###這里的參數表示接收他們的協議 >>>wget --no-check-certificate --no-cookies --header "Cookie: oraclelicenseaccept-securebackup-cookie" http://download.oracle.com/otn-pub/…