九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

boost::bind 学习

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

    最近学习了太多与MacOS与Iphone相关的东西,因为不会有太多人有兴趣,学习的平台又是MacOS,不太喜欢MacOS下的输入法,所以写下来的东西少了很多。

    等我学习的东西慢慢的与平台无关的时候,可能可以写下来的东西又会慢慢多起来吧。。。。。不过我想早一批订阅我博客的人应该已经不会再看了,因为已经很少会有程序语言或者C++之类的信息,相关的东西应该都是关于计算机图形,游戏的。或者,早一批订阅我博客的人现在也已经毕业,工作了呢?

    对了,这次的主题是boost:bind。在历经了
boost::asio(1)

boost::asio(2)

boost::asio(3)

boost::foreach

boost::function

boost::lambda

boost::serialization(1)

boost::serialization(2)

boost::string_algo

boost::thread

之后,boost常用的很多都学会了,现在自己写点小东西,那是大胆的用大。。。。。。。呵呵,反正自己的东西,就当用来锻炼技术了。在学习boost::signal2的过程中发现自己其实对boost::bind这个将来要进C++标准的库了解还不够多,(在boost::functon中有所提及,也同时学了一点),所以抽了点时间,好好的学习了一下。


Purpose(目的)

原来文档中的purpose完全就是教程了,事实上,bind主要用于改造函数,比如,一个地方需要一个无参的函数,你只有一个以int为参数的函数,并且你知道此时int一直为1就可以了,你怎么办?传统方法,直接实现一个函数,然后以1调用以前的那个int为参数的函数。如下:

void
 Fun1(int
 i) {

    printf("
%d
/n
"
, i);

}

void
 FunWeNeed() {

    Fun1(1
);

}

int
 main()

{

    FunWeNeed();

    

    return
 0
;

};

当然,这个例子太扯了,我们只需要直接用Fun1(1)调用就可以了,但是bind的目的就是如此,只不过现实中因为各种各样的原因,你的确需要改造函数。再比如下面的情况,你有一个原来写好的函数,接受一个以无参函数为参数,那么,你的Fun1就没有办法派上用场了,那么,传统上,怎么办?如下:

typedef
  void
 FunWeNeed_t(void
);

void
 CallFun( FunWeNeed_t f) {

    f();

}

void
 Fun1(int
 i) {

    printf("
%d
/n
"
, i);

}

void
 FunWeNeed() {

    Fun1(1
);

}

int
 main()

{

//  CallFun(Fun1);          // this line can't compile

    CallFun(FunWeNeed);

    return
 0
;

};

Ok,你不得不写新的函数以满足需求,因为你没有简单的办法改变一个函数的参数。事实上,假如你是STL流派的话,那么随着你使用标准库的算法的次数的增加,你会遇到越来越多上面描述的情况,到底很简单,C++是如此的类型安全的语言,它不会加上诸如参数过多就忽略后面参数的胡扯特性。那么,一个算法需要你传进来的是什么形式的函数(或者函数对象),那么你就的怎么做。

去看看,标准库中提供了一大堆多么扯的函数对象吧,less, more,,greater_equal,not1,not2,。。。。。。然后还给了你一堆compose1,compose2........最后附带最恶心的bind1st,bind2nd,事实上,这些东西如此之多,以至于我甚至懒的列举出来,但是事实上我们在项目中用到了多少?简而言之,None!一次也没有,甚至因为很多算法与此相关,我们连那些算法也不用!

为啥C++当年出现了那么多奇怪臃肿无用的设计?可能是,C++标准出现的那个年代,编程技术的发展也就到那个地步吧。。。。。。。。。在C/C++语言中,关于函数的抽象特别的少,原因很简单,因为函数指针太不好用了!(函数抽象也用,但是好像用的最多的是在C语言中无物可用时,不得已的而为之)

记得在哪看过一句话,技术再花哨也没有用,最重要的是足够简单,因为不够简单的技术就很难给人讲解,别人很难理解那么就很难应用。这些C++特性应该算是其中的一例了。

boost中的bind,function
,lambda
就是为此而生的。注意,在tr1中,你就已经可以使用bind和function特性了,这也很可能是将来的C++标准之一。现在boost中的lambda还不够成熟,语法很怪,限制很多,因为,毕竟,boost再强大,仅仅是库啊。。。。。。。。。匿名函数的功能完全值得用语言特性来实现!

上面那个很扯的例子,总的给个bind的解决方案吧。

#include
<boost/bind.hpp>

#include
<boost/function.hpp>

void
 CallFun( boost::function<void
(void
)> f) {

    f();

}

void
 Fun1(int
 i) {

    printf("
%d
/n
"
, i);

}

void
 FunWeNeed() {

    Fun1(1
);

}

int
 main()

{

    CallFun(boost::bind(Fun1, 1
));         // this line can't compile

    CallFun(FunWeNeed);

    return
 0
;

};

需要注意的是,此时不能再使用函数指针了,bind的天生合作伙伴是function,而function是支持函数指针的(如上例所示)

。目的将的很多了,下面看看用法吧(不是什么问题)。


普通函数

最有意思的是,你甚至能用bind来扩充原有函数的参数,见下例:


#include
<iostream>

#include
<boost/bind.hpp>

#include
<boost/function.hpp>

using
 namespace
 std;
using
 namespace
 boost;

void
 f(int
 a, int
 b)

{

    cout <<"Argument 1 is "
 <<a <<endl;

}

void
 g(int
 a, int
 b, int
 c)

{

    cout <<"sum is "
 <<a+b+c <<endl;

    cout <<"arg 1: "
 <<a <<endl;

    cout <<"arg 2: "
 <<b <<endl;

    cout <<"arg 3: "
 <<c <<endl;

    cout <<"---------------------------"
 <<endl;

}

int
 main()

{

    function<void
(int
,int
)>  f1= bind(f, _2, _1);                 // 调整参数1,2的位置

    f1(1
, 2
);

    function<void
(int
)> sum1 = bind(g, _1, _1, _1);        // 3个参数变1个

    sum1(10
);

    function<void
(int
, int
)> sum2 = bind(g, _2, _2, _2);       // 3个参数变2个,仅用一个

    sum2(10
, 20
);

    function<void
(int
, int
, int
)> sum3 = bind(g, _3, _3, _3);      // 3个参数还是3个,但是仅用1个

    sum3(10
, 20
, 30
);

    function<void
(int
, int
, int
, int
)> sum4 = bind(g, _4, _4, _4);     // 3个参数变4个,但是仅用1个

    sum4(10
, 20
, 30
, 40
);

    return
 0
;

};


输出结果:


Argument 1 is 2

sum is 30

arg 1: 10

arg 2: 10

arg 3: 10

---------------------------

sum is 60

arg 1: 20

arg 2: 20

arg 3: 20

---------------------------

sum is 90

arg 1: 30

arg 2: 30

arg 3: 30

---------------------------

sum is 120

arg 1: 40

arg 2: 40

arg 3: 40

---------------------------

函数对象

注意用法中很重要的一条:通常情况下,生成的函数对象的
operator()
的返回类型必须显式指定(没有
typeof
操作符,返回类型无法推导)。(来自Boost文档)

#include
<boost/bind.hpp>

#include
<boost/function.hpp>

using
 namespace
 std;
using
 namespace
 boost;

struct
 F

{

    int
 operator
()(int
 a, int
 b) { return
 a - b; }

    bool
 operator
()(long
 a, long
 b) { return
 a == b; }

};

int
 main()

{

    F f;

    int
 x = 104
;

    function< int
(int
) > fun1 = bind<int
>(f, _1, _1);      // f(x, x), i.e. zero

    cout <<fun1(1
);

    function< bool
(long
) > fun2 = bind<bool
>(f, _1, _1);       // f(x, x), i.e. zero

    cout <<fun2(1
);

    return
 0
;

};

其他的也就很简单了。


成员指针

例子来源于boost文档。
#include
<iostream>

#include
<boost/bind.hpp>

#include
<boost/function.hpp>

#include
<boost/smart_ptr.hpp>

using
 namespace
 std;
using
 namespace
 boost;

struct
 X

{

    void
 f(int
 a) {

        cout <<a <<endl;

    }

};

int
 main()

{

    X x;

    shared_ptr<X> p(new
 X);

    int
 i = 1
;

    bind(&X::f, ref(x), _1)(i);     // x.f(i)

    bind(&X::f, &x, _1)(i);         //(&x)->f(i)

    bind(&X::f, x, _1)(i);          // (internal copy of x).f(i)

    bind(&X::f, p, _1)(i);          // (internal copy of p)->f(i)

    return
 0
;

}

可见bind的强大,支持自己拷贝需要的对象,支持引用,甚至,支持智能指针。

最后一个例子,结合标准库容器及算法的例子,这才是展示bind的强大的地方。

还是来自于boost文档。
class
 image;
class
 animation

{
public
:

    void
 advance(int
 ms);

    bool
 inactive() const
;

    void
 render(image & target) const
;

};

std::vector<animation> anims;

template
<class
 C, class
 P> void
 erase_if(C & c, P pred)

{

    c.erase(std::remove_if(c.begin(), c.end(), pred), c.end());

}

void
 update(int
 ms)

{

    std::for_each(anims.begin(), anims.end(), boost::bind(&animation::advance, _1, ms));

    erase_if(anims, boost::mem_fn(&animation::inactive));

}

void
 render(image & target)

{

    std::for_each(anims.begin(), anims.end(), boost::bind(&animation::render, _1, boost::ref(target)));

}

例子展示了erase_if,for_each算法中使用bind的方法,当然,实际中,假如是你的游戏引擎中的update,render函数,碰到上述需求或者类似代码实现是很正常的,但是,你会放心的仅仅为了简化一些代码,然后将如此性能相关的位置,直接交给bind吗?

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

寻找最佳的数据存储方式

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

游戏中总是有很多相关的数据需要存储,比如编辑器产生的关卡数据,人物,物品属性的配置等等,并且现在已经不是每人都设计一种自己数据格式的时代了.程序员们是越来越懒,大家都想着一套文件格式,一套解析,处理代码能够通吃所有的程序.以下讨论,包括优缺点,都仅仅是对于游戏数据(还仅指编辑的数据)存储而言,以下都省略此前提背景,其他领域的使用本文仅供参考,游戏领域的使用本文也仅是一家之言,当然也仅供参考.
首先,看看传统的数据存储解决方案.

传统数据存储解决方案

EXCEL方案

最大的优点是几乎人人都会更改并且可以更改而不用进一步的学习.干这行的,别说策划,即使是文秘应该起码也会EXCEL吧?
缺点,需要自己写一大堆的Excel解析代码,这点是无奈的.
并且,假如不想每个Excel文件都写一套解析代码的话,你就要想好一种通用,并且扩展性良好的Excel编写格式,这也不是太容易的.
即使设计的Excel格式扩展性足够好,当数据慢慢复杂的时候,你会发现,事情越来越复杂.....几乎就开始将Excel做关联数据库使用了.有的时候,通过改动一些关联性很强的Excel数据来完成某个功能,甚至比你通过代码改变来实现还要复杂.....到那个时候,你才会发现,Excel已经不敷使用.
最最重要的是,Excel(起码03版的是这样)是二进制数据,SVN,mercurial等版本控制工具(偏偏这就是大家项目中所用的吧)不支持merge,这样,每次多人更改,必然导致冲突,然后,手工再去痛苦的重写吧.
但是,假如你碰到像我们公司说的这种,翻译组只懂使用Excel,那么你再痛苦,也就无奈去吧.这也是Excel的优点吧........当然,从程序的角度来看,那是最大的缺点.

数据库方案

数据库方案最大的优点是扩展性异常强大,数据库方案的扩展性几乎是无限的,无论多么庞大的数据量,放在数据库中总是不会显得很多,关联表格也总是能将冗余数据降到最低,并且,如此的清晰.由于现代数据库前端做的越来越好,修改数据库中的数据也不总是需要SQL语句了.最最重要的是,当你需要的时候,你还是能用SQL语句来实现你需要的最最复杂的查询,或者批量修改某些相关的数据.
当然了,没有方案是完美的,用数据库方案,的确强大,但是也有强大的代价,首先,项目中得有数据库专家,能够设计足够好的表格,及写出复杂的多重嵌套SELECT语句,项目中得有人懂得使用各自语言自己的数据库接口API,并且比较郁闷的是,各种数据库的API设计还不太一样......通用的API设计往往是以低效率为代价的....当然,这些都扯远了,扯上了原来做服务器的经验了........假如仅仅将数据库作为普通游戏数据存储的方式,缺点还没有那么夸张,并且,我们总是可以使用mySQL,SQLLite这样的开源解决方案.在各类语言中也总是有可用的相应的API可用.
除了上面那些,还有个问题,即使仅仅将数据库作为普通游戏数据存储的方式,为每个需求设计一套表,写一堆的从数据库中获取数据的代码,并且,最要要是以二进制数据发布的话,你还得写一套从数据库中读数据,然后序列化成二进制数据的代码,然后,额外的,解析二进制的代码也不可少.强大的数据库解决是以庞大的人工代码量堆积起来的.

