1 objective-c和c++混合編程
1.1 OC調用C++類的方法
????????在 Objective-C++中,可以用C++代碼調用方法也可以從Objective-C調用方法。在這兩種語言里對象都是指針,可以在任何地方使用。例 如,C++類可以使用Objective-C對象的指針作為數據成員,Objective-C類也可以有C++對象指針做實例變量。下例說明了這一點。
????????注意:Xcode需要源文件以".mm"為擴展名,這樣才能啟動編譯器的Objective-C++擴展。
1.1.1 簡單示例
/* Hello.mm
* Compilewith: g++ -x objective-c++ -framework Foundation Hello.mm? -o hello
*/
#import <Foundation/Foundation.h>
class Hello {
????private:?
????????id greeting_text;? // holds an NSString
????public:????
????????Hello() {
????????????greeting_text = @"Hello, world!";
????????}
????????Hello(const char* initial_greeting_text) {
????????????greeting_text = [[NSString alloc] initWithUTF8String:initial_greeting_text];
????????}
????????void say_hello() {
????????????printf("%s\n", [greeting_text UTF8String]);
????????}
};
@interface Greeting: NSObject {
????@private
????????Hello *hello;
}
- (id) init;
- (void) dealloc;
- (void) sayGreeting;
- (void) sayGreeting:(Hello*)greeting;
@end
@implementationGreeting
- (id) init {
? ? if (self = [super init]) {
????????hello = new Hello();
????}
????return self;
}
- (void) dealloc {
????delete hello;
????[super dealloc];
}
- (void) sayGreeting {
????hello->say_hello();
}
- (void) sayGreeting:(Hello*)greeting {
????greeting->say_hello();
}
@end
int main() {
????NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
????Greeting *greeting = [[Greeting alloc] init];
????[greeting sayGreeting];? // > Hello,? world!
????Hello *hello = new
????Hello("Bonjour, monde!");
????[greeting sayGreeting: hello];// > Bonjour,? monde!
????delete hello;
????[greeting release];
????[pool release];
????return 0;
}
????????正如你可以在OC接口中聲明C結構一樣,你也可以在OC接口中聲明C++類。跟C結構一樣,OC接口中定義的C++類是全局范圍的,不是OC類的內嵌類(這與標準C(盡管不是C++)提升嵌套結構定義為文件范圍是一致的)。
????????為了允許你基于語言變種條件化地編寫代碼,OC++編譯器定義了__cplusplus和__OBJC__預處理器常量,分別指定C++和OC。??? 如前所述,OC++不允許C++類繼承自OC對象,也不允許OC類繼承自C++對象。
class Base {/* ... */ };
@interfaceObjCClass: Base ... @end // ERROR!
class Derived:public ObjCClass ... // ERROR!
????????與 OC不同的是,C++對象是靜態(tài)類型的,有運行時多態(tài)是特殊情況。兩種語言的對象模型因此不能直接兼容。更根本的,OC和C++對象在內存中的布局是互不 相容的,也就是說,一般不可能創(chuàng)建一個對象實例從兩種語言的角度來看都是有效的。因此,兩種類型層次結構不能被混合。
????????你可以在OC類內部聲明C++類,編譯器把這些類當作已聲明在全局名稱空間來對待。就像下面:
@interface Foo{
????class Bar { ... } // OK
}
@end
Bar *barPtr;// OK
????????OC允許C結構作為實例變量,不管它是否聲明在OC聲明內部。
@interface Foo{
????struct CStruct { ... };
????struct CStruct bigIvar; // OK
} ...
@end
????????Mac OS X 10.4以后,如果你設置fobjc- call-cxx-cdtors編譯器標志,你就可以使用包含虛函數和有意義的用戶自定義零參數構造函數、析構函數的C++類實例來做為實例變量 (gcc-4.2默認設置編譯器標志fobjc-call-cpp-cdtors)。OC成員變量alloc完以后,alloc函數會按聲明順序調用構造器。構造器使用公共無參數恰當的構造函數。OC成員變量dealloc之前,dealloc方法按聲明順序反序調用析構函數。OC沒有名稱空間得概念,不能在C++名稱空間內部聲明OC類,也不能在OC類里聲明名稱空間。OC類、協(xié)議、分類不能聲明在C++ template里,C++ template也不能聲明在OC接口、協(xié)議、分類的范圍內。
????????但是,OC類可以做C++ template的參數,C++ template參數也可以做OC消息表達式的接收者或參數(不能通過selector)。
1.1.2 OC++類的頭文件定義
????????你可能會想使用等價的Objective-C類型和函數將C++代碼封裝(wrap)起來。比方說,你有一個名為CppObject的C++類(CppObject.h):
#include <string>
class?CppObject
{
??? ??public:
??????????void?ExampleMethod(conststd::string&?str);
??????????//?constructor,?destructor,?other?members,?etc.
};
????????在Objectiv-C類允許定義C++類的成員變量,所以可以首先嘗試定義一個ObjcObject封裝類(ObjcObject.h):
#import?
#import?"CppObject.h"
@interface?ObjcObject?:?NSObject?{
????CppObject?wrapped;
}
-?(void) exampleMethodWithString:(NSString*)str;
//?other?wrapped?methods?and?properties
@end
????????然后在ObjcObject.mm中實現這些方法。不過,此時會在兩個頭文件(ObjcObject.h&CppObject.h)中得到一個預處理和編譯錯誤。問題出在#include和#import上。對于預處理器而言,它只做文本的替換操作。所以#include和#import本質上就是遞歸地復制和粘貼引用文件的內容。這個例子中,使用#import "ObjcObject.h"等價于插入如下代碼:
//?[首先是大量Foundation/Foundation.h中的代碼]
//?[無法包含],因為它僅存在于C++模式的include?path中
class?CppObject
{
? ??public:
??????????void?ExampleMethod(conststd::string&?str);
??????????//?constructor,?destructor,?other?members,?etc.
};
@interface?ObjcObject?:?NSObject?{
????CppObject?wrapped;
}
-?(void)exampleMethodWithString:(NSString*)str;
//?other?wrapped?methods?and?properties
@end
????????因為class CppObject根本不是有效的Objective-C語法,所以編譯器就被搞糊涂了。 錯誤通常是這樣的:
Unknown type name'class'; did you mean 'Class'?
????????正是因為Objective-C中沒有class這個關鍵字.所以要與Objective-C兼容,Objective-C++類的頭文件必須僅包含Objective-C代碼,絕對沒有C++的代碼-這主要是影響類型定義(就像例中的CppObject類)。
1.1.3 簡潔頭文件——使用ivars
????????之前的文章已經提到一些解決方案.其中最好的一個是PIMPL,它也適用于現在的情況。這里還有一個適用于clang的新方法,可以將C++代碼從Objective-C中隔開,這就是class extensions中ivars的。
????????Class extensions(不要同categories弄混)已經存在一段時間了:它們允許你在class的接口外的擴展部分定義在@implementation段前,而不是在公共頭文件中。這個例子就可以聲明在ObjcObject.mm中:
#import "ObjcObject.h"
@interface ObjcObject() // note the empty parentheses
- (void)methodWeDontWantInTheHeaderFile;
@end
@implementation ObjcObject
//etc.
????????GCC也支持這個操作。不過clang還支持添加ivar塊,也就是你還可以聲明C++類型的實例變量,既可以在classextension中,也可以在@implementation開始的位置。本例中的ObjcObject.h可以被精簡為:
#import <Foundation/Foundation.h>
@interface ObjcObject : NSObject
- (void)exampleMethodWithString:(NSString*)str;
//other?wrapped?methods?and?properties
@end
????????去掉的部分都移到實現文件的class extension中(ObjcObject.mm):
#import "ObjcObject.h"
#import "CppObject.h"
@interface ObjcObject(){
? CppObject wrapped;
}
@end
@implementation ObjcObject
- (void)exampleMethodWithString:(NSString*)str
{
//NOTE: str為nil會建立一個空字串,而不是引用一個指向UTF8String空指針.
??? std::string cpp_str([str UTF8String],[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
??? wrapped.ExampleMethod(cpp_str);
}
????????如果我們不需要interface extension來聲明額外的屬性和方法,ivar塊仍然可以放在@implementation開始位置:
#import "ObjcObject.h"
#import "CppObject.h"
@implementation ObjcObject{
??? CppObject wrapped;
}
- (void)exampleMethodWithString:(NSString*)str
{
//NOTE: str為nil會建立一個空字串,而不是引用一個指向UTF8String空指針.
??? std::string cpp_str([str UTF8String],[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
??? wrapped.ExampleMethod(cpp_str);
}
????????定義的CppObject實例wrapped在ObjcObject創(chuàng)建時,CppObject的缺省建構函數會被調用,而在ObjcObject被調用dealloc析構時,ObjcObject的析構函數也會被調用。如果ObjcObject沒有提供缺省的建構函數,編譯就會失敗。
1.1.4 管理被封裝C++對象的生命周期
????????解決方案是透過new關鍵字掌握建構過程,比如:
@interface ObjcObject(){
??? CppObject *wrapped;//指針!會在alloc時初始為NULL.
}
@end
@implementation ObjcObject
- (id)initWithSize:(int)size
{
??????? self = [super init];
? ??? ??if(self)
??? ????{
??????? ????wrapped = new CppObject(size);
??????? ????if(!wrapped) self = nil;
??? ????}
??? ????return?self;
}
//...
????????如果是使用C++異常,也可以使用try {...} catch {...}把創(chuàng)建過程封裝起來.相應地,還要顯式地釋放封閉對象:
- (void)dealloc
{
??? deletewrapped;
??? [super dealloc];//如果使用了ARC,這句就要略去
}
????????作者接著提到了另一個方法,顯示分配一塊內存,然后在它的基礎上調用new來創(chuàng)建對象。首先聲明char wrapped_mem[sizeof(CppObject)];再使用wrapped = new(wrapped_mem) CppObject();創(chuàng)建了實例wrapped。釋放時if (wrapped) wrapped->~CppObject();這樣雖然可行,但不建議使用。
1.1.5 總結
????????一定要確保封裝的方法僅返回和使用C或Objective-C類型的返回值及參數。同時不要忘記C++中不存在nil,而NUL是不可用于解引用的。
1.2 在C++代碼中使用Objective-C類
1.2.1 簡單示例
????????這個問題同樣存在于頭文件中。你不能因為引入Objective-C類型而污染了C++頭文件,或無法被純C++代碼所引用。比方說,我們想封裝的Objective-C類ABCWidget,在ABCWidget.h聲明為:
#import <Foundation/Foundation.h>
@interface ABCWidget
- (void)init;
- (void)reticulate;
//etc.
@end
????????這樣的類定義在Objective-C++中是沒有問題的,但在純C++的代碼是不允許的:
#import "ABCWidget.h"
namespace?abc
{
??? class?Widget
??? {
??????? ????ABCWidget *wrapped;
??? ????public:
??????? ????Widget();
??????? ????~Widget();
??? ????????void?Reticulate();
??? };
}
????????一個純粹的C++編譯器在Foundation.h中的代碼和ABCWidget聲明位置出錯。
1.2.2 永恒的PIMPL
????????有沒有這樣的東西作為一類擴展C++,這樣的把戲將無法正常工作。 另一方面,PIMPL,工作得很好,實際上是比較常用的純C++了。在我們的例子中,我們減少到最低限度:C++類
????????C++并沒有之前提到的class extension,但是卻有另一種較為常用的方式:PIMPL(Private Implementation,私有實現)。這里,將C++class的定義精簡為:
namespace?abc
{
??? struct WidgetImpl;
??? class?Widget
??? {
??????? WidgetImpl *impl;
??? ????public:????
??????? ????Widget();
??????? ????~Widget();
??????? ????void?Reticulate();
??? };
}
????????然后在Widget.mm中:
#include "Widget.hpp"
#import "ABCWidget.h"
namespace?abc
{
??? struct?WidgetImpl
??? {
??????? ABCWidget *wrapped;
??? };
??? Widget::Widget():impl(newWidgetImpl)
??? {
??????? impl->wrapped = [[ABCWidgetalloc] init];
??? }
??? Widget::~Widget()
??? {
??????? if(impl)
??????????? [impl->wrapped release];
??????? delete?impl;
??? }
??? void?Widget::Reticulate()
??? {
??????? [impl->wrapped reticulate];
??? }
}
????????它的工作原理是——前置聲明。聲明這樣的結構或類對象的指針成員變量、結構或類就足夠了。需要注意的是封裝的對象會在析構函數中釋放。即便對于使用了ARC的項目,我還是建議你對這樣的C++/Objective-C重引用的文件屏蔽掉它。不要讓C++代碼依賴于ARC。在XCode中可以針對個別文件屏蔽掉ARC。Target properties->Build phase頁簽,展開'CompileSources',為特定文件添加編譯選項-fno-objc-arc。
1.2.3 C++中封裝Objective-C類的捷徑
????????您可能已經注意到,PIMPL解決方案使用兩個級別的間接引用。如果包裝的目標類像本例中的一樣簡單,就可能會增大了復雜性。雖然Objective-C的類型一般不能使用在純C++中,不過有一些在C中實際已經定義了。id類型就是其中之一,它的聲明在頭文件中。雖然會失去一些Objective-C的安全性,你還是可以把你的對象直接傳到C++類中:
#include <objc/objc-runtime.h>
namespace?abc
{
??? class?Widget
??? {
??????? id/*ABCWidget* */wrapped;
??????? public:
??????????? Widget();
??????????? ~Widget();
??????????? void?Reticulate();
??? };
}
????????不建議向id對象直接發(fā)送消息。這樣你會失去很多編譯器的檢查機制,特別是對于不同類中有著相同selector名字的不同方法時。所以:
#include "Widget.hpp"
#import "ABCWidget.h"
namespace?abc
{
??? Widget::Widget() : wrapped([[ABCWidgetalloc] init])
??? {
??? }
??? Widget::~Widget()
??? {
??????? [(ABCWidget*)impl release];
??? }
??? void?Widget::Reticulate()
??? {
??????? [(ABCWidget*)impl reticulate];
??? }
}
????????像這樣的類型轉換很容易在代碼中隱藏錯誤,再嘗試一個更好的方式。在頭文件中:
#ifdef __OBJC__
@class?ABCWidget;
#else
typedef struct?objc_object ABCWidget;
#endif
namespace?abc
{
??? class?Widget
??? {
??????? ABCWidget *wrapped;
??????? public:
??????????? Widget();
??????????? ~Widget();
??????????? void?Reticulate();
??? };
}
????????如果這個頭文件被一個mm文件引用,編譯器可以充分識別到正確的類。
????????如果是在純C++模式中引用,ABCWidget*是一個等價的id類型:定義為typedef struct objc_object* id;。
????????#ifdef塊還可以被進一步放到一個可重用的宏中:
#ifdef __OBJC__
#define OBJC_CLASS(name) @class
name
#else
#define OBJC_CLASS(name) typedef
struct objc_object name
#endif
????????現在,我們可以前置聲明在頭文件中一行就可以適用于所有4種語言:
OBJC_CLASS(ABCWidget);
1.3 C++詞匯歧義和沖突
????????OC頭文件中定義了一些標識符,所有的OC程序必須包含的,這些標識符識id,Class,SEL,IMP和BOOL。
????????OC方法內,編譯器預聲明了標識符self和super,就像C++中的關鍵字this。跟C++的this不同的是,self和super是上下文相關的;OC方法外他們還可以用于普通標識符。
????????協(xié)議內方法的參數列表,有5個上下文相關的關鍵字(oneway,in,out,inout,bycopy)。這些在其他內容中不是關鍵字。
????????從OC程序員的角度來看,C++增加了不少新的關鍵字。你仍然可以使用C++的關鍵字做OC selector的一部分,所以影響并不嚴重,但你不能使用他們命名OC類和實例變量。例如,盡管class是C++的關鍵字,但是你仍然能夠使用NSObject的方法class:
[foo class];// OK
????????然而,因為它是一個關鍵字,你不能用class做變量名稱:
NSObject *class; // Error
????????OC里類名和分類名有單獨的命名空間。@interface foo和@interface(foo)能夠同時存在在一個源代碼中。OC++里,你也能用C++中的類名或結構名來命名你的分類。
????????協(xié)議和template標識符使用語法相同但目的不同:
id<someProtocolName> foo;
TemplateType<SomeTypeName> bar;
????????為了避免這種含糊之處,編譯器不允許把id做template名稱。
????????最后,C++有一個語法歧義,當一個label后面跟了一個表達式表示一個全局名稱時,就像下面:
label:::global_name = 3;
????????第一個冒號后面需要空格。OC++有類似情況,也需要一個空格:
receiver selector: ::global_c++_name;
1.4 限制
????????OC++沒有為OC類增加C++的功能,也沒有為C++類增加OC的功能。例如,你不能用OC語法調用C++對象,也不能為OC對象增加構造函數和析構函數,也不能將this和self互相替換使用。類的體系結構是獨立的。C++類不能繼承OC類,OC類也不能繼承C++類。另外,多語言異常處理是不支持的。也就是說,一個OC拋出的異常不能被C++代碼捕獲,反過來C++代碼拋出的異常不能被OC代碼捕獲。
摘自:http://ocen.iteye.com/blog/522028
2 參考鏈接
混合使用Objective-C,C++和Objective-C++
http://blog.csdn.net/horkychen/article/details/7935910
objective-c和c++混合編程
http://www.cnblogs.com/85538649/archive/2011/09/29/2195332.html