Fork me on GitHub

9/27/2010

Xcode 好用快速鍵備忘

記錄幾個好用且常用的 Xcode 快速鍵

1. 註解/反註解: Cmd + /
選取一段你要 註解/反註解 的程式碼,按下 Cmd + / 即可。類似 VS 的 (Ctrl + K + C, Ctrl + K +U)

2. 新增/移除中斷點: Cmd + \

3. 打開 API 文件: Option + 左鍵雙擊

4. 跳到函式定義處: Cmd + 左鍵雙擊

5. Indent / Un-Indent:Cmd + ] / Cmd + [

6. 放大縮小 Editor 視窗:Shift + Cmd + E

7. 跳轉 .h 和 .m 檔: Cmd + Option + ↑

8. 刪除一行:Ctrl + D

9. 自定義快速鍵:Xcode -> Preferences -> Key Bindings (終極秘技)

參考網頁:
Xcode shortcut list at Labs
http://labs.grupow.com/index.php/2010/12/xcode-shortcut-list/

XCode 常用快捷键 | Elton's Blog
http://blog.prosight.me/index.php/2009/06/146



......

9/26/2010

[C++] 利用函數指標(funtion pointer)實現委派機制(delegate)

這篇文章記錄一下函數指標如何使用 以及 在委派機制上的實現方式。

所謂函數指標,顧名思義就是一個指向函數的指標,看起來他的用途似乎像是要把函數拿來傳遞用,也就是說可以把一個函數指標當做參數丟進另一個函數。

如下程式碼所示,有一個輸出 bool,輸入 int 的函式叫做 action,如果我們希望將他傳遞到 test 當中,然後再由 test 決定該 action 的輸入參數,這時候函數指標就派上用場了!

#include <iostream>
using namespace std;

bool action(int i) {
    cout << "action: " << i << endl;
    return 0;
}

// define pFun is a function pointer of (out: bool in: int)
typedef bool (*pFun)(int); 

void test(pFun p1, int i) {
    (*p1)(i);
}

int main() { 
    test(&action, 1);
    return 0;
}

除此之外由於 C++ 沒有所謂的委派,而函數指標另外一個最大的功用就拿來實現委派。當有兩個物件彼此之間需要溝通 (例:A 物件的某個 A-1 方法需要觀察 B 物件的 B-1 欄位來決定其行為),這時候就可以幫 B 物件建立一個委派,這個委派不斷的觀察 B 物件的狀態,然後告訴 A 物件要做怎樣的行動。

如下程式碼,有一個 Subject 物件和 Observer 物件,當 Subject 物件當中的 somethingHappen 方法啟動的時候 (可以把它想做一個事件),就會引發 Observer 物件當中的 action 函式!

#include <iostream>
using namespace std;

class Observer {
public:
 
 bool action(int p);
 
};

bool Observer::action(int p) {
 
 std::cout << "print by member function pointer" << std::endl;
 
 return true;
}

class Subject {
 
public:
 
 bool (Observer::*memberFunPtr)(int); // member function pointer
 Observer* m_observer;
 
 void attach (Observer* observer);
 void somethingHappen();
 
 
};

void Subject::attach (Observer* observer) {
 m_observer = observer;
 memberFunPtr = &Observer::action;
}

void Subject::somethingHappen() {
 ((m_observer)->*(memberFunPtr))(1);
}


int main() 
{ 
 Subject mySubject;
 Observer myObserver;
 
 mySubject.attach(&myObserver);
 
 mySubject.somethingHappen();
 
    return 0; 
} 

截至目前為止我對於 Delegate 的理解大概是,他不是什麼特別的語法,只是一種概念。沒有這種機制的話,兩個物件彼此間要溝通,或是互相修改彼此的成員變數,會利用 composition 的方式實現 (也就是在A物件當中包含有B物件,B物件當中包含有A物件)。

透過函數指標,可以降低物件之間的耦合程度,例:原本你想用 B 物件的 B-1 方法修改 A 物件的 private 成員 A-1,那麼勢必要將 A 物件傳入 B-1 方法,甚至還要將 A-1 成員改成 public 才有辦法讓 B-1 存取;如果使用委派,則可以設定一個 A-2 方法取代 B-1 方法,並將 A-2 方法以函數指標傳進 B-1 方法當中 (連帶要將 A 物件的參考傳進去),需要的時候在由 B-1 引發 A-2 函數,也不需要修改 A-1 成員的屬性。

其實我也還不是很懂,或許未來實作上有遇到在來補充一下好了!參考資料有許多精闢的使用範例,搭配泛型甚至可以做到和 C# 當中 Delegate 一樣的效果!

參考資料:
- 函式指標的用途
- What are the advantages of delegates?
- Member Function Pointers and the Fastest Possible C++ Delegates
- Generic Observer Pattern and Events in C++
- Passing member function pointer to member object in c++

......

9/25/2010

[emacs] Emacs 新手第一章

Mac Terminal 下也有安裝 emacs,只要下 emacs 指令就可以了,但是如果需要比較視覺化的介面可以安裝 Carbon Emacs Package

切換 buffers : C-x + 左右鍵
開新檔案:C-x C-f
關閉檔案, 關閉buffers:C-x k (先按 Ctrl+x,然後再按 k)
儲存檔案:C-x C-s
插入檔案:C-x i
另存新檔:C-x C-w
打開檔案並取代當前buffer:C-x C-v
離開 Emacs:C-x C-c

查詢某指令:C-h k 然後輸入你要查詢的指令 ( 例:C-x i )

如果你想要 disable Emacs 的自動備份存檔功能,那麼可以打開 .emacs 檔案 ( C-x C-f ~/.emacs ) 然後加入以下指令:

;disable backup
(setq backup-inhibited t)
;disable auto save
(setq auto-save-default nil)

9/24/2010

iPhone App 的基本流程

在開始寫 iPhone App 之前,對於 iPhone 程式整個的流程應該要有一個大致上的了解,不像之前在寫 C++ 程式一樣,iPhone 採用了 MVC, delegate 等等設計模式,那些新增專案自動產生出來的程式碼,似乎不是那麼容易讓人理解,於是我潛心鑽研了兩小時,在此記錄一下。



上面這張圖是擷取自 iOS Application Programming Guide,大概可以說明整個 App 是如何開始運作的。可以開一個 View-based Application 來對照看看上面這張圖是怎麼回事。

說到程式的進入點,第一個想到的就是 main function,當然 iPhone 程式也不例外,可以在 Group &Files 當中的 Other Sources 找到 main.m 這個檔案,當中定義了 main 函式如下:

int main(int argc, char *argv[]) {
   
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}


首先他建立了一個記憶體管理的 pool 這個先不用管他,重點是第二行 UIApplicationMain,是的,這個函式我將他理解成 UIApplication 的建立,也就是整個 iPhone App 開天闢地的天下第一 funciton。接著,由上方的流程圖可以看到,下一部就是連結 Application Delegate

但是,其實這個動作,當我們使用專案範本建立新專案的時候,Xcode 就幫我們做好了,假設你建立了一個名為 VB 的 View-based Application,那麼你可以看到兩個 delegate 檔案已經自動被建立好了 (VBAppDelegate.h 和 VBAppDelegate.m)。

打開 VBAppDelegate.h 來瞧瞧可以看到他的 interface 定義了兩個成員 (如下),一個是 UIWindow 另一個則是 VBViewController。可以看到 VBAppDelegate 其實是 conform 了 <UIApplicationDelegate> protocol,也就是說他是一個 custom object,請參照上圖,也就是說設計者可以新增一些任務在當中。

@interface VBAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    VBViewController *viewController;
}