现代数据存储解决方案

因为传统解决方案有着其天生的缺点,Excel对于越来越大的游戏,越来越多的游戏数据有些吃力,而是用数据库方案对于光是保存游戏数据(仅指编辑数据,不包括网游的运行时数据)又显得太过臃肿.那我们有没有更好的方法呢?先看看我们想要什么样的方案:
1.有着文本存储的方式,便于开发期调试和修改,也能直观的查看到改变和开发期的版本控制merge.
2.有二进制存储方式,在发布后可以使用这样的方式,减少parse时间,并且,从文本方式切换到二进制方式是有很简单解决方案的.
3.parse,序列化的代码编写要足够的简单,起码不会随着数据量的扩大而扩大到难以掌握的地步.
4.多语言支持,你总能在你喜欢的语言中使用
5.在数据之间建立关联,嵌套关系要足够的简单,这样才能简单的表示丰富的内容,而不是通过另外再建一套数据来表示关联(就像在Excel中需要做的那样)

有方案能满足上面所有的需求吗?可以寻找一下.
因为我能力有限,这里仅举较为常用的XML,JSON,Google Protobuf为例,作为比较,也作为自己选择最佳方式的参考.(在查找资料的过程过还发现了一个名叫Thrift的解决方案,有文章
的比较指出其比Google Protobuf要强大较多,因为使用较少,我不准备尝试,在此提供此信息,仅供参考)

XML方案

现在游戏数据中XML方案应用的应该算是非常广泛了,几乎算是事实上的工业标准?起码我的感受是这样,甚至于,Office 2007以后的通用格式都是XML化的了.是因为网页开发的太过流行,导致大家都喜欢上了尖括号横生的标记语言了吗?我不知道.但是,XML的确有其优点.在WIKI百科上,有这么一段描述:
As of 2009[update]

, hundreds of XML-based languages have been developed,[5]

including RSS
, Atom
, SOAP
, and XHTML
. XML-based formats have become the default for most office-productivity tools, including Microsoft Office
(Office Open XML
), OpenOffice.org
(OpenDocument
), and Apple
's iWork
.[6]


看看吧,应用的还真不少.
首先,上述5点需求的需求1,3,4,5是较为完美的满足的.XML是写给人看的数据存储格式,虽然喜欢尖括号的人(比如我)会反感,并且因为其流行,解析XML的库也是非常丰富,各类语言对XML的支持肯定少不了,XML对于嵌套,关联的支持也是非常好的,其自描述性,能让你看到XML就能知道,它在描述什么.XML的处理都是字符串,支持unicode, wikipedia上还提供了一个有意思的例子:
version
="1.0"
encoding
="UTF-8"
?>


<烏語>

Китайська мова>


中国人写的?-_-!当然,实际中,除了字符串可能会用上中文外,一般的tag我们还是习惯用英文.
另外,有一点也很重要,XML是可扩展的,添加新的XML属性完全不影响原有的解析及代码的运行,直到你真的需要这个属性,然后再添加处理代码即可.
但是,XML有个致命的缺点,一堆类似HTML的标记,根本就不是设计给人来写的,(当然,好用的工具另外),在阅读的时候,很显然,废数据也过多,那么,存储,传输的时候,(假如传输的真是文本的话)同样的,信噪比太低,这点我不是太喜欢.当然,最主要的是直接写XML比较麻烦.也许有强大的工具能解决此问题,但是鉴于我们公司的制度,那可能是可望而不可及的事情.

JSON
方案

上述5点需求的需求1,3,4,5可以得到比XML更完美的满足.得益于简单的格式,所以解析及创建都可以做的很简单的,特别是JSONCPP这个库,在使用的时候简直就像获取到JS中的Object一样,直接通过[]操作完成索引及建立操作,使用起来感觉非常爽,并且,也有XML有的可扩展性,相对来说,比起以前使用LUA做配置而言,JSON会更加简单,当然,功能也稍微弱一些,不用逻辑处理(到脚本层次)的时候,我个人认为JSON就是最合适的数据存储格式.当然,从数据处理的速度来说,可能还需要一个从JSON到二进制的转换工具,(XML也有类似问题),然后在发布期完全使用二进制.
目前有点问题的就是,JSON的格式没有XML那么规范,所以常常会容易写错,然后自己还没有感觉,JSONCPP使用了MAP的特性,也就是说,使用方便,但是当[]查找不到的时候,是默认建立了空的索引,这会带来一些混乱.....
在我们的项目中,使用JSON来配置界面,为UI的灵活性提供了很大的方便,并因此节省了无数代码及编译时间,此时我想起一句LUA之父说的话,只有当配置足够灵活,人们才会使用它,的确如此,当JSON的使用如此方便的时候,使用配置,而不是代码,那是享受,而不是痛苦.

另外,XML与JSON方案还有个好处,因为他们的通用性,各个项目之间的沟通也会更加简单,与人解释XML,JSON在干什么,比与人解释自己的二进制文 件在干什么要简单的多.也因为沟通更加简单,使得在通用格式上面的那一层代码也能得到更多的复用机会.甚至,我们考虑过使用其他项目中创造的用JSON实现的关键帧动画.........

Google Protobuf
方案

用的不多,试用了一下,谈下感想,Google在说明文档中说Google内部使用时,所有的进程间他通行,一律要求使用Google Protobuf,也可见其自有其强大之处.使用方式上与上述两个方案有比较大的不同,XML与JSON仅仅是描述数据,单纯的数据,解析的代码还是需要自己去写(尽管JSON的解析已经非常简单了)但是Google Protobuf不同,其要我们编写的是结构描述文档,并且通过其附带的工具,直接生成C++,JAVA,Python的代码(也就是Google内部使用的3语言,与我们公司完全一致),至此,不仅解析简单了,甚至结构都有了,我们需要做的是直接调用结构的接口去完成输入输出,所有的数据的getter都通过Google规定的方式进行了命名.甚至,直接将结构打包成2进制数据然后发送了.(也就是Google在文档中所描述的进程间通信).比起XML,JSon而言,此方案最大的优势在于速度,它有很好的文本格式以及二进制格式,可以很方便的切换(原生就支持),这也是3个方案中唯一满足了需求2的方案.(事实上,完全满足上述所有需求)但是,很明显的,有个缺点,(实在是没有实际使用,仅仅是通过试用感觉的),假如结构变换的话,可能得重新使用Google提供的工具,重新生成结构,这就需要重新编译代码,这是个较大的问题.................其他的,个人感觉,Google Protobuf是非常不错的解决方案,特别是用在需要进程间通信时(比如需要进行网络通信,连打包的代码都省了).

最后的总结是,未来的趋势应该是JSON
方案吧,Google Protobuf
方案更适合需要网络通信的情况,毕竟还需要通过工具生成代码,使用的方便性来说不如JSON.

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

不远的将来 程序语言是怎样的

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

其实很久就看到Bruce Eckel的文章了《Programming in the Mid-Future》,原文发表于2010-3-10日.作为一个经常被自己无聊的求知欲所控制的程序员,我利用工作或者业余时间学习了N多种语言,也常常在想,到底怎么样的语言才是我们需要的,真的想要的,是能让(绝)大部分程序员所能接受的,公认的好语言,而不是一提到某某语言,就必定引发一场无休止的口水战呢?
Bruce Eckel提出了一些他的看法,虽然他不仅讲述了关于程序语言的内容,而是涵盖了程序设计的各个方面,但是,很显然,程序设计再多方面,主要的内容还是在程序设计语言上面. 在这里,大牛面前,我发表太多言论就类似班门弄斧了,这里先摘录一下原文的小标题及核心内容.
在Bruce Eckel心中,不远的将来 程序语言是这样的:

1.极端动态(Extremely dynamic)

有越来越多的问题是静态语言解决不了的,所以,程序语言将向越来越动态的方向发展.

2.极其简单的并行(Stupidly parallel objects)

对象将管理他们自己的进程,就算不知道啥叫并行编程,也能无误的使用.

3.无盘持久化环境(Persistent diskless environment)

你将创建对象并随时使用它们,而不用关心它们在哪里,对象将在必要的时候包含必要的数据.

4.本地开发和云部署开发一致(Transparency between local and cloud)

程序员将不用考虑这些问题,它们将自动化.

5.自动覆盖测试(Swarm testing)

将来的测试要更快,更周密,更自动化,为测试所写的代码也要少的多.

6.代码安全测试(Security via suspicious systems)

将来的测试不仅包含现在普通意义上的测试,逻辑测试,安全测试也将存在.

7.以查询为基础的数据模型(Query-based data)

将来查询数据库就像使用Google一样简单............

8.更大尺度上的复用(Reusability on a vast scale)

未来的对象交互会以'组件'为基础,组件间的接口将是通用的,向一个系统中添加一个新的组件将非常容易.

9.简单的系统集成(Effortless System Integration)

你可以自由选择单独使用一个程序/组件,或使用由这些程序构成的更大的系统.

10.可复用的用户界面元素(Reusable UIs)

用户界面的元素将分类存储,并且获取和显示数据,大部分时间,我们选择并粘贴它们到系统中就能添加某个功能.

11.简单的效率伸缩性(Effortlessly Scalable)

一个程序在无论在多大规模下使用都不需要进行更改.

12.内建的可进化性(Built-in Evolvability)

无论向多大规模的程序中添加新特性,都是一样的简单.

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

在Google Doc不可用的时候,见识了Google的18国语言道歉, 力压苹果5国

 

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie

讨论新闻组及文件

在Google
Doc不可用的时候,见识了Google的18国语言道歉。。。联想到苹果的4国,5国。。。还是Gooogle 的用户群更广?

以下为具体内
容:

Google:

Google Docs is temporarily unavailable -- please try
back later. If you get this page again, please check our Help Center

for outage
notices. We apologize for any inconvenience.

Google
ドキュメントーは一時的にご利用いただけません。 しばらくしてから、もう一度試してみてください。 また、ヘルプ センター


ご不便をおかけしますが何卒ご了承ください。

Google Documents est momentanément indisponible.
Veuillez réessayer ultérieurement. En attendant, vous trouverez des
informations utiles dans notre Centre d'aide

. Nous
vous prions de nous excuser des désagréments occasionnés.

Google Text & Tabellen ist
vorübergehend nicht verfügbar. Versuchen Sie es später erneut. In der
Zwischenzeit erhalten Sie nützliche Informationen in der Google Hilfe-Seiten

.
Wir entschuldigen uns für eventuelle Unannehmlichkeiten.

Google Docs no estará disponible
temporalmente. Por favor, intenta acceder más tarde. Mientras tanto,
puedes consultar el Centro
de Asistencia

. Disculpa las molestias.

Google Documenten is tijdelijk niet
beschikbaar. Probeer het later nogmaals. In de tussentijd kun je nuttige
informatie vinden in ons Helpcentrum

. Onze
excuses voor het ongemak.

Google Documenti non è temporaneamente disponibile.
Riprova più tardi. Nel frattempo prova a consultare il nostro Centro assistenza

. Ci
scusiamo per gli eventuali disagi causati.

O Google Docs não está disponível no
momento. Tente novamente mais tarde. Enquanto isso, você pode encontrar
informações úteis na nossa Central de Ajuda

.
Pedimos desculpas por qualquer inconveniente.

Google 文件是暂时无法使用。 請稍後再試。 在此期間,您可以在我們的說明中心

中找到有用的資訊。
對於造成您的任何不便,我們謹此致歉。

Google 文档暂时不可用。请稍后再试。在此期间,您可以在我们的支持中心

查找有用的信息。对于由此带来的任何不便,我们深表歉意。

Dokumenty Google jest tymczasowo niedostępny. Spróbuj
ponownie później. Tymczasem możesz znaleźć przydatne informacje w naszym
Centrum pomocy

.
Przepraszamy za wszelkie niedogodności.

Google Dokument är inte tillgängligt
för tillfället. Försök igen senare. Under tiden kan du hitta användbar
information i vårt hjälpcenter

.
Vi ber om ursäkt för eventuellt besvär.

Google Dokumenter er midlertidigt ikke
tilgængelig. Prøv igen senere. I mellemtiden kan du muligvis finde
brugbare oplysninger i vores Hjælp

. Vi beklager
ulejligheden.

Google
문서도구 잠시 사용할 수 없습니다. 나중에 다시 열어보십시오. 궁금한 점이 있으면 우선 Google 도움말 센터

에서 찾아보십시오. 불편을
끼쳐드려 죄송합니다.

