推荐:http://onevcat.com/2013/02/xcode-plugin/
 

刚写iOS程序的时候就知道Xcode支持第三方插件,比如ColorSense等很实用的插件,但Xcode的插件开发没有官方的文档支持,一直觉得很神秘,那今天就来揭开它的面纱。

在Xcode启动的时候,它会检查插件目录(~/Library/Application Support/Developer/Shared/Xcode/Plug-ins)下所有的插件(扩展名为.xcplugin的bundle文件)并加载他们。其实到这里我们就猜到了,我们做的插件最终会是一个扩展名为.xcplugin的bundle文件,放在插件目录下供Xcode加载。

OK,我们先做一个简单的插件,需要很简单的几个步骤即可完成,我的环境是Xcode 4.6.3 (4H1503)。

1. 新创建一个Xcode Project

Xcode插件其实就是一个Mac OS X bundle,所以可以参考下图创建一个Bundle。 

给Project起个名字,并确保不要勾选Use automatic reference counting,因为Xcode是使用GC来管理内存的,所以Xcode的插件也需要是用GC来管理内存的。Framework选择Cocoa

2. 设置Target Info

像下图一样设置这些信息

  • XC4Compatible = YES
  • XCPluginHasUI = NO
  • XCGCReady = YES
  • Principal Class = Plugin (这个设置为你插件的名字,本例中命名为Plugin)

前三个可能Info里缺省没有,可以自己添加,都选Boolean类型,最后一个Principal ClassString类型。 

3. 设置Build Settings

然后打开Build Setting Tab,设置这些:

  • 设置Installation Build Products Location${HOME},Xcode会自动转换为你当前用户的Home路径
  • 设置Installation Directory 为 /Library/Application Support/Developer/Shared/Xcode/Plug-ins, Xcode会把拼接Installation Build Products LocationInstallation Directory为一个绝对路径来查找你的插件
  • 设置Deployment Location 为 YES
  • 设置Set Wrapper extension 为 xcplugin

4. 添加 User-Defined 设置

  • 设置GCC_ENABLE_OBJC_GC 为 supported
  • 设置GCC_MODEL_TUNING 为 G5

有了这些设置,每次build这个Projct的时候,Xcode就会把build后的插件copy到plugin文件夹下,然后我们需要重启Xcode来重新加载新build的插件。开发插件相对来说简单一些,调试插件就比较纠结了,唯一的办法就是build之后,重启Xcode,来加载最新build的插件。

准备工作已经结束,下面开始实现我们的插件。

5. 实现我们的插件

在第二步的时候我们设置了一个Principal Class,那么在Xcode里新建Objective-C类,名字和Principal Class设置的值保持一致。在实现文件中添加上+ (void) pluginDidLoad: (NSBundle*) plugin方法。 该方法会在Xcode加载插件的时候被调用,可以用来做一些初始化的操作。通常这个类是一个单例,并Observe了NSApplicationDidFinishLaunchingNotification,用来获得Xcode加载完毕的通知。

+ (void) pluginDidLoad: (NSBundle*) plugin {
static id sharedPlugin = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedPlugin = [[self alloc] init];
});
} - (id)init {
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidFinishLaunching:)
name:NSApplicationDidFinishLaunchingNotification
object:nil];
}
return self;
}

一旦接收到Xcode加载完毕的通知,就可以Observe需要的其他notification或者在菜单中添加菜单项或者访问Code Editor之类的UI组件。

在我们的这个简单例子中,我们就在Edit下添加一个叫做Custom Plugin的菜单项,并设置一个⌥ + c快捷键。它的功能是使用NSAlert显示出我们在代码编辑器中选中的文本。我们需要通过观察NSTextViewDidChangeSelectionNotification并访问接收参数中的NSTextView,来获得被选中的文本。