既然他叫做 AppDelegate,也就是說當 UIApplication 開始之後,會交代一些事情給 AppDelegate 去做,而 AppDelegate 所要做的最基本的事情就是建立一個 window 然後把一個空的 view 放進去 (就 View-based Application 而言)。

那麼,UIApplication 是怎麼告訴 UIApplication Delegate 要做事了呢,打開 Group & Files 當中 Resources 裡面的 MainWindow.xib 變可一窺究竟。.xib 稱之為 nib 檔,是用來描述 Application 的外表與其控制物件關係的檔案 (是以視覺化的方式呈現),總之,雙擊 MainWindow.xib 來瞧瞧,如下:



選取 File's Owner 可以看到,原來這個 nib 檔案的擁有者是 UIApplication,然後觀察他的連結部分,可以看到 UIApplication 有一個 Outlet 叫做 delegate,而且已經被 link 到 VBAppDelegate,是的,一切都是範本幫你建好好的,有了這個連結 UIApplication 就會在某個適當的時機將任務交派給 Application Delegate 去做。

接著看看 VBAppDelegate 做了哪些事,點選 VB App Delegate 看看他的連結狀況 (如下圖),可以看到它的兩個 Outlet 成員 viewController 和 window 都已經被範本幫你建立好的 View Controller 和 Window 元件給連上了。