Документы
Google временно недоступен. Повторите попытку позже. А пока полезную
информацию можно найти в нашем справочном центре

.
Приносим извинения за причиненные неудобства.

Google Dokumenter er midlertidig
utilgjengelig. Prøv igjen senere. I mellomtiden kan du finne nyttig
informasjon i Brukerstøtten

.
Vi beklager eventuelle ulemper dette medfører.

Google-dokumentit on väliaikaisesti
poissa käytöstä. Yritä myöhemmin uudelleen. Voit löytää hyödyllisiä
tietoja ohjekeskuksestamme

.
Pahoittelemme tilanteesta mahdollisesti aiheutuvaa haittaa.

Google Dokümanlar geçici olarak
kullanım dışıdır. Lütfen daha sonra tekrar deneyin. Bu arada, Yardım Merkezimizde

. Bu
durum için özür dileriz.

苹果:


 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by
九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

各种进程创建方式比较总结(MacOS, Win32, Qt)

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie


讨论新闻组及文件

   
由于进程创建是非常基础,很重要的工作,由此导致的恶果就是每个系统自己都喜欢封装一套,以显示自己对其有原生的,较好的支持,但是,说实话,有的时候,
那方式,实在是相当别扭,这里,我讲我了解的平台/系统下的进程创建方式统一的梳理总结一下,也顺便可以比较一下各个系统的API封装情况,窥一斑而见全
豹嘛。这里很容易加入主观因素,也没有太多客观事实可以作为唯一的评定标准,那么,完成同样的工作,代码量少点总不是太坏的事情,仅以代码量为参考之一
吧,参考之二就是大家自己的感觉了。毕竟其中一些地方无法做到公平,因为所有代码并不是以精简代码为首要目的,首要目的是适应对应接口的风格.



式说明

    1, 最简单的创建一个带参数的进程,以显示当前目录下的文件a,文件b的内容为例。
   
2,展示进程的输出重定向使用,以将当前目录下的文件a,文件b定向到文件c中为例。
   
3,展示进程间的管道通信技术,以将当前目录下的文件a,文件b通过管道传递给另外一个输出的进程,并且显示。
  
 (以下简称Test1,2,3)。

1.Shell/DOS

1.从最简单的开始吧,Shell/DOS,这种简单的情
况,Windows的DOS与Unix/Linux的Bash
Shell下都差不多,以Bash为例了。(其实Windows下仅仅将cat换成type命令即可。。。。其实还是有些细微差别的,参看《从Unix的
echo与Dos的type之间的区别,看操作系统之间的软件设计哲学的不同》一文)以下因为type,dos命令的限制导致Windows下某些时候
Test3无法完成,忽略之)

jtianling$ echo "I'm File A." > a
jtianling$
echo "I'm File B." > b
jtianling$ ls
a    b
jtianling$ cat a
I'm
File A.
jtianling$ cat b
I'm File B.
jtianling$ cat a b
I'm
File A.
I'm File B.
jtianling$
好了,cat a
b,这样简单的调用,进程创建完成,参数输入完成,显示正常。


2.输出重定向,对于Shell来说实在是小
case,那简直可以说是其原生的。

TianLings-MacBook:test jtianling$ cat a b > c
jtianling$
cat c
I'm File A.
I'm File B.
jtianling$
cat a b >
c一句而已。


3.管道,还是Shell饭碗里面的内容。

jtianling$ cat a b |
cat
I'm File A.
I'm File B.
cat a b | cat还是一句代码而已。



此,Shell完成了全部任务,可以看到Shell这玩意儿不愧是为管理,调用进程而生的家伙,完成类似任务,那是信手拈来,毫不费劲,即便是最复杂的
Test3,也没有超过一行,字符数(以下都不计空格)甚至没有达到两位数。当然,到了这里,离谱的简单,有人可能会说这种比较根本不应该加入
Shell,甚至会觉得Shell参与此分类比较简直就是作弊。。。。。bash/bat也算是一种编程语言,而且为了进程调用的方便,牺牲了那么多,为
啥不算呢?^^

2.C Runtime Library

这里运行环境是MacOS,Windows下还是得用
type....利用了C Runtime Library的system函数。

Test1:
#include
<stdlib.h>

int
 main()
{
        system("cat a b"
);

        return



 0


;
}

结果:
Running…
I'm File A.
I'm File
B.

Debugger stopped.
Program exited with status value:0.

OK,

次类推,接下来的也就就是按照Shell的调用方式换system的参数而已了,不重复描述了,简单计算,起码都是5行内解决问题。(自然分行,没有特别
去缩减,也没有特别去增加,空行不算)有人又会说,这样使用完全是依靠了SHELL的强大,system函数不过是个简单的Shell调用而已。。。。根
本不算数,C语言本身压根就不带进程创建功能-_-!但是。。。。任务完成了吧。。。Shell本身都算,依赖于Shell完成任务,只要任务能够很好的
完成,为啥不算呢?

3.Python中的System

Python的OS模块中也有System这个C
Runtime
Library的函数,于是,第2种方法的好处其可以占尽,并且,因为Python作为脚本语言,少了很多负担,所以其实从完成任务的角度来说,要比C语
言更加简单一些。当然,我不知道这不属于Python社区接受的方式,也即,不知道这算不算Pythonic。
>>>
import os
>>> os.system("cat a b")
I'm File A.
I'm
File B.
0
当然,能够如此简单,还是少不了Shell的功劳。

4.Win32 API
WinExec/ShellExcute

这两个接口都好像不怎么推荐使用了,但是使用的简单性上还算是一个量级的.放在一起了.
Test1:


#include
<windows.h>

int
 _tmain(int
 argc, _TCHAR* argv[])
{
    WinExec("cmd /c type a b"
, SW_SHOW);

    getchar();
    return



 0


;
}

Test2与Test1类似,不累述了,Test3因为type不支持管道输入,所以无法完成.

5.Win32
API CreateProcess

CreateProcess其实是平时我在Windows下创建新进程使用最多的函数,因为可以获得新进
程的句柄,可以进行很多额外的操作,比如让新进程先停止,甚至可以hack新进程,通过API直接对新进程的内存进行write操作.....呵呵,当时
研究反外挂时的一些操作,平时用的其实也少.当时Wait相关API,只有获得CreateProcess返回的进程Handle才能进行.其实对与此
API我以前特别有一文讲述了一些经验(其实是做一个笔记,防止忘记)<

Windows下
的进程创建API--CreateProcess使用经验漫谈


>


#include
<windows.h>

#include

<stdio.h>

#include
<tchar.h>

int
 _tmain( VOID ) {
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    LPTSTR szCmdline=_tcsdup(TEXT("cmd /c type a b"
));

    ZeroMemory(
&si, sizeof



(si) );
    si.cb
= sizeof



(si);

    ZeroMemory( &pi, sizeof



(pi)
);

    // Start the child process.



    if



( !CreateProcess( NULL
,  szCmdline, NULL
,
NULL
, FALSE, 0


,
NULL
, NULL
,
&si, &pi )) {
       printf( "CreateProcess
failed (

%d


).
/n


"
, GetLastError() );
       return



 0


;
    }
    // Wait until child process exits.



    WaitForSingleObject( pi.hProcess, INFINITE );

    //
Close process and thread handles.



    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

    return



 0


;
}

纯C语言的接口,第一次看到此接口的时候简直懵了,后来用ShellExecute替代了,用到再后
来,CreateProcess用多了,每次copy示例代码,竟然也慢慢觉得看的顺眼了,习惯的力量是强大的..........


6.MacOS
Cocoa

下面看重头戏…………
Test1:


#import
<Foundation/Foundation.h>

int
 main (int
 argc,
const
 char
 *
argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool
alloc] init];
        
        NSTask *task = [[NSTask alloc]
init];
        [task setLaunchPath:@"/bin/cat"
];
        
        NSMutableArray *args = [NSMutableArray array];
        [args addObject:@"a"
];

        [args addObject:@"b"
];
        // can be replace by -- NSArray *args = [NSArray
arrayWithObjects:@"a", @"b", nil];


        [task
setArguments:args];      
        [task launch];
        [task
waitUntilExit];
        
        [task release];

        [pool drain];
        return
 0
;
}

一共15行,Cocoa中对进程进行了封装,
用NSTask来表示,并且将参数分割成一个一个字符串对象,组合起来成为一个Array后才能设定为参数,再然后,通过launch启动进程,通过
waitUntilExit等待进程推出,再释放自己分配的NSTask对象,对了,Objective
C程序中,为了适应没有垃圾收集的情况,还得好好的设定自动释放内存池。

Test2:
想投机取巧一下吧,利用Shell,如下:

        NSMutableArray *args = [NSMutableArray
array];
        [args addObject:@"a"
];
        [args addObject:@"b"
];

        [args addObject:@">"
];

        [args addObject:@"c"
];


会得到如下错误信息:
I'm File A.
I'm File B.
cat: >: No such file or
directory
cat: c: No such file or directory

Debugger stopped.
Program
exited with status value:0.
这是行不通的。。。。Apple给了你封装,你就得用,抵抗只有死路一条。。。。。

确用法:(不知道还有没有更简单的,这个方法还是自己想到的。。。。试了试还真可以用。。。。)


#import
<Foundation/Foundation.h>

int
 main (int
 argc,
const
 char
 *
argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool
alloc] init];
        
        NSTask *task = [[NSTask alloc]
init];
        [task setLaunchPath:@"/bin/cat"
];
        
        NSMutableArray *args = [NSMutableArray array];
        [args addObject:@"a"
];

        [args addObject:@"b"
];

        [task setArguments:args];

        NSFileManager *fm =
[NSFileManager defaultManager];
        if
 ([fm
createFileAtPath:@"c"
 contents:nil
 attributes:nil
]
== FALSE) {
                NSLog(@"Can't
Create the file named c."

);
                return
 1
;

        }
        
        NSFileHandle* fh = [NSFileHandle
fileHandleForWritingAtPath:@"c"
];

        [task setStandardOutput:fh];
        
        [task
launch];
        [task waitUntilExit];
        [task release];
        
        [fh closeFile];
        
        [pool
drain];
        return
 0
;
}

于是乎,你可以知道了,apple是崇尚学习
的。。。。。因此,你又学会了好几个类,明白了好几个概念,
NSFileManager不
能直接创建文件然后返回

NSFileHandle进行操作的设定难道是为了更好的实行类的
分工?

Test3:
经历过Test1,Test2的洗礼后,应该会明白了,为啥苹果的笔记本起码都要7千多,苹果的一个外接完整
键盘都要500多,一根破转接线都要200(见《看到苹果
的把视频转接线当金条卖,我彻底怒了。。。。。。。

》生产效率太低估计是原因之一(可是苹果很多东西不都是中国组装的吗?)。。。。-_-!为
啥与苹果粉丝的宣传的好像不大一样啊。。。。。原因我也不知道,用苹果最大的感受是Mac
Book的Fx键都被笔记本的特性完全占用,苹果自己默认设定的一些快捷键都会形成冲突,XCode的调试常用键你都得用3个以上的键组合才能按
出。。。。人性化的设计极大的培养了用户手指的灵活度。(别说可以改,VS,Eclipse,Qt
Creator的调试常用快捷键为啥都只用Fx键一键调用是有原因的,这个道理Ollydbg都懂,为啥苹果这么注重用户体验及GUI设计的人为啥不懂我
是不太明白。。。。。。。难道苹果的GUI设计光是指使用鼠标或者触摸屏?唉。。。。也许我还没有适应苹果的文化吧。。。。。。当大家都被苹果的文化熏陶
后,也许苹果的东西就是好吧。。。。。。(估计说这些话会被很多人骂,所以不开新文章特别说明了,省的骂声一片,仅插播在这个枯燥的技术文章当中吧。某年
某月某日我改变主意了,再回来改掉这些文字吧。)
对了,主题是,Cocoa中的管道:


#import
<Foundation/Foundation.h>

int
 main (int
 argc,
const
 char
 *
argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool
alloc] init];
        
        NSTask *task = [[NSTask alloc]
init];
        [task setLaunchPath:@"/bin/cat"
];
        
        NSMutableArray *args = [NSMutableArray array];
        [args addObject:@"a"
];

        [args addObject:@"b"
];

        [task setArguments:args];

        NSPipe *pipe = [NSPipe
pipe];
        [task setStandardOutput: pipe];

        NSTask *newTask = [[NSTask alloc] init];
        [newTask
setLaunchPath:@"/bin/cat"
];

        [newTask setStandardInput: pipe];

        [task launch];
        [newTask launch];
        [task waitUntilExit];

        [newTask waitUntilExit];
        [task release];

        [newTask release];
        
        [pool drain];

        return
 0
;
}
Pipe的封装还算简单,使用也还算方便吧,但是因为Task的使用,代码量实在也够多了。