- (void) applicationDidFinishLaunching: (NSNotification*) notification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(selectionDidChange:)
name:NSTextViewDidChangeSelectionNotification
object:nil]; NSMenuItem* editMenuItem = [[NSApp mainMenu] itemWithTitle:@"Edit"];
if (editMenuItem) {
[[editMenuItem submenu] addItem:[NSMenuItem separatorItem]]; NSMenuItem* newMenuItem = [[NSMenuItem alloc] initWithTitle:@"Custom Plugin"
action:@selector(showMessageBox:)
keyEquivalent:@"c"];
[newMenuItem setTarget:self];
[newMenuItem setKeyEquivalentModifierMask: NSAlternateKeyMask];
[[editMenuItem submenu] addItem:newMenuItem];
[newMenuItem release];
}
} - (void) selectionDidChange: (NSNotification*) notification {
if ([[notification object] isKindOfClass:[NSTextView class]]) {
NSTextView* textView = (NSTextView *)[notification object]; NSArray* selectedRanges = [textView selectedRanges];
if (selectedRanges.count==0) {
return;
} NSRange selectedRange = [[selectedRanges objectAtIndex:0] rangeValue];
NSString* text = textView.textStorage.string;
selectedText = [text substringWithRange:selectedRange];
}
} - (void) showMessageBox: (id) origin {
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText: selectedText];
[alert runModal];
}

你会发现在出现selectedText的地方会报错,在实现里添加上NSString *selectedText即可。

@implementation Plugin {
NSString *selectedText;
}

最终效果:

6. 需要注意的

  • ~~Plugin不能使用ARC,需要手动管理好内存~~(谢谢@onevcat的提醒,因为是用GC,不需要手动管理内存了)
  • 不能直接Debug,不过可以在程序里通过NSLog打印出日志,并通过tail -f /var/log/system.log 命令来查看输出的日志
  • 如果Xcode突然启动不起来了,可能是插件有问题,跑去~/Library/Application Support/Developer/Shared/Xcode/Plug-ins目录下,把插件删掉,restart Xcode,查找问题在哪
  • 如果1-4步骤的各种设置你比较讨厌的话,可以直接用这个Xcode4 Plugin Template来搞定, 怎么使用在它的Readme中有详细的说明,:)

总结

这只是一个简单的Xcode插件的入门编写示例,不过“麻雀虽小,五脏俱全”,可以了解到Xcode的插件一些东西,比如Xcode插件本质上其实就是一个Mac OS X bundle等等,而且因为没有Apple官方的文档的支持,很多东西只能去Google,或者参考别人插件的一些实现。

REF

本文主要参考和编译自WRITING YOUR OWN XCODE 4 PLUGINS,感谢原作者Blacksmith Software


另: 前两天我们的小伙伴@onevcat写了一个Xcode插件VVDocumenter,作用是在方法、类等前面输入三个/就会自动生成规范的JavaDoc文档(Xcode5中将支持JavaDoc类型的文档,对于我这样从Java转过来的来说是真是雪中送炭),赶紧clone了一个,用起来很方便,很好很强大,强烈推荐! 赶紧把我们的项目代码文档化起来,迎接Xcode5的到来吧,:)

Enjoy!!!