稍微回顧整理一下,UIApplication 出現,然後叫 Application Delegate 做事,然後它建立了一個 window 和一個 view controller 並且把這個 view 丟到 window 當中,觀察 VBAppDelegate 的 application:didFinishLaunchingWithOptions: 函式可以得知!

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {   
   
    // Override point for customization after application launch.

    // Add the view controller's view to the window and display.
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];

    return YES;
}


接著我們看看 View Controller 的連結狀況 (如下圖),咦,怎麼只有看到他被連結到 Application Delegate,它本身的 view Outlet 沒有被連結,空空如也呢?




Oh, 原來他是透過 NIB Name 將這個 View Controller 物件以另一個 nib 檔案來初始化,如下圖:




那麼,只要打開 VBViewController.xib 一切就水落石出啦,的確在這個 nib 檔案當中,View Controller 的成員 view (也是 outlet) 的確連結到一個空的 View 物件,真相大白!




以上大致就是透過專案範本所產生出來的現成程式碼所在做的事情,當然 App 在運行的途中可能會遭遇到許多的狀況,一個完整的運行流程在 這篇 文章中有詳細的圖解!引用該圖如下:





參考資料:Demystifying iPhone App Startup, Demystifying View Controllers and Views

......






9/23/2010

在 iPhone 程式開發當中使用 OpenCV


本篇記錄如何使用 Xcode 開發 iPhone App 的時候可以使用 OpenCV 進行影像處理。

首先,要先下載網路上熱心的人已經製作好的 static library: http://niw.at/articles/2009/03/14/using-opencv-on-iphone/en

下載回來解壓縮之後可以看到兩個資料夾,opencv_device 和 opencv_simulator 這當中有需要用到的 header 和 lib 檔案。

接著,在建立專案的時候,編輯 Group & Files 當中打開 Target 項目。雙擊其子項目則會跳出 Target "專案名稱" Info 的視窗,找到其中的 Search Paths 欄位,其中有 Header Search Paths 和 Library Search Paths 兩個項目需要設定。

由於針對 device 和 simulator 有不一樣的 lib 對應,所以分成兩組,因此選取 Header Search Paths 之後點擊左下角的功能選單,選擇 Add Build Setting Confition,然後分成兩組 Any iOS device 和 Any iOS simulator,針對這兩組在添加對應的 header 和 lib 路徑。( 附註. ${SOURCE_ROOT} 是專案資料夾路徑的縮寫, 並注意路徑當中最好不要有空格,否則會出現找不到 <opencv/cv.h> 的錯誤)




接著,要在 Linking 的欄位當中找到 Other Linker Flags 並且加入 -lstdc++-lz 兩個項目。

最後,滑鼠選取專案,然後按右鍵 Add -> Add Group 然後新增一個名為 Library 的 Group,然後選取它,到主選單裡面的 Project -> Add to Project,會跳出一個選取視窗,這時候就找到 device 資料夾底下 lib 資料夾的 libcv.a, libcxcore.a, libopencv_lapack.a 三個檔案並且加入,就大功告成了!

如果是要在 MacOSX 下建立 OpenCV 環境的話可以參考:http://opencv.willowgarage.com/wiki/Mac_OS_X_OpenCV_Port。基本上使用 MacPorts 蠻簡單的!