7.Qt
QProcess

作为几乎对C++进行语言级别改变的框架Qt,进行了与Cocoa类似的封装,对了,其实感觉Qt对C++从语言特性的改变
上来说类似于Cocoa对于Objective C的改变,不同的是Qt没能统治C++世界。
Test1:


#include
<QtCore/QCoreApplication>

#include
<QtCore/QProcess>

#include
<QtCore/QStringList>

int
 main(int
 argc, char
 *argv[])
{
    QString program = "cat"
;
    QStringList arguments;

    arguments <<"a"
 <<"b"
;

    QProcess *myProcess = new



 QProcess();
    myProcess->start(program, arguments);

    myProcess->waitForFinished();
    QByteArray output =
myProcess->readAllStandardOutput();
    printf("
%s


"
, (const


 char
*)output);

    return



 0


;
}

QProcess就是Qt封装的进程类,需要特别说明的此类使用上在Qt4与Qt3是不同的。并且,有个问题是因为
Qt现在几乎已经是纯面向GUI的界面库了,所以QProcess默认是进行命令行不输出的,这里转了个弯,先读出了输出,然后用printf输出了。

Test2:


#include
<QtCore/QCoreApplication>

#include
<QtCore/QProcess>

#include
<QtCore/QStringList>

int
 main(int
 argc, char
 *argv[])
{
    QString program = "cat"
;
    QStringList arguments;

    arguments <<"a"
 <<"b"
;

    QProcess *myProcess = new



 QProcess();
    myProcess->setStandardOutputFile("c"
);
    myProcess->start(program, arguments);

    myProcess->waitForFinished();

    return



 0


;
}
但是看了示例,什么感觉?个人感觉Qt不愧是专门做API的,靠API吃饭的与靠卖硬件为生的公司就是不一样,API的设计实
在是恰到好处,简介简练,并且,最重要的是,够用!

Test3:



#include
<QtCore/QCoreApplication>

#include
<QtCore/QProcess>

#include
<QtCore/QStringList>

int
 main(int
 argc, char
 *argv[])
{
    QString program = "/bin/cat"
;
    QStringList arguments;

    arguments <<"a"
 <<"b"
;

    QProcess *myProcess = new



 QProcess();
    QProcess *outProcess = new



 QProcess();
    myProcess->setStandardOutputProcess(outProcess);

    myProcess->start(program, arguments);

    outProcess->start(program);

    myProcess->waitForFinished();

    outProcess->waitForFinished();

    QByteArray output =
outProcess->readAllStandardOutput();
    printf("
%s


"
, (const


 char
*)output);
    return



 0


;
}

总体上而言,Qt没有设计pipe对应的类,但是,对于一个函数可以解决的任务来说,很明显使用起来是更加简单的,像
Cocoa那样,对这么简单的概念都进行相应的封装,其实有过度设计之嫌。。。。其实NSPipe也就2,3个函数,而且,如此例所示,其实,除了一个有
用外,NSPipe多出的那么几个都是因为多了NSPipe才出现的函数。。。。。悲哀中。。。。

总表

使用方式/代码量
Test1
Test2
Test3
SHELL

行5字符
单行7字符
单行9字符
C Runtime
Library's system
5行
5行
5行
Python's
os.system
2行
2行
2行
Win32
API WinExec/ShellExecute
6行
6行
type
限制
Win32 API CreateProcess
16行
16

type限制
Objective C With Cocoa
15

23行
23行
Qt QProcess
16

15行
20行

别的不
说了,起码有一点可以肯定。。。。。有的时候面向/基于对象的设计,不仅实现起来不能省代码量,就连使用起来都需要更多的代码。。。。。
而且封装
这技术,使用一定要适度,太细太过并没有太大好处。有的时候一个简单的C语言函数,已经可以做的事情,为啥我们要用3,4各类来完成?

 

原创文章作者保留版
权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie


阅读全文....

从Unix的echo与Dos的type之间的区别,看操作系统之间的软件设计哲学的不同

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie


讨论新闻组及文件

本文其实仅仅是写
《各种进程创建方式比较总结(MacOS, Win32, Linux, Qt,
Python…………)》一文时的过度发挥,发现插入大段无关文字有点问题,就给剥离出来了,也算一次文章的重构。。。。。
从Unix的echo
命令与Dos的type命令,从基本使用上来说,都是用于显示文件内容的,但是大致的使用一致,却有一些细微的差别,这些细微的差别,让我挺有感触,于是
借题发挥了,也算无聊一把。但是,毫不夸张的说,两个小命令,展示了两个不同的世界。

比较手段

    1,
最简单的创建一个带参数的进程,以显示当前目录下的文件a,文件b的内容为例。
   
2,展示进程的输出重定向使用,以将当前目录下的文件a,文件b定向到文件c中为例。
   
3,展示进程间的管道通信技术,以将当前目录下的文件a,文件b通过管道传递给另外一个输出的进程,并且显示。
  
 (以下简称Test1,2,3)。
以上述三个操作,显示echo及type的不同。

Test1:

1.
从最简单的开始吧,先以Unix下的Bash为例了。

jtianling$ echo "I'm File A."
> a
jtianling$ echo "I'm File B." > b
jtianling$ ls
a  
 b
jtianling$ cat a
I'm File A.
jtianling$ cat b
I'm File B.
jtianling$
cat a b
I'm File A.
I'm File B.
jtianling$
好了,cat a
b,这样简单的调用,进程创建完成,参数输入完成,显示正常。


Test2:

2.输出重定向,对于
Shell来说实在是小case,那简直可以说是其原生的。

TianLings-MacBook:test jtianling$ cat
a b > c
jtianling$ cat c
I'm File A.
I'm File B.
jtianling$

cat a b > c一句而已。


Test3:

3.管道,还是Shell饭碗里
面的内容。

jtianling$ cat a b | cat
I'm File A.
I'm File B.
cat
a b | cat还是一句代码而已。


DOS中type的对比:

Test1:


DOS
下,type命令也可以完全按照上述方式尝试一次:

Test1,基本可用,也就是输出嘛,但其实,仔细看看DOS下的
type命令,会发现其实输出有些许不同:

F:/MySrc/TestProcess>type a b

a

I'm
File A.

b

I'm File B.


直观来看,type a
b时自动的将a,b文件按文件名给你排下版,多人性化啊?呵呵,但是,很明显的可以看出DOS开发者的文化(MS特有?)与UNIX社群的文化差
异.UNIX中提倡命令行程序不要输出废话(比如上面的a,b及排版),这样才能够通过管道或者重定向更多的重复被其他程序使用,DOS似乎提倡给人直接
看.......


Test2:

实践发现,虽然直接的输出会有额外输出,但是type命令的对于重
定向时进行了处理:

F:/MySrc/TestProcess>type a b > c

a

b

F:/MySrc/TestProcess>type
c
I'm File A.
I'm File B.

会发现重定向后的结果与cat a b >
c完全一样.但是a,b的格式还是输出了,说明a,b格式的输出根本走的不是标准输出通道。那剩下什么?标准错误通道。。。我们测试一下。

>type
a b 2>d
I'm File A
I'm File B

>type d

a

b

>



然如此,当把错误通道重定位到文件d中时,可以看到输出了精简的输出,并且可以看到d中的内容就是附加的输出。用较为复杂的hack手段,(利用错误输出
来输出正常的信息,总之不是什么好手段)实现其实不算太必要的但是有点用的任务,是好是坏,就看个人意见了。


Test3:


发现,type其实是无法接受标准输入的,也就是,无法形成 | type的链式调用,就像cat a b | cat一样.

这样会导致
Test3无法使用type完成。这也算文化差异吧......因为Unix的Shell使用的习惯,管道及重定位几乎是命令行必备的协作工
具,DOS/Windows程序没有此传统,所以没有实现,也就可以理解了。更进一步的说,Win32
API甚至没有对管道及输入输出重定向有任何直接的支持,因为似乎根本就不在意,而POSIX的pipe,popen对于相关使用的支持却能做到非常方便
快捷。(当然,其实Windows中也有POSIX的函数的一些实现,就包括pipe,popen,但是命名前加下滑线)



结:

所以,对于type命名来说,描述为:TYPE Displays the contents of a text file. 


于cat命令,man中描述为:Concatenate FILE(s), or standard input, to standard
output.

而且,查查cat的参数就会发现,cat其实还有很多定制功能,但是type呢?估计只有/?参数
吧............

因为Unix以前相当依赖于Shell,(以前只有Shell),加上其文化的影响,加上工作需要,每个
Shell命令都是比较强大,并且协作性非常好(因为那古老的规则),其中最最典型的就是find,grep有意者去尝试一下就知道了。另外,其实说命令
的话。。。。那perl,python算不算一个命令呢?。。。。。。呵呵,扯远了。




 

原创文章作者保留版
权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie


阅读全文....

【转】开发人员为何应该使用 Mac OS X 兼 OS X 小史




一周前我和 Tinyfool 闲聊苹果操作系统,都认为对于开发人员来说,苹果操作系统(Mac OS)是上佳的选择。 Tinyfool
笔头很快,当即就写了
篇长文章

, 我则笔头很慢,今天才全部码好。 他的文章的主要切入点在于 Mac 平台作为目标开发平台的优势,而我这篇的切入点主要是 Mac
OS 作为一种开发工具的优势。

开发人员的趁手工具

对于开发人员来说,所有的开发工具的最大的用途,就是最大限度的提高开发人员的生产率 (productivity)
和创造力(creativity)。在我们这个时代,使用 GUI (图形界面) 是一个提高生产率的好手段。虽然上一代的那些 UNIX
开发人员的确不需要 GUI。一个屏幕,一个键盘,一个编辑器,在陋巷,人不堪其忧,也不改其乐的黑客比比皆是, 但二十多年过去了,
现如今开发环境发生了巨大的变化。 比如说,相比较于当年程序员使用的基于文本的环境,在 GUI
下格式丰富的文档显得更直观,阅读体验更加好;就算工作中不需要开发任何 GUI 程序,现代开发人员也会使用 GUI 来完成网页图片和文档阅览等等。
因此,即使是最传统的用命令行的开发人员,其实也能沾 GUI 的光。 比如说现在最好的终端程序,都是 X
下模拟的,因为这些模拟的终端的出现,一些复杂的可视化功能可以在这些终端中实现了,比如 Unicode 的显示(rxvt-unicode)等等。

对于开发人员,拥有一组非常好用的,能够最大程度的提高生产率的开发工具乃是一大人生梦想。那么,这套开发工具从何而来呢?
大体来说,这些工具来自于三个方面: 1. 通过系统和单一的应用软件提供的;2. 通过搭配使用各种应用软件 3. 通过定制和改变现有的应用软件。
这三点,对于 UNIX 开发人员是再熟悉不过的了, 无非就是写脚本,走管道而已。 所以,在前 GUI 时代,这一套哲学非常盛行,
开发人员都知道,需要通过安装脚本解析器,写一些的脚本,配置一些环境等等,才能把刚出厂的 UNIX 系统,改造成自己使用起来得心应手的系统。
基本上任何一个使用 UNIX/Linux 系统多年的人,机器里面都有各种各样的“私藏”的脚本。离开了这些脚本,他的效率会大打折扣。

GUI 时代传统的丧失

上世纪 80年代的时候,GUI
时代和个人计算机普及的时代降临了。从此,计算机变成了个人电脑,历史上第一次,计算机不是专为开发人员设计,而是为了普通用户设计。普通用户的需求就是
完成一个一个的现实问题,软件产业提供的解决办法就是为用户提供一个一个的应用软件,而不是让用户自己一行一行的编程和写脚本,巨大的软件需求瞬间成就了
一个巨大的软件产业。
这样的一个间接后果就是,对于普通用户来说,让一台计算机变成能够帮助自己完成任务的“个人计算机”的唯一手段,就是叠床架屋的不断的装各种应用软件。

我们可以用一个简单的例子说明这种使用模式。 我们都知道,安装 Windows
系统的一个经验原则是把操作系统和应用程序分成两个逻辑盘,一个在 C 盘,一个在 D
盘。这个磁盘分区的经验原则不光网吧老板知道,连我大学里面只会点鼠标的那些女同学都知道。为什么有这个奇妙现象呢?其实,这是由 Windows
系统的用户的典型使用模式决定的。 在 Windows 系统上,
应用程序和文档是关键,操作系统只是一个随时可以重装的东西而已,所以干脆两者分开,互不影响。在这样的使用模式引导下,Windows
系统上格盘重装是非常低成本的,只要文档不丢,应用程序不丢就行。这种使用习惯,浪费了多少 geek
男美好的时光为人重装系统,又促成了多少美妙的姻缘 :)。 总之,在 GUI
时代,要解决一个问题,就装一个应用程序。至于应用程序之间的通信,和用非键盘鼠标的方法控制应用程序等等,都不再是要考虑的问题,有这样的需求的人成了
非主流,非主流到以致于主流的操作系统和应用软件都不让你这么干了。 操作系统把所有其他的路都封死,就是明摆着告诉你,要想某样功能,请出门买软件。