写个自己的Xcode4插件的更多相关文章

  1. 写个自己的Xcode4插件(二)

    补充上一篇: 一.在XCode5里面,要在info.plist里面再加入以下两个字段: 1. 内容要保持一致喔,别问我为什么,我也不知道,是参考其他许多插件发现的,那些插件都用了这个字段,而且内容一样 ...

  2. 用jQuery写了一个模态框插件

    用jQuery写了一个模态框插件 大家觉得下面的框框好看么, 水印可以去掉(这个任务交给你们了(- o -)~zZ); "info"框 $("div").con ...

  3. 大前端工程化之写一个简单的webpack插件

    今天写一个简单的webpack插件,来学习一下webpack插件 webpack插件机制可以使开发者在webpack构建过程中加入自己的行为,来针对自己项目中的一些需求做一些定制化 首先我们得知道一个 ...

  4. 菜鸟写的第一个chrome插件

    一.新建一个文件夹,用来放插件的代码 二.首先新建配置文件manifest.json // 开发参考:http://open.chrome.360.cn/extension_dev/overview. ...

  5. 亲手用模块化方式写一个jquery QQ表情插件。

    在回复或是评论的时候,很多时间都需要有回复表情的功能,然后而需要插入QQ表情可以是最常见的. 插件也写多很多个了,这次写插件就下了一个决定.就是使用模块化来开发. 最后在我的源代码中有这样子一段: v ...

  6. 使用iScroll和photoswipe写手机浏览图片的插件的几点经验

    首先,当我知道我得到一个任务需要写一个在手机上能浏览图片的插件时,我第一想到了iScroll.它的左右滑动,上下滑动的效果在安卓手机上也能让用户有良好的体验,自己写也能方便控制. 我的需求是,插件要能 ...

  7. jquery写的树状列表插件-alvintree

    在做项目的时候遇到选择部门下人员的功能,可多选可单选,所以就想着使用树状列表来进行选择,但在网上找了很多,发现要么就是挺复杂,要么就是需要各种前端框架的支持,试了一个感觉难用,所以就想着自己写一个简便 ...

  8. 分享一下自己写的一个vscode-leetcode答题插件

    0. 前言 春节这几天每天吃吃喝喝睡睡玩玩,突然发现明天就要上班了,吓得我虎躯一震. 春节结束之后,学生党们陆续开学,相信有许多同学马上就要在春季招聘中拼杀一番.想要收获心意的offer,当然免不了对 ...

  9. 写一个简单的JQ插件(例子)

    虽然现在 vue angular react 当道啊但是那 JQ还是有一席之地很多很多的小单位啊.其实还会用到 我也放一个例子吧虽然我也不是很肯定有没有人写的比我更好啊但是我相信 我这个还是蛮实用的 ...

随机推荐

  1. 2013腾讯编程马拉松初赛第一场(3月21日) 湫湫系列故事——减肥记II ----线段树

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4509 虽然制定了减肥食谱,但是湫湫显然克制不住吃货的本能,根本没有按照食谱行动! 于是,结果显而易见… 但 ...

  2. ll 详解

    长选项必须使用的参数对于短选项时也是必需使用的.  -a, --all                     不隐藏任何以. 开始的项目  -A, --almost-all             ...

  3. LogMiner详细讲解

    原文地址:LogMiner 一.LogMiner的用途 日志文件中存放着所有进行数据库恢复的数据,记录了针对数据库结构的每一个变化,也就是对数据库操作的所有DML语句. 在Oracle 8i之前,Or ...

  4. Android开源项目分类汇总【畜生级别】[转]

    Android开源项目分类汇总 欢迎大家推荐好的Android开源项目,可直接Commit或在 收集&提交页 中告诉我,欢迎Star.Fork :) 微博:Trinea    主页:www.t ...

  5. BZOJ 2525 Poi2011 Dynamite 二分答案+树形贪心

    题目大意:给定一棵树,有一些点是关键点,要求选择不超过mm个点.使得全部关键点到近期的选择的点距离最大值最小 二分答案,问题转化为: 给定一棵树,有一些点是关键点,要求选择最少的点使得每一个关键点到选 ...

  6. HDU 1025 Constructing Roads In JGShining's Kingdom (DP)

    Problem Description JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which ...

  7. SqlLite 简明教程

    SQL DML 和 DDL        可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL).         注:"--"双减号为行注释     ...

  8. Linux Device Driver 3th 中的一些坑

    linux设备驱动第三版由于年代比较久远,有很多东西已过时.开一贴记录自己发现的一些问题. 4.3.1.4. seq_file接口 此节最后提到用 struct proc_dir_entry* cre ...

  9. iOS UIKit:viewController之Present (3)

    弹出和转换view controller技术是一种快速且简单的方式将新view content展示在屏幕中.目前有两种方式弹出新的view controller:Present方式和segues方式. ...

  10. Java(Android)线程池 总结

        JAVA的Executors源码:(可以看出底层都是通过ThreadPoolExecutor来具体设置的~) public static ExecutorService newCachedTh ...