參考資料:How to add multiple static library files with different architectures to Xcode

......




9/16/2010

[C++] 成員函式對私有成員的存取權限是 type-wise 不是 object-wise

如下 main 所示,宣告兩個 OneNumber 物件 aa 和 bb,並呼叫 aa 當中的 sumWithAnother 方法。可以看到 sumWithAnother 這個成員函式對於 bb 這個非 aa 物件,是有權力存取該私有成員變數 _num 的!

#include <iostream>

using namespace std;

 

class OneNumber

{

private:

  int _num;

 

public:

  OneNumber(int x); 

  int sumWithAnother(OneNumber n);

};

 

int main()

{

  OneNumber aa(2);

  OneNumber bb(8);

  int sum = aa.sumWithAnother(bb);

  cout << "sum: " << sum << endl;

 

}

 

OneNumber::OneNumber(int x) : _num(x) {}

int OneNumber::sumWithAnother(OneNumber n)

{

  return (_num + n._num);

}



...

9/15/2010

[C++] Pass vector which is not allowed to change to a funciton by constant reference

如下程式碼所述,使用 const 是一個好的習慣,在可讀性上可以明確的表示某變數不為更動,針對成員函式宣告 const 也可以表明該成員函式不會更動到所屬物件的資料成員。

那麼當要傳遞 vector 類型的資料到函數當中要怎麼利用 const 呢,這邊要注意的事如果你在函式當中使用 iterator 操作了該 vector 的話,就會造成 cannot convert from 'std::_Vector_const_iterator<_Ty,_Alloc>' to 'std::_Vector_iterator<_Ty,_Alloc>' 類似的錯誤。

原因就是容器當中若你要使用不更動容器內容的 iterator,應該要改用 const_iterator,所以只要把 iterator 改成 const_iterator 就可以利用 const & by reference 的方式把 vector 傳進函式當中並且確保他不會被更動!


#include <vector>
using namespace std;

int sumOfVector(const vector<int> &vec)
{
  int sum = 0;

  //typedef vector<int>::iterator intVect; // Error
  typedef vector<int>::const_iterator intVect; // Good!

  intVect itBegin = vec.begin();
  intVect itEnd   = vec.end(); 

  for (intVect it = itBegin; it != itEnd; it++)
    sum += *it;

  return sum;

}

int main()
{
  vector<int> vec(10, 1);
  int sum = sumOfVector(vec);

  return 1;

}

...

9/14/2010

[Tool] Visual C++ 記憶體洩漏偵測工具

Visual Leak Detector - Enhanced Memory Leak Detection for Visual C++

使用方式:

1. 下載 Visual Leak Detector
2. Copy the VLD library (*.lib) files to your Visual C++ installation's "lib" subdirectory.
3. Copy the VLD header files (vld.h and vldapi.h) to your Visual C++ installation's "include" subdirectory.
4. In the source file containing your program's main entry point, include the vld.h header file. It's best, but not absolutely required, to include this header before any other header files, except for stdafx.h. If the source file, include stdafx.h, then vld.h should be included after it.
5. If you are running Windows 2000 or earlier, then you will need to copy dbghelp.dll to the directory where the executable being debugged resides.
6. Build the debug version of your project.

完成之後,在 Debug 模式下執行程式,程式執行完畢後,Output 視窗會顯示是否偵測到 Memory Leak 的訊息,以及發生的位置。雙擊可以跳到該處。

......

9/06/2010

[轉] 程式設計語錄

一個好的程式設計師,就是那種就算只是要穿越單行道,也會看看兩邊再走的人。
A good programmer is someone who looks both ways before crossing a one-way street.
-- Doug Linder, 系統管理員


任何工具最重要、但也是最難以最到的一步,就是他對於使用者習慣的影響。
如果這個工具是程式語言,那不管我們是否願意,他都會影響我們的思考習慣。
A most important, but also most elusive, aspect of any tool is its influence on the habits of those who train themselves in its use. If the tool is a programming language this influence is, whether we like it or not, an influence on our thinking habits.
-- Edsger Dijkstra, 計算機科學家