Smalltalk 的启示



其实 GUI 时代原本不应该是这样的。 我们都知道,GUI 原本是施乐的 Alan Kay 那一帮人做科研做出来的,Bill Gates 和
Steve Jobs 各自到施乐”抄袭” 了一部分过来,于是窗口啊按钮啊就到处都是了。 他们都看到了图形界面和面向对象的形,
看到了图形界面就是把按钮图标等等对象放好,然后鼠标点击拖动等等这些表面的东西。 因为所有的 GUI 界面都是从文字界面起步的,所以所有的 GUI
程序,其实就是原来的可执行程序的包装。 C++ 这个语言的出现也很讨巧,把 C 包装成了一个面向对象的语言,包装对包装, C++
很讨巧的适应了把可执行程序 GUI 化的趋势, 成了 GUI
时代的主流开发语言。从表面上看,只要运行这些可执行的程序,就能够看到图形界面,就能够用鼠标点击操作他们,可是这些东西的底层,都是一个编译过了的可
执行程序,原先 Smalltalk 中的那些运行时环境啊,对象容器啊,都统统不见了,所有的图形界面程序,还是直接运行在计算机的 CPU
上,而不是一个虚拟的面向对象的容器上。而这个面向对象的容器(也叫做“运行时”或者“运行环境”),才是 Smalltalk 的神。
简单的说,Smalltalk 本身具有一个面向对象的运行时,所以即使到了执行的时候,里面所有的对象还是可以互联互通的。 而 C++
写出来的程序,除了编译之前是面向对象外,只要一编译,就全部变成机器码,和对象就再也没有任何关系了,也就不存在运行时去动态的查看(inspect)
和改变(modify) 这些程序对象的说法。 总之,因为历史的局限,这些 GUI 的平台,都是渐进的照猫画虎的演变的,所以没有一个平台像
Smalltalk
那样细致地考量过对象的互相通信的问题,再加上我们上面说了,反正扩展系统的方法就是引入新的应用软件而已,本身也没有互联互通的需求,所以这种抛弃运行
时的,不让对象被外部程序控制的实现方法也无所谓不好。

可是开发人员不是普通用户啊,他们依然要改造计算机成为自己的工具的。在现有的现有工具不能解决问题的时候,要不然自己重新发明轮子,要不然就复用
现有的一些工具,或者重新按自己的需求重新配置这些工具。 所以,和一般用户不一样,开发人员需要这些 GUI 的可配置性,也需要这些 GUI
程序之间的互联互通。 用黑话来说,第一个问题关系到 GUI 应用程序的脚本化, 第二个问题关系到 GUI 程序之间的进程间通信。
这两个问题,说起来简单,但都牵扯到 GUI 系统的根本设计问题。 历史在这里开了一个不大不小的玩笑,把这个唯一的机会给了 Mac OS
X。其他操作系统,都因为这样那样的原因,在这两个问题上没有很好的解决方案。

进程间通信,苹果的方案

花开两朵,各表一只。我们先说 GUI 程序的进程间通讯的问题。 所谓的进程间通信 (IPC),就是两个程序之间的信息共享。
我们都知道,*nix 的一个强大之处就在于管道,管道是最简单,最廉价也是最常用的 *nix 进程间通信的方法。在 GUI 时代,最常用的 IPC
机制成了剪切板和鼠标拖放操作。这两个操作虽然都很直观,但都要人操作,离开了人,程序根本无法自动完成进程间通信。
而要工作效率的提高,就是要让计算机离开了人的干涉,也能完成这些任务。为了自动化这些任务,操作系统就不能简单的绘制窗口然后万事大吉了,它必须要知道
哪些程序在运行,哪个运行的程序可以给哪个程序发消息通信等等,比如说,如果我们想自动的在阅读器里面选择一个词送给字典程序查释义,计算机就需要知道字
典程序在运行的时候可以接受一个字符串,但是不可以接受图片。如果我们把字典程序抽象成一个可以提供“查字典”服务的对象的话,毫无疑问,如果想要向字典
程序发送字符,必须首先知道字典程序能够接受什么,用什么方式把这个单词发送给字典等等。 所有的这些信息,都必须由操作系统托管才行(不可能每
个应用程序里面都要记着字典这个程序能接受字符串不能接受图片,这样每个应用程序都要记下所有其他可能的应用程序的信息,这是一个平方级别的关系,需要开
发人员开发一个程序的时候还要兼顾其他所有程序,这显然是不现实的)

。用行话来说,必须要有一个统一管理的运行环境,来管理这些程序之间的互相
通信问题。 我们上面说了,Smalltalk 的神在于一个统一的面向对象的运行时,使得所有的应用程序能互联互通。 可是所有平台上的 GUI
程序的演化进程都没有走这条路,而是只把外表给模仿走了;有的平台即使想做互联互通,也做得不彻底(比如微软的 OLE,COM 等等)。

是好东西,总会发光的。
但是要想让这个好东西被新的操作系统全盘采纳,要想让一个系统能够从底层到上层全部采用统一的运行环境,就要扔掉很多的历史包袱。甩掉这种历史包袱,对于
任何操作系统都是不容易的。如果我们回到当年,一定会幻想,要是有个神人,能够不管市场也不管现有平台,从头打造一个没有任何历史包袱的干净整洁的
GUI 系统该多好。 历史就是这么戏剧,还真就安排了一个人,做成了这件事情,这个人,就是那个斯蒂夫乔布斯。

1985 年,乔布斯被苹果扫地出门,成立了 Next 公司, 一心想要做出质量上乘的 GUI 计算机系统。
历史给了乔布斯一个全部从头做的机会。这一次,乔老师和 Next 的开发人员意识到,光照搬 Smalltalk
的形是不行的,要连它的神也拿过来,重头设计进程间通信和 GUI 系统。 在内核层面,他们用了 Mach 这个为 BSD 设计的微内核。
这个操作系统内核就是为了替换已经过时的 UNIX 内核而设计的,其中的一个核心设计哲学就是重新设计进程间通信;
虽然现在基于微内核的操作系统已经不是什么潮流(为此 Linus 和 Tanenbaum 吵了一场著名的架),但在相比较于当时 UNIX
系统的内核(此时 Linux 还没出现的,UNIX 内核只有 BSD, Bell, SUN 等几套),Mach
算是一个高的起点。在这个内核上,Next 公司的工程师开始构建面向对象的基础系统。 这套系统在 Smalltalk
中已经有了蓝图,因此这些工程师以 Smalltalk 为蓝图,先设计了一套基于 C 的语言,也就是 Objective C,照搬了
Smalltalk 的经典的 [对象 消息: 参数] 语法。 (我个人不喜欢 Objective C 这个语言,Smalltalk
是一种纯面向对象的动态类型的语言,Next 公司当年完全有机会用 Smalltalk 语言的,如果用了 Smalltalk,现在的 Cocoa
框架还会更加漂亮,代码更加干净;用 Objective C 这个自创的语言,不知道是不是因为专利的考虑,反正 Objective C
这20年的所有创新,就是在慢慢的更像 Smalltalk 而已,Java 和 Ruby 这几年也是不断的从 Smalltalk 拿东西)


有了内核,有了语言,Next 构建了一个纯的面向对象的运行环境和类库(和 Java 和 .Net 的统一类库想法类似,只不过超前了十几年),
这套类库,在当时叫做 NextStep, 所以所有的类名前面都带有 NS 前缀,无比丑陋。可惜的是,当年这个超越时代的类库太阳春白雪了,话说
Smalltalk 超越了时代 20年,所以90 年代中期的时候, 程序员才想起来当年 Smalltalk 的好,出现了 Java Ruby
等等受  Smalltalk 启发的语言。 乔老师虽然落后了 Smalltalk 5 年,却领先也业界 5-10 年,所以在 1995
年的时候, Windows 95 卖疯了, 乔老师的 NextStep 却没动静,只能把这个类库重新打包当成 Web 类库卖卖,即
WebObjects。这倒是无心插柳,生意不错,因为当时的 Web 开发已经吃尽了没有一个统一的运行环境的苦头(这也是日后 Java
风行的原因)。 我们说,是金子总要发光的,但是前提是要 (1) Next 再等几年,等业界回过神来认识到它的好处,(2)
获得一个主流的操作系统支持,把底层全换成乔老师的东西。 乔老师也知道这两个条件,所以加快了和 SUN 合作的步伐,想要把这套系统放到 SUN
的工作站上。 但是 SUN 本身有很强的底层技术,那段时间又狂推 Java, 所以其实乔老师在 SUN 这条路上胜算不大,况且 SUN
自己内核技术很强,所以肯定要肢解 NextStep 把内核重写,如果不和 SUN 玩,一来Next 这家公司能够多撑 5
年都是问题,二来几乎每家做个人计算机的公司都倒戈微软了,其他做工作站的公司又都有自己很强的底层技术,不可能用乔老师的玩意儿的,所以看起来乔老师和
他的阳春白雪好像前景不妙。
可是天无绝人之路,放眼看当年的市场,只有一家公司没有倒戈微软,又没有很强的底层技术,又和乔老师有一些渊源,历史就是这么戏剧,这家公司就是把乔老师
扫地出门的苹果。

90年代中期苹果的日子很不好过,个人电脑市场败给了 Wintel
联盟,新兴的市场上成绩也一塌糊涂,投资人也不糊涂,把当年让乔老师扫地出门的 Sculley
也扫地出门了,随后就把乔老师的公司给买了回来,让乔老师复职负责复兴苹果。 所以,上面我们说的两个条件就这样突然的满足了:
第一,他现在是老大了,所以可以彻底的把原来苹果的系统推倒重来,用自己的新家伙;第二,原来 Next
公司的那帮工程师不要担心失业了,现在由苹果负责发工资了,所以,正好可以让这些人着手改造苹果系统,主要的工作就是用自己带过来的新系统取代苹果的旧系
统,并且让新系统的图形界面和旧系统保持风格的一致。 这个工作,从1995年 Next 被收购,到 2001 左右的时候才做好,这6年的时间里,
乔老师也顺带让苹果重新盈利了。

2001 年发布的 Mac OS X, 是苹果操作系统的第十代,完全基于了乔老师在 Next
开发出来的那套类库,所以自然的,具有了一个统一的面向对象的运行时。 这个运行时和类库系统,Mac OS X 把它叫做 Cocoa。其实 Mac
OS X 刚出来的时候也不怎么好,不过依赖于这套设计精良的底层系统,Mac OS X 的迭代开发周期要比其他操作系统短多了 (仅慢于Linux,
不过 Linux 只有内核部分). 在短短的 8 年里,Mac OS X 就搞出了 7 次大的版本发布。 虽然我们看 Mac OS 好像从
10.0 到 10.6 只是次版本号在进步, 其实每次都是一个 major release, 大致相当于从 Window 95 到
Windows 98 或者 Windows 2000 到 Windows XP 这样级别的升级。
这样的发布却不改主版本号,一方面是从市场上考虑,另一方面也的确说明 OS X 的底层已经处于一个相对稳定的状态。 有很多 Windows
程序员非常推崇 .Net。 是的,.Net
的确是一个非常好的框架,可是想像一下,苹果在1995年的时候就有了一个统一的运行时,加上这么多年所有的程序都在这个统一的框架上开发,如果论在
Mac OS X 这个平台上的经验积累,应该说 Cocoa 社区是比 .Net 社区更加成熟的。

应用程序脚本化

光有进程间通信的系统还不能算是一个完全成熟的 GUI 系统,因为进程间通信依然是相对底层,而 GUI
上的应用软件是层出不穷的,不可能任何问题都跑到底层用进程间通信解决;所以,要想让 GUI 系统进化到易用和易于定制的水平,就需要开放对 GUI
程序的脚本控制。只有 GUI 程序能被外部控制了,才能真正的达到搭配使用 GUI 系统的效果。
其实,一旦有了一个统一的运行时,只要开发应用软件的时候统一设计一下脚本接口,用脚本控制 GUI 程序应该不难。 比如说,微软的 Office
系列套件, 就完全可以用 VBScript 去控制。 可惜的是,没有一个系统能够实现全系统的控制。
要实现全系统的控制,不仅仅要这个系统能够提供底层的支持,更重要的是要能说服所有的开发人员,或者说让所有的开发人员养成开放脚本接口的好习惯。
从技术上来说,这不是太大的问题,只要开发人员按照统一的脚本通信协议,实现特定的接口就行了,可是,如果一个平台上开发 GUI
的方法太多,开发人员只选自己喜欢的来,这种标准就不可能统一。 比如说 Linux 上 KDE 和 Gnome
都有自己的脚本化系统,可是开发人员有的用 KDE, 有的用 Gnome, 有的干脆两者都不用,这就谈不成有一致的接口。
一个平台要想有一致的脚本控制接口,除非 (1). 这个平台上就一种 GUI 开发方法,自古华山路一条,要不不做,做出来的东西就只能是标准的接口;
(2). 这个平台上大部分的,主流的应用软件,都实现了这个脚本接口,这样因为这些程序的拉动,其他 GUI
程序想要融入这个平台上现有的应用软件的圈子相互通信,那也就必须要实现这个接口。 在 2000
年的时候,又只有一家公司能够同时满足这两个要求,就是苹果。 微软部分做到地了这两条,基本上用 VBA 统一了 Office 的控制,但是跳出
Office,微软的 OLE 对象模型几乎没有任何用武之地,与之捆绑密切的 VBA 自然无人问津。 不过据一些在金融行业工作的朋友说, VBA
能够大大提高 M$ Office 的生产率。

