1 objective-c和c++混合編程
1.1 OC調(diào)用C++類的方法
????????在 Objective-C++中,可以用C++代碼調(diào)用方法也可以從Objective-C調(diào)用方法柬唯。在這兩種語(yǔ)言里對(duì)象都是指針靠柑,可以在任何地方使用寨辩。例 如,C++類可以使用Objective-C對(duì)象的指針作為數(shù)據(jù)成員歼冰,Objective-C類也可以有C++對(duì)象指針做實(shí)例變量靡狞。下例說明了這一點(diǎn)。
????????注意:Xcode需要源文件以".mm"為擴(kuò)展名隔嫡,這樣才能啟動(dòng)編譯器的Objective-C++擴(kuò)展甸怕。
1.1.1 簡(jiǎn)單示例
/* 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結(jié)構(gòu)一樣,你也可以在OC接口中聲明C++類畔勤。跟C結(jié)構(gòu)一樣蕾各,OC接口中定義的C++類是全局范圍的,不是OC類的內(nèi)嵌類(這與標(biāo)準(zhǔn)C(盡管不是C++)提升嵌套結(jié)構(gòu)定義為文件范圍是一致的)庆揪。
????????為了允許你基于語(yǔ)言變種條件化地編寫代碼式曲,OC++編譯器定義了__cplusplus和__OBJC__預(yù)處理器常量,分別指定C++和OC。??? 如前所述吝羞,OC++不允許C++類繼承自O(shè)C對(duì)象兰伤,也不允許OC類繼承自C++對(duì)象。
class Base {/* ... */ };
@interfaceObjCClass: Base ... @end // ERROR!
class Derived:public ObjCClass ... // ERROR!
????????與 OC不同的是钧排,C++對(duì)象是靜態(tài)類型的敦腔,有運(yùn)行時(shí)多態(tài)是特殊情況。兩種語(yǔ)言的對(duì)象模型因此不能直接兼容恨溜。更根本的符衔,OC和C++對(duì)象在內(nèi)存中的布局是互不 相容的,也就是說糟袁,一般不可能創(chuàng)建一個(gè)對(duì)象實(shí)例從兩種語(yǔ)言的角度來(lái)看都是有效的判族。因此,兩種類型層次結(jié)構(gòu)不能被混合项戴。
????????你可以在OC類內(nèi)部聲明C++類形帮,編譯器把這些類當(dāng)作已聲明在全局名稱空間來(lái)對(duì)待。就像下面:
@interface Foo{
????class Bar { ... } // OK
}
@end
Bar *barPtr;// OK
????????OC允許C結(jié)構(gòu)作為實(shí)例變量周叮,不管它是否聲明在OC聲明內(nèi)部辩撑。
@interface Foo{
????struct CStruct { ... };
????struct CStruct bigIvar; // OK
} ...
@end
????????Mac OS X 10.4以后,如果你設(shè)置fobjc- call-cxx-cdtors編譯器標(biāo)志仿耽,你就可以使用包含虛函數(shù)和有意義的用戶自定義零參數(shù)構(gòu)造函數(shù)合冀、析構(gòu)函數(shù)的C++類實(shí)例來(lái)做為實(shí)例變量 (gcc-4.2默認(rèn)設(shè)置編譯器標(biāo)志fobjc-call-cpp-cdtors)。OC成員變量alloc完以后氓仲,alloc函數(shù)會(huì)按聲明順序調(diào)用構(gòu)造器水慨。構(gòu)造器使用公共無(wú)參數(shù)恰當(dāng)?shù)臉?gòu)造函數(shù)。OC成員變量dealloc之前敬扛,dealloc方法按聲明順序反序調(diào)用析構(gòu)函數(shù)。OC沒有名稱空間得概念朝抖,不能在C++名稱空間內(nèi)部聲明OC類啥箭,也不能在OC類里聲明名稱空間。OC類治宣、協(xié)議急侥、分類不能聲明在C++ template里,C++ template也不能聲明在OC接口侮邀、協(xié)議坏怪、分類的范圍內(nèi)。
????????但是绊茧,OC類可以做C++ template的參數(shù)铝宵,C++ template參數(shù)也可以做OC消息表達(dá)式的接收者或參數(shù)(不能通過selector)。
1.1.2 OC++類的頭文件定義
????????你可能會(huì)想使用等價(jià)的Objective-C類型和函數(shù)將C++代碼封裝(wrap)起來(lái)。比方說鹏秋,你有一個(gè)名為CppObject的C++類(CppObject.h):
#include <string>
class?CppObject
{
??? ??public:
??????????void?ExampleMethod(conststd::string&?str);
??????????//?constructor,?destructor,?other?members,?etc.
};
????????在Objectiv-C類允許定義C++類的成員變量尊蚁,所以可以首先嘗試定義一個(gè)ObjcObject封裝類(ObjcObject.h):
#import?
#import?"CppObject.h"
@interface?ObjcObject?:?NSObject?{
????CppObject?wrapped;
}
-?(void) exampleMethodWithString:(NSString*)str;
//?other?wrapped?methods?and?properties
@end
????????然后在ObjcObject.mm中實(shí)現(xiàn)這些方法。不過侣夷,此時(shí)會(huì)在兩個(gè)頭文件(ObjcObject.h&CppObject.h)中得到一個(gè)預(yù)處理和編譯錯(cuò)誤横朋。問題出在#include和#import上。對(duì)于預(yù)處理器而言百拓,它只做文本的替換操作琴锭。所以#include和#import本質(zhì)上就是遞歸地復(fù)制和粘貼引用文件的內(nèi)容。這個(gè)例子中衙传,使用#import "ObjcObject.h"等價(jià)于插入如下代碼:
//?[首先是大量Foundation/Foundation.h中的代碼]
//?[無(wú)法包含]祠够,因?yàn)樗鼉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
????????因?yàn)閏lass CppObject根本不是有效的Objective-C語(yǔ)法,所以編譯器就被搞糊涂了。 錯(cuò)誤通常是這樣的:
Unknown type name'class'; did you mean 'Class'?
????????正是因?yàn)镺bjective-C中沒有class這個(gè)關(guān)鍵字.所以要與Objective-C兼容粪牲,Objective-C++類的頭文件必須僅包含Objective-C代碼古瓤,絕對(duì)沒有C++的代碼-這主要是影響類型定義(就像例中的CppObject類)。
1.1.3 簡(jiǎn)潔頭文件——使用ivars
????????之前的文章已經(jīng)提到一些解決方案.其中最好的一個(gè)是PIMPL腺阳,它也適用于現(xiàn)在的情況落君。這里還有一個(gè)適用于clang的新方法,可以將C++代碼從Objective-C中隔開亭引,這就是class extensions中ivars的绎速。
????????Class extensions(不要同categories弄混)已經(jīng)存在一段時(shí)間了:它們?cè)试S你在class的接口外的擴(kuò)展部分定義在@implementation段前,而不是在公共頭文件中焙蚓。這個(gè)例子就可以聲明在ObjcObject.mm中:
#import "ObjcObject.h"
@interface ObjcObject() // note the empty parentheses
- (void)methodWeDontWantInTheHeaderFile;
@end
@implementation ObjcObject
//etc.
????????GCC也支持這個(gè)操作纹冤。不過clang還支持添加ivar塊,也就是你還可以聲明C++類型的實(shí)例變量购公,既可以在classextension中萌京,也可以在@implementation開始的位置。本例中的ObjcObject.h可以被精簡(jiǎn)為:
#import <Foundation/Foundation.h>
@interface ObjcObject : NSObject
- (void)exampleMethodWithString:(NSString*)str;
//other?wrapped?methods?and?properties
@end
????????去掉的部分都移到實(shí)現(xiàn)文件的class extension中(ObjcObject.mm):
#import "ObjcObject.h"
#import "CppObject.h"
@interface ObjcObject(){
? CppObject wrapped;
}
@end
@implementation ObjcObject
- (void)exampleMethodWithString:(NSString*)str
{
//NOTE: str為nil會(huì)建立一個(gè)空字串宏浩,而不是引用一個(gè)指向UTF8String空指針.
??? std::string cpp_str([str UTF8String],[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
??? wrapped.ExampleMethod(cpp_str);
}
????????如果我們不需要interface extension來(lái)聲明額外的屬性和方法知残,ivar塊仍然可以放在@implementation開始位置:
#import "ObjcObject.h"
#import "CppObject.h"
@implementation ObjcObject{
??? CppObject wrapped;
}
- (void)exampleMethodWithString:(NSString*)str
{
//NOTE: str為nil會(huì)建立一個(gè)空字串,而不是引用一個(gè)指向UTF8String空指針.
??? std::string cpp_str([str UTF8String],[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
??? wrapped.ExampleMethod(cpp_str);
}
????????定義的CppObject實(shí)例wrapped在ObjcObject創(chuàng)建時(shí)比庄,CppObject的缺省建構(gòu)函數(shù)會(huì)被調(diào)用求妹,而在ObjcObject被調(diào)用dealloc析構(gòu)時(shí),ObjcObject的析構(gòu)函數(shù)也會(huì)被調(diào)用佳窑。如果ObjcObject沒有提供缺省的建構(gòu)函數(shù)制恍,編譯就會(huì)失敗。
1.1.4 管理被封裝C++對(duì)象的生命周期
????????解決方案是透過new關(guān)鍵字掌握建構(gòu)過程,比如:
@interface ObjcObject(){
??? CppObject *wrapped;//指針!會(huì)在alloc時(shí)初始為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)建過程封裝起來(lái).相應(yīng)地神凑,還要顯式地釋放封閉對(duì)象:
- (void)dealloc
{
??? deletewrapped;
??? [super dealloc];//如果使用了ARC,這句就要略去
}
????????作者接著提到了另一個(gè)方法净神,顯示分配一塊內(nèi)存,然后在它的基礎(chǔ)上調(diào)用new來(lái)創(chuàng)建對(duì)象。首先聲明char wrapped_mem[sizeof(CppObject)];再使用wrapped = new(wrapped_mem) CppObject();創(chuàng)建了實(shí)例wrapped强挫。釋放時(shí)if (wrapped) wrapped->~CppObject();這樣雖然可行岔霸,但不建議使用。
1.1.5 總結(jié)
????????一定要確保封裝的方法僅返回和使用C或Objective-C類型的返回值及參數(shù)俯渤。同時(shí)不要忘記C++中不存在nil,而NUL是不可用于解引用的呆细。
1.2 在C++代碼中使用Objective-C類
1.2.1 簡(jiǎn)單示例
????????這個(gè)問題同樣存在于頭文件中。你不能因?yàn)橐隣bjective-C類型而污染了C++頭文件八匠,或無(wú)法被純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();
??? };
}
????????一個(gè)純粹的C++編譯器在Foundation.h中的代碼和ABCWidget聲明位置出錯(cuò)。
1.2.2 永恒的PIMPL
????????有沒有這樣的東西作為一類擴(kuò)展C++抡四,這樣的把戲?qū)o(wú)法正常工作柜蜈。 另一方面,PIMPL指巡,工作得很好淑履,實(shí)際上是比較常用的純C++了。在我們的例子中藻雪,我們減少到最低限度:C++類
????????C++并沒有之前提到的class extension秘噪,但是卻有另一種較為常用的方式:PIMPL(Private Implementation,私有實(shí)現(xiàn))。這里勉耀,將C++class的定義精簡(jiǎn)為:
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];
??? }
}
????????它的工作原理是——前置聲明指煎。聲明這樣的結(jié)構(gòu)或類對(duì)象的指針成員變量、結(jié)構(gòu)或類就足夠了便斥。需要注意的是封裝的對(duì)象會(huì)在析構(gòu)函數(shù)中釋放至壤。即便對(duì)于使用了ARC的項(xiàng)目,我還是建議你對(duì)這樣的C++/Objective-C重引用的文件屏蔽掉它椭住。不要讓C++代碼依賴于ARC崇渗。在XCode中可以針對(duì)個(gè)別文件屏蔽掉ARC。Target properties->Build phase頁(yè)簽京郑,展開'CompileSources',為特定文件添加編譯選項(xiàng)-fno-objc-arc。
1.2.3 C++中封裝Objective-C類的捷徑
????????您可能已經(jīng)注意到葫掉,PIMPL解決方案使用兩個(gè)級(jí)別的間接引用些举。如果包裝的目標(biāo)類像本例中的一樣簡(jiǎn)單,就可能會(huì)增大了復(fù)雜性俭厚。雖然Objective-C的類型一般不能使用在純C++中户魏,不過有一些在C中實(shí)際已經(jīng)定義了。id類型就是其中之一,它的聲明在頭文件中叼丑。雖然會(huì)失去一些Objective-C的安全性关翎,你還是可以把你的對(duì)象直接傳到C++類中:
#include <objc/objc-runtime.h>
namespace?abc
{
??? class?Widget
??? {
??????? id/*ABCWidget* */wrapped;
??????? public:
??????????? Widget();
??????????? ~Widget();
??????????? void?Reticulate();
??? };
}
????????不建議向id對(duì)象直接發(fā)送消息。這樣你會(huì)失去很多編譯器的檢查機(jī)制鸠信,特別是對(duì)于不同類中有著相同selector名字的不同方法時(shí)纵寝。所以:
#include "Widget.hpp"
#import "ABCWidget.h"
namespace?abc
{
??? Widget::Widget() : wrapped([[ABCWidgetalloc] init])
??? {
??? }
??? Widget::~Widget()
??? {
??????? [(ABCWidget*)impl release];
??? }
??? void?Widget::Reticulate()
??? {
??????? [(ABCWidget*)impl reticulate];
??? }
}
????????像這樣的類型轉(zhuǎn)換很容易在代碼中隱藏錯(cuò)誤,再嘗試一個(gè)更好的方式星立。在頭文件中:
#ifdef __OBJC__
@class?ABCWidget;
#else
typedef struct?objc_object ABCWidget;
#endif
namespace?abc
{
??? class?Widget
??? {
??????? ABCWidget *wrapped;
??????? public:
??????????? Widget();
??????????? ~Widget();
??????????? void?Reticulate();
??? };
}
????????如果這個(gè)頭文件被一個(gè)mm文件引用爽茴,編譯器可以充分識(shí)別到正確的類。
????????如果是在純C++模式中引用绰垂,ABCWidget*是一個(gè)等價(jià)的id類型:定義為typedef struct objc_object* id;室奏。
????????#ifdef塊還可以被進(jìn)一步放到一個(gè)可重用的宏中:
#ifdef __OBJC__
#define OBJC_CLASS(name) @class
name
#else
#define OBJC_CLASS(name) typedef
struct objc_object name
#endif
????????現(xiàn)在,我們可以前置聲明在頭文件中一行就可以適用于所有4種語(yǔ)言:
OBJC_CLASS(ABCWidget);
1.3 C++詞匯歧義和沖突
????????OC頭文件中定義了一些標(biāo)識(shí)符劲装,所有的OC程序必須包含的胧沫,這些標(biāo)識(shí)符識(shí)id,Class占业,SEL绒怨,IMP和BOOL。
????????OC方法內(nèi)纺酸,編譯器預(yù)聲明了標(biāo)識(shí)符self和super窖逗,就像C++中的關(guān)鍵字this。跟C++的this不同的是餐蔬,self和super是上下文相關(guān)的碎紊;OC方法外他們還可以用于普通標(biāo)識(shí)符。
????????協(xié)議內(nèi)方法的參數(shù)列表樊诺,有5個(gè)上下文相關(guān)的關(guān)鍵字(oneway仗考,in,out词爬,inout秃嗜,bycopy)。這些在其他內(nèi)容中不是關(guān)鍵字顿膨。
????????從OC程序員的角度來(lái)看锅锨,C++增加了不少新的關(guān)鍵字。你仍然可以使用C++的關(guān)鍵字做OC selector的一部分恋沃,所以影響并不嚴(yán)重必搞,但你不能使用他們命名OC類和實(shí)例變量。例如囊咏,盡管class是C++的關(guān)鍵字恕洲,但是你仍然能夠使用NSObject的方法class:
[foo class];// OK
????????然而塔橡,因?yàn)樗且粋€(gè)關(guān)鍵字,你不能用class做變量名稱:
NSObject *class; // Error
????????OC里類名和分類名有單獨(dú)的命名空間霜第。@interface foo和@interface(foo)能夠同時(shí)存在在一個(gè)源代碼中葛家。OC++里,你也能用C++中的類名或結(jié)構(gòu)名來(lái)命名你的分類泌类。
????????協(xié)議和template標(biāo)識(shí)符使用語(yǔ)法相同但目的不同:
id<someProtocolName> foo;
TemplateType<SomeTypeName> bar;
????????為了避免這種含糊之處癞谒,編譯器不允許把id做template名稱。
????????最后末誓,C++有一個(gè)語(yǔ)法歧義扯俱,當(dāng)一個(gè)label后面跟了一個(gè)表達(dá)式表示一個(gè)全局名稱時(shí),就像下面:
label:::global_name = 3;
????????第一個(gè)冒號(hào)后面需要空格喇澡。OC++有類似情況迅栅,也需要一個(gè)空格:
receiver selector: ::global_c++_name;
1.4 限制
????????OC++沒有為OC類增加C++的功能,也沒有為C++類增加OC的功能晴玖。例如读存,你不能用OC語(yǔ)法調(diào)用C++對(duì)象,也不能為OC對(duì)象增加構(gòu)造函數(shù)和析構(gòu)函數(shù)呕屎,也不能將this和self互相替換使用让簿。類的體系結(jié)構(gòu)是獨(dú)立的。C++類不能繼承OC類秀睛,OC類也不能繼承C++類尔当。另外,多語(yǔ)言異常處理是不支持的蹂安。也就是說椭迎,一個(gè)OC拋出的異常不能被C++代碼捕獲,反過來(lái)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