抽象化和和模糊化是完全不同的…抽象的目的不是去模糊它,而是建立一種新的、能夠絕對精確的語意層。
Being abstract is something profoundly different from being vague... The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise.
-- Edsger Dijkstra


除了數學外,精通母語也是一個勝任程式設計師的重要資產。
Besides a mathematical inclination, an exceptionally good mastery of one's native tongue is the most vital asset of a competent programmer.
-- Edsger Dijkstra


C 讓你很容易搬石頭砸自己的腳;C++ 雖然比較不容易做到,但是如果做到的話,你可能會打爛整隻腳。
C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, it blows away your whole leg.
-- Bjarne Stroustrup, C++ 程式語言開發者

大部分的錯誤都可以簡單地修正,困難在於如何找到問題所在。
Commentary: most debugging problems are fixed easily; identifying the location of the problem is hard.
-- 不知道是誰


考慮到目前我們電腦程式的悲慘狀況,軟體開發顯然還是門魔法,還不能稱他為一個工程學科。
Considering the current sad state of our computer programs, software development is clearly still a black art, and cannot yet be called an engineering discipline.
-- Bill Clinton, 美國前總統


有很長一段時間,我一直很困擾,為什麼有的東西如此地昂貴、如此前瞻、卻也如此沒用。
同時我也發現,電腦是一個很笨的機器,但是他卻可以做到難以置信的聰明事;而程式設計師是聰明的人,卻始終做些難以置信的蠢事。簡而言之,他們是絕配。
For a long time it puzzled me how something so expensive, so leading edge, could be so useless, and then it occurred to me that a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are, in short, a perfect match.
-- Bill Bryson, author, from Notes from a Big Country


只要有足夠的眼睛,就可以讓所有的 Bug 浮現(也就是,有足夠多的測試者和開發者,幾乎所有的問題都可以很快地發現、並且被修正)
Given enough eyeballs, all bugs are shallow (e.g., given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone).
-- Eric S. Raymond, programmer and advocate of open source software, from The Cathedral and the Bazaar


良好的程式碼是自己最好的文件。
當你打算加上註解的時候,先問問自己:「我要如何改善才能讓這段程式碼不需要註解?」
改善自己的程式碼、然後再註解,這樣可以讓他更清楚。
Good code is its own best documentation. As you're about to add a comment, ask yourself, 'How can I improve the code so that this comment isn't needed?' Improve the code and then document it to make it even clearer.
-- Steve McConnell, software engineer and author, from 《Code Complete》


嘿!編譯過了!可以出貨了!
Hey! It compiles! Ship it!
-- 不知道是誰


在每個寫得很好的大程式裡,都是寫得很好的小程式。
Inside every well-written large program is a well-written small program.
-- Charles Antony Richard Hoare, 電腦科學家


沒有一個經過倫理訓練的軟體工程師會同意去寫一個「摧毀巴格達」的程序。基本的專業倫理會要求他寫一個「摧毀城市」的程序,然後再把「巴格達」當參數傳進去。
It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. Basic professional ethics would instead require him to write a DestroyCity procedure, to which Baghdad could be given as a parameter.
-- Nathaniel S. Borenstein, 電腦科學家


管理程式設計師就像放養貓一樣。
Managing programmers is like herding cats.
-- 不知道是誰


用程式碼行數來衡量進度,就像用重量來評估飛機的建造進度一樣。
Measuring programming progress by lines of code is like measuring aircraft building progress by weight.
-- Bill Gates, 微軟共同創辦人


用那些被批評成很差的語言所寫的好程式,遠比用那些宣稱美妙的語言來的多。
More good code has been written in languages denounced as bad than in languages proclaimed wonderful -- much more.
-- Bjarne Stroustrup, 《The Design and Evolution of C++》


程式必須是要寫給人看得,讓電腦能執行只是附加功能。
Programs must be written for people to read, and only incidentally for machines to execute.
-- Harold Abelson 和 Gerald Jay Sussman,
電腦科學家、《The Structure and Interpretation of Computer Programs》作者


真正的程式設計師不為他們的程式碼註解;如果程式碼很難寫,那他理所當然也應該很難懂。
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
-- 不知道是誰