GUI
脚本化不是一夜之功,特别是我们说要做出统一的脚本接口,能兼顾各种程序的需求,这就完全不是一两年的时间能够搞定的,总需要很多年的技术积累和设计取舍
后才能收敛到一个相对稳定成熟的系统, 而苹果,居然很神奇在十几年前就有这方面的经验,苹果再次怎么这么幸运呢?

在 80 年代后期的时候,苹果机上有一个非常超越时代的软件,叫做 Hypercard。
这个软件我曾经在上一代苹果上玩过,具体的思想就是你可以存储一张一张的“卡片”,这些卡片上面可以放置多媒体的声音,图像文字和其他对象,基本上就和现
在网页一回事,唯一的区别就是这些卡片都存在本机。 在没有 Powerpoint 这类软件之前,这个 Hypercard
的软件可以用来做课件,做幻灯片演示等等,是个极其强大的工具。 为了让用户可以定制这个卡片,这个程序提供了一套非常强大的编程系统,叫做
Hypertalk。 因为这种编程语言是给普通人而不是程序员用的,所以你会感觉根本不是编程,而是写英语。这套东西,虽然本质上也是从
Smalltalk
学来的,但是用英语语法的方法编程的确是一个全新的思路,苹果把这个给普通人编程的语言发扬光大了,用更加贴近自然语言的方法重写了语言和文档,模仿
Hypertalk 系统,发布了一个跨系统的脚本控制语言,叫做 Applescript。这个语言就和自然语言没什么区别,比方说,
获取窗口的大小不再是
window.getSize()

而是
get size of window

显示第 22 段的 第一个单词不再是
print(paragraph[22].getWordByIndex[0])

而是
print the first word of paragraph 22

更狠的是,你还能用法语和日语写。 80年代后期的时候和整个 90年代初期,苹果基本上已经被 PC
机逼到墙角了,只剩下出版行业,设计行业等等专业的行业因为应用软件和图形处理能力的关系,依旧在守着苹果机。 两个行业的用户都需要自动化的 GUI
控制,但是编程都不怎么样,于是,这些应用软件的开发商也主动掺合加入 Applescript 旗下。 在90年代乔老师没有加入前,苹果自己把
Finder 全部脚本化,出版业的 QuarkXPress 和 Filemaker 也都完全脚本化,等乔老师入主苹果后,基于 Cocoa
的新技术,苹果一口气在 Mac OS X 上推出了 Safari, iTunes, iPhotos 等等软件,一股脑儿的全部脚本化了。
在别的公司都可望而不可求的历史机遇,又是被苹果给抓住了,一股脑儿全部塞进了 Mac OS X。这下,所有的第三方开发的工具,如 Firefox,
Adium 这些,其实本来都不是苹果开发的,也没有太强的苹果渊源,但是 Firefox 要读 Safari 书签吧,哈,那就用
Applescript 吧,所以, Firefox 也逼着脚本化了(这个在其他平台上都不存在的事情)。 Adium 也是,这个聊天软件想要把
iTunes 正在播放的歌曲当成状态信息,好呀, Applescript,所以,也被带着脚本化了,而在 Linux 上的对应产品 pidgin
就没有这么脚本化。 所以,苹果平台已经成了一个惯性,你不想脚本化,就不带你玩,看你还脚本化不?

结语

我们都知道, UNIX 时代的主要哲学是提供给开发人员一组小巧精美且可以任意搭配使用的小工具,也就是所谓的 Software Tools,

然后任由开发人员由此出发,自己搭建自己的工具,打造自己的瑞士军刀。而开发人员所用的操作系统的目的,要不就是提供这样的一组开发工具,要不就是为这样
的开发工具提供一个便利的平台,使得这样的工具变为可能。如果说 UNIX 是命令行时代的一个易于改造成 “自己的操作系统” 的操作系统的话,
Mac OS X 就是 GUI 时代的这样的一个操作系统。 即使是从应用软件的层面看, Mac OS X
的底子好,更加容易出精品软件,所以即使仅使用应用软件,开发人员也应当优先考虑 Mac OS X。

附A: 相对正确的 Mac OS X 使用习惯

0. 一定要装 Quicksilver 或者用“服务”,否则就是把苹果当 Windows 用。
1. 在苹果计算机上,因为有服务和 Quicksilver 这样的工具,90% 的程序间的拷贝粘帖都是可以避免的。
2. 剩下的 10% 的程序内的拷贝粘帖,如果用一个好的编辑器的话,又可以省略掉 90%。

附B: 为什么 Linux 系统在这个方面还不够好

第一, Linux 上的 GUI 子系统,其实不是 Linux 的一部分,而是 X 和上面的 KDE 以及 Gnome
等等。 这几年,这些系统终于开始统一管理一个面向对象的运行环境了。可是这两个系统都是用C++
所写,所以免不了费很大的力气才有了运行时信息,绕了一个大弯路,如果一开始这两个系统就用 Smalltalk
之类的有运行时的语言编写,至少现在应该有能和 Cocoa 抗衡的框架。

第二, 这几年 X 也认识到了在脚本化控制上面的不足,所以几年前做桌面的 Redhat 提出了 DBus 标准。
可惜的是不是每个程序都开放了 Dbus 接口,所以和苹果比起来,还有比较长的路要走。

 

作者知识比较丰富。。。。。历史老师?-_-!而且,很明显对Small Talk有太多的偏爱。。。。。。。

阅读全文....

【转】为什么我认为每个程序员都应该用Mac OS X?

原文:http://tiny4.org/blog/2010/02/why-programmers-should-use-mac-os-x/

查尔斯·狄更斯老师的《双城记》里有句非常著名的话,我每次看到都心潮澎湃,所以看了无数次《双城记》总是在那两句话前后打转。心说,开头就这么
好,正文无论如何也无法让你觉得精彩了吧?

原文是,“It was the best of times, it was the worst of times, it was the
age of wisdom, it was the age of foolishness, it was the epoch of
belief, it was the epoch of incredulity, it was the season of Light, it
was the season of Darkness, it was the spring of hope, it was the winter
of despair, we had everything before us, we had nothing before us, we
were all going direct to Heaven, we were all going direct the other
way–in short, the period was so far like the present period, that some
of its noisiest authorities insisted on its being received, for good or
for evil, in the superlative degree of comparison only. ”

译做:“那是最美好的时代,那是最糟糕的时代;那是智慧的年头,那是愚昧的年头;那是信仰的时期,那是怀疑的时期;那是光明的季节,那是黑暗的季
节;那是希望的春天,那是失望的冬天;我们拥有一切,我们一无所有;我们全都在直奔天堂,我们全都在直奔相反的方向–简而言之,那时跟现在非常相象,某些
最喧嚣的权威坚持要用形容词的最高级来形容它。说它好,是最高级的;说它不好,也是最高级的。”

我认为对于程序员来说,我们正在经历最美好的时代,当然也是最糟糕的时代。

美好的原因在于,这是一个充满机会的年代,近十年每次我们以为世界一切尘埃落定的时候,就会出现新的惊喜。如youtube、facebook、
twitter等等,甚至包括了iPhone以及即将上市的iPad。世界上无数聪明和勤奋的人们正在努力,告诉我们一切还早,我们还活在一个迅速发展着
的世界。只要世界还在迅速发展,谁知道你会不会就是下一个twitter、下一个iPhone的发明者?甚至是下一个马化腾,虽然现在腾讯如日中天,但是
如果时间倒流到99年,甚至01年,大多数心中可能还是会觉得那只是个玩物吧?

然而,糟糕其实也正在于此,前面的机会多,陷阱就多。人们天生就是风险躲避者(某个名人或者是名著里面说的,可惜记不清了,无法拿来掉书袋了),这
也许就是独裁百试百灵的原因。人们总害怕错误的选择,所以变得犹豫起来。现在这样的时代,你就有无数可以犹豫的事情,因为很多事情还在发展中,不知道谁会
笑到最后。最近仍在胶着的战斗很多,如,Wifi和3G(我认为赌局还没开场),微软和Google,微软和苹果,苹果和Google,苹果和Nokia
等等等等。

这些战斗的结果,对普通用户来说,只是买什么产品,用什么产品的问题。对开发者来说则是抱谁的的大腿,跟谁混的大事儿。

所以我们这些久经沙场而没有发达的家伙,总是对面前的选择感到胆战心惊,甚至养成了父母问你吃什么都会大发雷霆,大喊“不知道”的选择恐惧症(我的
症状非常严重,出去吃,绝不点菜,在家里一律随便。但也有可能是看Monk看多了闹的)。

之前我和赵姐夫(@Jeffz_cn)
霍炬(@virushuo)
方块(@KrzyCube)
还有很多人在twitter上面大侃过一
次这个问题,有兴趣的可以看看Cat Chen的评论
、以及
姐夫的整理

、和方块的整理
(我
们的讨论很长,但还算有趣吧)。

现在,我已经远离了Windows阵营,做Web开发用Php,做客户端只做Mac客户端软件,还做一些iPhone开发。我认为每个程序员都应该
至少用一用Mac OS X。

原因如下:

1、操作系统的竞争到了一个新的阶段

Windows虽然仍旧是实质上的老大,但是操作系统的战争早已不是10年前的样子。随着Web应用的大行其道,很多人的电脑,都已经成了名符其实
的上网本或者上网机。

Window/Mac OS
X/Linux/Unix几大主流平台上都有了流行的Web浏览器,只要你不是一个执着的IE粉丝,那么如果你的主要任务是Web浏览,现在几大操作系统
的体验已经非常接近了。其他的基于互联网的流行软件,一般也都有比较好的跨平台支持。例如,聊天工具,Gtalk全平台覆盖,Msn全平台覆盖,现在甚至
连一向被人看作很封闭的QQ,实际上也有Linux版本(虽然口碑很大,但是还有lumaQQ这样的替代产品),Mac版本(口碑不错),iPhone版
本了(最近刚更新过,很不错)。

我们甚至看到了上网本这一硬件类别的产生。互联网成了计算机的中心,这很大程度上降低了微软的垄断优势。

3-4年前,我切换到Mac OS
X的时候,除了招商银行等少数应用以外,我没有觉得任何的痛苦。那时候,我主要开发服务器端程序,程序的运行环境是Linux,在Mac OS
X这个Unix上面开发,其实很方便。

程序员可以在不同平台切换并不重要,重要的是,我们这些程序员的衣食父母已经可以自由的在各种平台上切换了。这就给了我们去尝试Windows以外
平台的可能性。

很多人在比较Windows和Mac OS X的时候,有一个误区。因为他们自己是长期的Windows用户,对很多Mac OS
X的细节很不喜欢(右键、按钮位置等等)。但是作为一个开发者,你要注意的是用户。我希望你们设想一个场景,一个从来没有用过电脑的人,他们学习
Windows和Mac OS X,他们会更喜欢什么。我想更多人会喜欢Mac OS X,如果我的设想是对的,那么未来是属于Mac OS X的。

2、iPhone以及未来的iPad

iPhone是近10年来,我看到的最革命性的硬件产品。虽然他的功能,除了多点触摸、加速度传感器以外,大多数我们都在Nokia和其他竞争者的
产品中见到过。我用过Nokia2002年的产品7650,iPhone的所有功能它几乎都有(没有wifi,但是有gprs),但是用起来跟
iPhone就是不一样。

那么区别在哪里呢?05年我买iPod
Nano的时候,看到盒子的时候,我吃了一惊,因为盒子最显眼的地方写的不是“容量:4G”,而是“可以容纳1000首歌,xxx张照片”。这家卖水果的
公司突然让我明白,用户其实关心的是那个叫mp3的东西可以容纳多少歌,而不是从比特的角度,从存储的角度他的容量是多少G。多少G多少M的容量,是实现
“容纳多少多少歌”这个目的的一种手段。我们总是太过care手段,忘了我们的目的是什么。

做产品的时候,很多人也是这样。如果你是第一次看电视购物卖手机,也许你就会想把手里面的iPhone扔掉,去买那些垃圾。因为你听介绍的时候,你
会觉得全世界最好的手机就在那电视里,叫做“XX全能王”。从功能列表来看,它们简直是世界上最好的产品,但是如果你真的拿到手,你也许会发现那东西3分
钟死机,也许什么操作都很慢,也许摔了一下就坏了。那个时候,我估计你会领悟到,功能多当然好。但是功能列表不会包括“不会3分钟死机”,“操作不会很
慢”,“摔一下不会坏”。手机,或者任何产品对你最重要的不是功能多,而是好用。

iPhone让我发现原来手机上也可以有这么好用的浏览器。这是个纯技术问题么?不是,要知道Nokia很多手机用的浏览器内核和iPhone毫无
二致,都是Webkit。但是操作起来看看,大家知道iPhone出来没有多久,所有网站手机流量最大的就是iPhone了,这不是靠运气啊。我有个
Nokia
E61i欧版手机,可以用wifi,但是用它的浏览器,实在是不方便,每次都要重新选择wifi,打开网站也很慢,打开了以后,放大缩小也很不方便。

好的体验仅是其一。更重要的是水果公司在iPhone这个产品上开发了AppStore,这是近10年来,我认为在软件行业最重要的一个东西。1976年2月3日比尔盖茨发表了著名的《An
Open Letter to hobbyists》(给爱好者们的一封信)

,事实上开启了商业软件的道路。然而,商业软件从诞生起,就受到盗版
的诅咒。从全球看,表面上微软是盗版的最大受害者。没错,从金额上看,是这样的。但是实际上所有的第三方开发者,才是盗版的最大受害者,他们中的很多人因
为盗版而丧失了成长为微软这样企业的机会。

AppStore不能完全杜绝盗版,但是它从发行和支付渠道出发(跟iTune
Store如出一辙),很大程度降低了盗版对第三方开发者成长带来的风险。很多单枪匹马的第三方开发者,凭借做iPhone软件发家(当然一个市场是有容
量的,你不能指望它超越自己的机极限)。很多开发Mac软件多年的人,没因为开发Mac软件发家,但是因为移植了一个简单版本到iPhone发了家。据
说,因为开发iPhone只能用Mac OS X,这成了近两年对Mac电脑销售最大的拉动。很多人为了开发iPhone程序而购买Mac电脑。

就在这时iPad来了。价格在2000-3000之间的上网本,应该是最近和未来,电脑市场一个很重要的增长点。这个价位可以吸引很多买不起电脑
(可能更多是不舍得买电脑)的人。另外,关注上网这个概念简化操作,也可以让很多觉得电脑太难用的人们,来购买电脑。可惜,基于Windows的上网本在
用户体验上实在是乏善可陈,往往仅仅是一个屏幕更小的Windows而已。

Windows在PC上面的优势本来有机会拓展到其他平台。在WinCE和Palm,微软实际上获胜了。但是之后就固步自封了,现在iPhone出
来了,WinCE实际上已经没有什么大机会了。上网本和触摸设备,本来也是Windows有巨大优势的,但是微软实在缺乏在用户体验方面的努力(你去看看
各种基于Windows的触摸查询设备吧,居然需要点一下然后鼠标光标蹦过来,然后才能点!!!!!!!)。

iPad的发布会,并不让人多么惊喜。这是因为iPad和iPhone战略在水果公司是紧密结合的,iPad包含了iPhone的所有革命性,然后
把尺寸放大。发布会开到结尾,说到电池时间和价格的时候,我才开始兴奋。

iPad意味着什么?意味着一台便宜的上网本;有电脑的用户也不介意购买一个,买不起或者不舍得买电脑的人可能会买;因为便宜,所以医院和餐馆等可
能会购买作为内部管理系统(医院信息系统,点餐系统);学校可以买做教育用途(电子书包)。意味着,我们可以在电脑上面写文档累了的时候,躺在床上抱着它
听歌,看看书,玩个游戏。

这个产品如果操作的好的话,可以造就一个比iPhone更大更好的软件市场。

3、Mac OS X是Unix

对于大多数做服务器端开发的开发者,服务器端开发也许就意味着Linux下的C/C++和Java。Mac OS
X是真正的Unix(符合标准,有授权,合法的可以用Unix商标)。大多数这类开发,你可以在Mac OS
X下做,在Linux下执行。少数涉及到Linux和BSD内核不同的应用,稍微麻烦一点。但是如果你做到这个级别的话,我想你也不在乎了。

大多数基于Linux的开源软件,大多数为Unix架构设计的程序,都可以在Mac OS
X下正常工作。虽然有很多软件编译起来,会遇到很多稀奇古怪的问题,但是放心,你在不同的Linux版本间编译的时候遇到的会更多。虽然Mac OS
X下面的自动软件安装工具也会有各种各样的问题,但是放心,Linux下的也是。

Unix文化最经典的管道和脚本,Mac OS X也支持的很好。

Mac OS
X是一个非常好的平衡,Unix式的命令行、脚本能力它全部具备,而且带有一个非常好用高效的GUI。所以不管你是CLI(命令行界面)控,还是GUI
控,你都应该可以玩的很好。

有人问我Mac OS X是不是一个很好的学习Linux的途径。我不得不说,你要是真的玩转了Mac OS
X(CLI+GUI)的话,你真的无需去学习Linux,用就是了。尤其是,学习Linux是为了学习服务器管理的人们,真玩转了Mac OS
X,Linux有什么区别?

4、开拓你的视野

一段时间以来,
研究自己发展的过程中的瓶颈的时候,发现一切瓶颈都不来自能力,都来自视野,来自于给自己设置的愚蠢的极限

。这么说,是因为我发现,我想做一辈子
的程序员,而我也乐意学习新的知识,我也确实擅长不停的学习新的知识。

只是每经一个阶段,一旦我发现自己长期没有什么进步的时候,就会发现原因就是我已经不知道前面的路在哪里了。在.net出现之前,我已经是一个微软
几乎所有产品的专家(但我不是MVP
),
我可以不借助ATL自己写Com组件,我写过IE和Outlook插件,等等。但是到了.net时代,我的心疲惫了,我决心不再跟随微软的脚步。

这两年,尤其是09年,做了一些iPhone开发,Cocoa开发
(Tiny4Reader)

,做了些服务器端开发(GinkgoTek.com),我又开始像刚开始接触电脑那样的激情澎湃了。

对大多数纯Windows程序员,Mac OS
X的GUI和基于Unix的CLI都是新的天地,哪怕你不真的从事这两方面的开发。在这些领域看到的东西对你的开发都是有利的。

腾讯是一个大家印象中很不注重用户体验的公司(老程序员视角)吧?但是现在QQ已经有了Mac版,iPhone版,而且iPhone版本已经多次打
磨,已经是一个相当好用的iPhone应用了。年前我去了趟深圳,见了他们的iPhone开发team,他们很重视用户体验,也做了很多的尝试。而腾讯更
多其他平台开发的工程师,也开始更加关注用户体验了。

对绝大多数Windows程序员,关注用户体验的经验和意识都不足,使用Mac OS X有利于提高这些方面的素养。

最近我在参与翻译《Cocoa® Programming Developer’s Handbook》,从中知道,我最崇拜的Tim Berners-Lee爵士

就是在Mac OS
X的前身,NeXTSTEP操作系统(NeXT公司产品,乔布斯创建的公司,后来被买回水果公司,这个操作系统和原有MacOS一起,成了Mac OS
X的前身)下开发的人类第一个浏览器WorldWideWeb的,打开了人类的Web时代的。

附,本文引发的一些相关的讨论:

 

不尽同意原文观点,但是比较欣赏作者对水果的热爱

阅读全文....

MacOS中的全局快捷键设定 With Objective C

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

    全局快捷键属于比较有用的功能了,在Windows下使用RegisterHotkey可以很方便的设定,(指的是通过程序设定啊)Qt中没有封装此功能,所以稍微麻烦一点,可以参看我原来的文章《Qt/PyQt中使用系统全局的快捷键》,换到了MacOS中后,又得重新学习了,真是悲哀。。。。。。。
    搜遍互联网,才总算发现有用的文章,《Program Global Hotkeys in Cocoa Easily》一文真是精华中的精华。不仅说明了怎么在MacOS中设定全局快捷键,而且超额的指点了怎么让用户在程序中自定义。。。。强就一个字,作者绝对是介于牛A及牛C之间的人物。
    唯一还有点遗憾的是,作者虽然是说用Cocoa,但是其实使用了carbon框架,而carbon框架使用的还是C语言的接口,并且有回调函数的设置,导致接口使用上不能用纯Objective C。。。。。。
    本文仅记录大致使用流程,作为备档,详细的接口意义及各类,结构的意义未作详细说明(文档中我都没有查到较为详细的说明)。

准备工作

如上所述,用了Carbon框架,所以首先得为工程添加框架得链接依赖,然后使用上包含Carbon/Carbon.h文件。

注册全局快捷键回调函数

回调函数的原型如下:
OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent,
                                                 void *userData) {
}

注册的方式

        EventTypeSpec eventType;
        eventType.eventClass=kEventClassKeyboard;
        eventType.eventKind=kEventHotKeyPressed;
        InstallApplicationEventHandler(MyHotKeyHandler, 1, &eventType;,NULL, NULL);
主要的函数是InstallApplicationEventHandler,表示注册相应的回调函数,作为C语言接口,回调的函数原型必须一致。

注册快捷键


        EventHotKeyRef gMyHotKeyRef;
        EventHotKeyID gMyHotKeyID;
        gMyHotKeyID.signature='rt2h';
        gMyHotKeyID.id=1;
        
        RegisterEventHotKey(6, controlKey, gMyHotKeyID,
                                                GetApplicationEventTarget(), 0, &gMyHotKeyRef;);
        
        gMyHotKeyID.signature='te2h';
        gMyHotKeyID.id=2;
        
        RegisterEventHotKey(7, controlKey, gMyHotKeyID,
                                                GetApplicationEventTarget(), 0, &gMyHotKeyRef;);
注册快捷键,RegisterEventHotKey是整个过程中最重要的接口,第一个参数的数字,代表了最终响应的键值,这里有点奇怪,不是像Windows中那样使用表示虚拟键值的宏,而是直接用按键代表的数字来表示,而且此数字甚为奇怪,我也很为纳闷。比如上面的6,7分别表示Z键和X键。这些数字我只有在《Program Global Hotkeys in Cocoa Easily》一文中提到的AsyncKeys软件中我才能正确的知道。此软件运行时,如下图:

上面就是我按Z键时,显示的样子,其中我们需要设置的值为AsyncKey Number那一栏的值,即6.。。。。。。。。原因不明,没有深入了解。
RegisterEventHotKey第二参数是控制键的设置参数,分别可以为cmdKey, shiftKey, optionKey,controlKey,各自的意思我想就不用我讲了,不过需要注意的是,同时设置时不是用一般的|符号来组合,而是用+。。。。。。这点还真是比较奇怪,难道Mac下的接口都是这样设置的吗?-_-!对了,不需要设置这样的常量感到奇怪,Mac下的代码风格有所不同,以前Windows下(其实我在其他平台也是这样),常量习惯性的全大写,这样大家就能知道这是不能改变的宏或者常量,但是Mac下的接口不是这样。。。。但是,知道它们都是系统常量就好了。

实现回调函数


OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent,
                                                 void *userData) {
        EventHotKeyID hkCom;
        GetEventParameter(theEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,
                                          sizeof(hkCom),NULL,&hkCom;);
        int l = hkCom.id;
        
        switch (l) {
                case 1: //do something
                        convertRtfToHtml();
                        break;
                case 2: //do something
                        convertTextToHtml();
                        break;
        }
        return noErr;
}

这里通过GetEventParameter来获取需要的信息,然后通过事件的ID来分辨是哪个快捷键按下。整个过程基本就是这样了。

小结

接触Cocoa的时间较短,发现接口设置的一些惯例都不太一样,虽然在GUI世界,消息(或者说事件)驱动是肯定的,但是在Windows的Win32 API及MFC那个层面编写代码的时候(.net不了解,就不知道了),消息的流向是知道的,可控制的,消息响应的时候也常常在OnMessage函数中自己去设置,但是Cocoa中感觉封装的层次更加高一些,我不知道消息从那儿来,又到哪儿去,只能通过回调或者控件的binding,action的链接,类的委托来设置我希望被调用的函数,具体啥时候调用,入口在哪,我都无法控制,这点,可能属于从较为底层跑到较为高层有点不适应。作为C/C++程序员,可能难免带上了一定的刨根问底的性格,甚至一定要回溯到系统加载进程的汇编代码才肯善罢甘休,不然总是觉得没有底,在不能彻底了解一个库的源代码前,甚至都不敢大量的使用,不然效率怎么样,内存怎么控制的,健壮不健壮总是没底,到了Objective C with Cocoa的世界后,还有点不适应。。。。。。。。。。

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