簡單是可靠性的前提。
Simplicity is prerequisite for reliability.
-- Edsger Dijkstra


C 程式語言--這是一種結合了組合語言的靈活性和組合語言的強大能力的語言。
The C programming language -- a language which combines the flexibility of assembly language with the power of assembly language.
-- 不知道是誰


前 90% 的程式碼用了 90% 的開發時間,但是剩下的 10% 還需要另外 90% 的開發時間。
The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time.
-- Tom Cargill, 貝爾實驗室的物件導向程式專家


很重要的一點:加入一個功能的成本不是只有為了這項功能寫程式所花的時間,還要包含他對以後擴展造成阻礙。當然,只要有足夠的時間,所有要求的功能是都能被實作出來的;但是隨著這些功能出現的,通常會是一個脆弱的程式庫,脆弱到就算只是一個應該是極度簡單的新想法,都需要越來越長的時間和已經存在的網路糾纏。處理的秘訣就是只挑選那些不會和其他功能打架的功能。
The important point is that the cost of adding a feature isn't just the time it takes to code it. The cost also includes the addition of an obstacle to future expansion. Sure, any given feature list can be implemented, given enough coding time. But in addition to coming out late, you will usually wind up with a codebase that is so fragile that new ideas that should be dead-simple wind up taking longer and longer to work into the tangled existing web. The trick is to pick the features that don't fight each other.
-- John Carmack, 電腦遊戲程式設計師


效能的關鍵在於簡潔,而不是大量的特例。除非成效非常顯著,不然應該忍住去微調它的衝動。
The key to performance is elegance, not battalions of special cases. The terrible temptation to tweak should be resisted unless the payoff is really noticeable.
-- Jon Bently 和 M. Douglas McIlroy, 貝爾實驗室的電腦科學家


最後一個用 C 寫出來的好作品,是法蘭茲‧舒伯特的第九號交響曲。(C 大調…)
The last good thing written in C was Franz Schubert's Symphony Number 9.
-- Erwin Dieterich, 程式設計師


使用 C++ 的問題在於他的語言中有很強烈的傾向,你必須要在你做任何事之前知道所有事情。
The problem with using C++ ... is that there's already a strong tendency in the language to require you to know everything before you can do anything.
-- Larry Wall, Perl 語言的開發者


越早開始寫程式,程式寫得越久。
The sooner you start to code, the longer the program will take.
-- Roy Carlson, University of Wisconsin


原型的價值在於它對你的教育,而不是程式碼本身。
The value of a prototype is in the education it gives you, not in the code itself.
-- Alan Cooper, 《The Inmates are Running the Asylum》作者


世界上只有兩種程式語言:總是被人罵的、根本沒人用的。
There are only two kinds of programming languages: those people always bitch about and those nobody uses.
-- Bjarne Stroustrup


有兩種設計軟體的方法:一種是讓它簡單到明顯地沒有缺陷,另一種則是讓它複雜到沒有明顯的缺陷。
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.
-- Charles Antony Richard Hoare


醜陋的程式就和醜陋的吊橋一樣:他們都比漂亮的容易崩潰;因為人們(尤其是工程人)的審美方法,是和我們處理、理解複雜事物的能力密切相關的。一個讓人難以寫出優雅程式的語言碼,同時也代表他難以寫出好的程式碼。
Ugly programs are like ugly suspension bridges: they're much more liable to collapse than pretty ones, because the way humans (especially engineer-humans) perceive beauty is intimately related to our ability to process and understand complexity. A language that makes it hard to write elegant code makes it hard to write good code.
-- Eric S. Raymond


花幾個禮拜的時間寫程式,就可以省下你幾個小時的計畫時間。
Weeks of programming can save you hours of planning.
-- 不知道是誰


如果創造了一種程式語言,可以讓程式設計師用簡單的英文來寫的話,那你會發現程式設計師不會講英文。
When a programming language is created that allows programmers to program in simple English, it will be discovered that programmers cannot speak English.

出處:http://heresy.spaces.live.com/blog/cns!E0070FB8ECF9015F!11414.entry

......