MacOS中的剪贴板操作 With Objective C


MacOS中的剪贴板操作 With Objective C

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

每日废话

     从《Macos中的Mercurial GUI工具Murky试用》一文的访问量基本可以看出,国内使用MacOS 的程序员毕竟还是少数…………就如当年关于Linux的文章没啥人看一样。。。。。
    呵呵,但是读者再少,工作还得继续,学习还得继续,我写我的,没人看就当自言自语了。(其实有人看的时候也常常自言自语-_-!)

前言

     对于常发博客的我,以前很明智的放弃了Windows Live Writer来写博客,而是转向了Google Document,所以现在即使在MacOS平台,对写博客都没有太大影响,唯一有个问题,对于技术博客,难免会发一些代码,WLW有很多代码高亮插件可用,Google Docs却没有,因此,转向Google Docs写博客后,深度依赖自己写的代码高亮工具(见《一键在剪贴板中进行语法高亮的工具发布》,此工具竟然还有人提Win7总是会出行号的Bug,因为我家的 Win7删了,所以没法试了,起码说明除了我还真有人用^^,其实,说实话,实在太好用了,最新的源码连弹出窗口的问题都解决了)当时为了方便,使用了一些平台相关特性,并且是用PyQt完成的,所以现在在MacOS中没得用了。。。。。郁闷啊,首先解决这个问题吧。
     本来还是可以使用Qt的,会省很多事,一些东西也不用学了,在Windows,Linux平台我就这样做了,但是我刚接触MacOS,出于学习的目的,我还是使用Objective C With Cocoa来完成,鉴于国内MacOS玩家本来就不多,Objective C玩家就更少了,对本文不做太高期待了…………
    因为上述原因,本文自然还无法实现语法高亮了,见谅,还好发现XCode中的代码复制过来使用RTF格式的,虽然无法实现高亮(Google Docs不知道以RTF格式读取)缩进格式还在,不幸中的万幸了。

真的开始

     参考2指出,MacOS中粘贴板服务是一个独立的进程,名为/usr/bin/pboard,验证一下:
$ ps -A | grep pboard
   81 ??         0:00.00 /usr/sbin/pboard
  408 ttys000    0:00.00 grep pboard

看来没有错。
先看看基本的使用:
因为没有发现命令行下编程使用MacOS剪贴板的办法,所以再简单的测试也使用窗口程序了,先用XCode建立一个Cocoa程序,我这里命名为MacPasteboardTest,
然后用 Interface Builder拖入一个多行的Text Filed用于显示剪贴板的内容。(Windows下叫clipboard,MacOS下叫Pasteboard。)嫌麻烦,按钮都省了,直接通过菜单绑定action,这样还能使用快捷键:)
另外,为了简化程序,我直接使用自动生成的程序的委托类了,不建立自己的类了

MacPasteboardTestAppDelegate.h:

#import Cocoa.h> <br >
@interface MacPasteboardTestAppDelegate : NSObject {
    NSWindow *window;
    NSTextField *textField;
}

@property (assign) IBOutlet NSWindow *window;
@property (nonatomic, retain) IBOutlet NSTextField *textField;

- (IBAction) cut:(id)sender;
- (IBAction) copy:(id)sender;
- (IBAction) paste:(id)sender;

@end

MacPasteboardTestAppDelegate.m

#import "MacPasteboardTestAppDelegate.h"

@implementation MacPasteboardTestAppDelegate

@synthesize window;
@synthesize textField;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
}

- (void)writeToPasteboard:(NSPasteboard *)pb withString:string{
    [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType]
               owner:self];
    [pb setString:string forType:NSStringPboardType];
}

- (BOOL)readFromPasteboard:(NSPasteboard *)pb {
    NSArray *types = [pb types];
    if ([types containsObject:NSStringPboardType]) {
        NSString *value = [pb stringForType:NSStringPboardType];
        
        [[self textField] setStringValue:value];
        return YES;
    }
    
    return NO;
}

- (IBAction)cut:(id)sender {
    [self copy:sender];
    [[self textField] setStringValue:@""];
}

- (IBAction)copy:(id)sender {
    NSPasteboard *pb = [NSPasteboard generalPasteboard];
    [self writeToPasteboard:pb withString:[textField stringValue]];
}

- (IBAction)paste:(id)sender {
    NSPasteboard *pb = [NSPasteboard generalPasteboard];
    if( ![self readFromPasteboard:pb] ) {
        NSBeep();
    }
}

@end

    然后,将菜单对应的cut,copy,paste的action都与此委托类的相应action绑定就好了。(类似的话都是对知道的人太多了,对于不知道的人这样说估计还是不懂,推荐不懂的可以去看看某个Hello World教程,那样都会了。)
    这里,最最主要的就是使用NSPasteboard这个系统类了,所有的剪贴板操作都依赖于它。这样实现后,基本与参考2中21章的程序差不多了,此例中因为觉得考虑Selected字符串等情况较为麻烦,所有的操作都是直接针对控件的整个字符串,所以有点不符合操作习惯,需要注意一下,因为现在的主题是剪贴板,这些就算了。

剪贴板内容的类型

     可惜的是原书也仅仅这样点到为止了。我的需要更多,所以我需要进一步的挖掘剪贴板的功能,我的目标是直接复制HTML内容到Google Docs中,并且可以正常识别,因为以前的学习经验,知道这牵涉到剪贴板的内容类型,比如上例中都是NSStringPboardType类型。参考2一共列出了17种各种各样的类型,可惜没有HTML类型,我很惊讶,查了查文档,参考2太老了,MacOS 10.5就有HTML类型了,但是剪贴板的类型字符串在MacOS 10.6有了较大的改变,应该仅仅是从非常量改成常量了,使用应该差不多,为了兼容性,我还是使用MacOS 10.5那种非常量的吧。
文档:
Types for Standard Data (Mac OS X 10.5 and earlier)
The NSPasteboard class uses the following common pasteboard data types.

NSString *NSStringPboardType;
NSString *NSFilenamesPboardType;
NSString *NSPostScriptPboardType;
NSString *NSTIFFPboardType;
NSString *NSRTFPboardType;
NSString *NSTabularTextPboardType;
NSString *NSFontPboardType;
NSString *NSRulerPboardType;
NSString *NSFileContentsPboardType;
NSString *NSColorPboardType;
NSString *NSRTFDPboardType;
NSString *NSHTMLPboardType;
NSString *NSPICTPboardType;
NSString *NSURLPboardType;
NSString *NSPDFPboardType;
NSString *NSVCardPboardType;
NSString *NSFilesPromisePboardType;
NSString *NSMultipleTextSelectionPboardType;

Types for Standard Data (Mac OS X 10.6 and later)
The NSPasteboard class uses the following constants to define UTIs for common pasteboard data types.

NSString *const NSPasteboardTypeString;
NSString *const NSPasteboardTypePDF;
NSString *const NSPasteboardTypeTIFF;
NSString *const NSPasteboardTypePNG;
NSString *const NSPasteboardTypeRTF;
NSString *const NSPasteboardTypeRTFD;
NSString *const NSPasteboardTypeHTML;
NSString *const NSPasteboardTypeTabularText;
NSString *const NSPasteboardTypeFont;
NSString *const NSPasteboardTypeRuler;
NSString *const NSPasteboardTypeColor;
NSString *const NSPasteboardTypeSound;
NSString *const NSPasteboardTypeMultipleTextSelection;
NSString *const NSPasteboardTypeFindPanelSearchOptions;

需要注意的是,NSSring本身就是常量,这里所谓从非常量到常量其实是对该指针值而言的,从NSString *到NSString *const的区别在于,NSString *值的指针可以改变指向(虽然不能改变内容,但是对于系统常量来说还是非常危险啊,这应该算是设计缺陷了,所以MacOS 10.6修改过来了),NSString *const的就是无论内容,指向都不能改了。类似于C++中const *及 const * const的区别。这里展示一下这个危险性(勿学),同时使用HTML类型试试,看能不能达到我想要的与Google Docs兼容的效果。
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    
    // notice!It's ugly but legality.
    NSStringPboardType = NSHTMLPboardType;
}

这样的代码竟然是合法的,你说苹果不改能行吗?

如上图所示,NSStringPboardType类型的值已经变了。。。。。。当然,我就需要这个类型,所以我诡异的这样使用了。(勿效仿)可以看出,就算是苹果公司的框架设计者也会犯一些非常低级的错误的。

看看HTML剪贴板的效果

输入上述内容,注意,上面可是带HTML的了啊:)复制,然后在Google Docs中粘贴,如下:

我是从MacPasteboardTest复制过来的

看到了吗?Google Docs识别出来了,就如同我以前在Windows中用Qt实现的一样,HTML内容的剪贴板复制到Google Docs是直接识别的,而不是作为文字常量给输出,那么,事实上,我可以给大家看看将来我的MacOS的语法高亮软件实现的效果了:)
//
//  MacPasteboardTestAppDelegate.m
//  MacPasteboardTest
//
//  Created by JTianLing on 3/3/10.
//  Copyright 2010 JTianLing. All rights reserved.
//

#import "MacPasteboardTestAppDelegate.h"

@implementation MacPasteboardTestAppDelegate

@synthesize window;
@synthesize textField;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
        // Insert code here to initialize your application
        
        // notice!It's ugly but legality.
        NSStringPboardType = NSHTMLPboardType;
}

- (void)writeToPasteboard:(NSPasteboard *)pb withString:string{
        [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType]
                           owner:self];
        [pb setString:string forType:NSStringPboardType];
}

- (BOOL)readFromPasteboard:(NSPasteboard *)pb {
        NSArray *types = [pb types];
        if ([types containsObject:NSStringPboardType]) {
                NSString *value = [pb stringForType:NSStringPboardType];
                
                [[self textField] setStringValue:value];
                return YES;
        }
        
        return NO;
}

- (IBAction)cut:(id)sender {
        [self copy:sender];
        [[self textField] setStringValue:@""];
}

- (IBAction)copy:(id)sender {
        NSPasteboard *pb = [NSPasteboard generalPasteboard];
        [self writeToPasteboard:pb withString:[textField stringValue]];
}

- (IBAction)paste:(id)sender {
        NSPasteboard *pb = [NSPasteboard generalPasteboard];
        if( ![self readFromPasteboard:pb] ) {
                NSBeep();
        }
}

@end

    效果不错吧:)MacOS版本的Code Highlighter已经不远了。。。。。。。呵呵,别流口水啊。

 

不足之处

     做了3个版本的语法高亮软件了,也试用了不少别的Code Highlighter,甚至还有在线版的,其实还是我自己以前做的那个最符合我自己的需求,毕竟,我就是针对Google Docs,我也就只需要HTML格式的剪贴板就可以了。但是,一直以来其实有个地方很不完美,我的语法高亮完全依赖Vim,(当然,依赖也没有什么不好),这是其一,另外,Vim的语法高亮的确是漂亮,但是已经无法复原VS/XCode/Eclipse本身的效果了,但是其实无论从上述哪个IDE中拷贝代码,其实原来的高亮信息其实是在的,只是Google Docs比较蠢(Google Docs is silliness),仅能识别HTML格式的剪贴板内容不能识别其他内容(比如RTF)。
    举个例子,看看XCode本身的语法高亮内容在剪贴板中的显示:(利用了MacOS 10.6中Finder的Show Clipboard功能,啊?怎么到了这里变成clipboard啊?类的命名全叫pasteboard。。。。唉,估计MacOS与XCode的开发者不是同一批。。。。)

其实语法识别内容是比VIM要多的多的。。。。。注意 cilpboard窗口下面的文字,Clipboard contents: rich text(RTF),多么笨的Google Docs啊,连RTF都识别不了。。。。。。。。。。其实,最最好的解决方案应该是为Google Docs开发插件,以直接识别RTF格式,再次一点的,就是制作一个RTF->HTML的软件然后再到Google Docs中了。。。。。有时间我尝试一下吧。

最后

     第一次在Mac Book上写技术博客,这么点的东西花了我一个晚上。。。。。。。。悲哀。。。。。。毕竟对XCode和Objective C还是不太熟悉啊,当然,MacOS下的输入也不算太好,苹果的笔记本键盘嘛。。。。其实我感觉有点硬,达不到运指如飞的境界。。。。呵呵,扯远了。

参考

1.《Objective-C 2.0 程序设计》Stephen G. Kochan著
2.《苹果开发之Cocoa编程》第三版 Aaron Hillegass著
3. 苹果在线文档
    

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....