使用本地c/c++提高iOS性能 之四

c++編程
c++


c++是c的超集,因此這裏你將會學到更多的技術。和Objective-C相比,c++有不少不一樣的概念和語法,儘管他們都是面向對象的編程語言。他們有不少重要的不一樣之處:類,指針,多重繼承,內存管理,和模板。sql


class類數據庫


這點和Objective-C相似,一個類有兩個文件:一個是頭文件,另外一個是實現文件。你一樣能夠指定private和public的成員或方法。下面是聲明一個類的頭文件的標準方式:編程


class Cat {  編程語言

   public:
ide

           // public accessors
           unsigned int GetAge();
           void SetAge(unsigned int Age);
函數

           Cat ();
           Cat (int initialAge);~Cat();
性能

           // public member functions學習

           void Meow();ui


           // private member data

   private:

           unsigned int itsAge;

};


// GetAge, Public accessor function

// returns value of itsAge member

unsigned int Cat::GetAge() {

       return itsAge;

}


// returns sets itsAge member
void Cat::SetAge(unsigned int age) {

       // set member variable its age to

       // value passed in by parameter age

       itsAge = age;

}


// action: Prints "meow" to screen

void Cat::Meow() {

       cout << "Meow.\n";

}


int main() {  

   Cat cat;

   cat.Meow();

}


你能夠看到,public和private聲明能夠分開放在不一樣地方,就像Objective-C中定義頭文件時使用的修飾符@private和@public。對於類的實現,你須要在方法的前面加上類的名稱,來代表方法是屬於這個類的。


對於訪問和設置方法,你須要顯示的聲明他們:


unsigned int GetAge();
void SetAge(unsigned int Age);


爲了建立一個Cat對象,你只須要聲明Cat cat;而後調用方法cat.Meow();可是對象只存在函數的局部範圍內部。


對於內存管理,你須要同時聲明構造函數和析構函數。若是沒有聲明你本身的構造函數,編譯器會使用一個沒有參數的默認構造函數。



注意:若是聲明瞭本身的構造函數,請記住也要聲明析構函數,即便它沒有作什麼事情。遵照這個預約是很是好的。若是使用Cat cat建立Cat的一個新對象,你須要一個默認構造函數。




指針和內存管理


若是你想要在程序其餘地方使用你新建立的對象,在c++中,你須要在自由存儲區中申請分配內存。這個對象會一直存在,直到你顯示的在內存中把它刪除,就像c語言同樣。爲了聲明指向一個包含整型值的內存地址指針,你能夠像這樣:


int *pInt;
pInt = new int;

*pInt = 72;


分配和釋放一個對象,你可使用new和delete關鍵字。


Cat *pCat = new Cat;

pCat->SetAge(5);

delete pCat;
pCat = 0;



注意:若是你delete同一個指針兩次,你的應用會崩潰。若是你在從新分配以前沒有delete它,會有內存泄露。




繼承


相比於Objective-C,c++繼承的概念更加複雜。你能夠像正常同樣使用繼承,複寫,多態。惟一不一樣的是virtual和pure virtual方法。


class Dog : Mammal {

}


Dog類繼承了Mammal類的全部屬性和方法,同時它可以有本身的屬性和方法。當Dog對象被建立的時候,Mammal的構造函數先調用,而後在調用Dog的構造函數。


c++繼承中有一個很是重要的概念:你只可以複寫virtual方法。對於其餘方法,你只能繼承而不能複寫。在父類和子類中能夠有兩個同名的方法,可是不存在複寫和多態。對於virtual方法,建議在必要的時候在子類中進行復寫。c++中方法默認的修飾符是非virtual的。有兩種類型的虛函數:虛函數和純虛函數。


#include "stdafx.h"

#include "stdio.h"

using namespace System;

class Animal {

   public:

           virtual void Speak() = 0;

           virtual void Eat();
           void Run();

};

void Animal::Eat(){

       printf("Animal eats\n");

}

void Animal::Run(){

   printf("Animal runs\n");

}


class Dog : Animal{
   public:

           void Eat();

           void Speak();

           void Run();

};

void Dog::Eat(){

       printf("Dog eats\n");

}

void Dog::Run(){

       printf("Dog runs\n");

}

void Dog::Speak(){

   printf("Dog speaks\n");

}

int main(){

   Animal *dog1 = (Animal *) new Dog;

   dog1->Run();

   dog1->Speak();

   dog1->Eat();

   getchar();

}


對於虛函數,在父類中你能夠有它的實現;可是對於純虛函數,你只能聲明。Eat方法是虛函數,而Speak是純虛函數。在子類中,這兩個方法都須要複寫。


由於Run不是虛函數,子類不須要複寫。所以main函數的輸出將是


Animal Runs

Dog Speaks

Dog Eats



多重繼承


在c++中,你的類能夠繼承不少父類,所以你可以重用更多的屬性和方法。例如,Teacher類同時繼承Employee和Person類,如如9-4。


你能夠經過下面這樣進行多重繼承


class Teacher : public Person, public Employee {

}


多重繼承存在一個廣泛的問題:當兩個父類有相同的方法時。例如,Pesron和Employee兩個類都聲明瞭GetIncome方法。所以,當你在Teacher對象中調用此方法時,你必須顯示的指定是調用哪一個父類的方法。


若是你只是這樣寫


int currentIncome = pTeacherGetIncome();


你會獲得一個編譯錯誤


Member is ambiguous: ‘Person::GetIncome’ and ‘Employee::GetIncome’


你須要顯示的指明調用哪一個父類的函數來消除這個二義性。


int currentIncome = pTeacherEmployee::GetIncome();



模板


在c++中,你能夠定義類或方法只接收某種指定的類型。例如,你能夠定義一個方法,只接收兩個同類型的參數進行比較而後返回結果。


template <class T>
T GetMax (T a, T b) {

   return (a>b)? a : b;

}

int main () {
   int i=5, j=6, k;

   long l=10, m=5, n;

   k=GetMax<int>(i,j);

   n=GetMax<long>(l,m);

   return 0;

}


經過定義一個模板方法,你能夠確保a和b是一樣的數據類型,這樣他們就能夠進行比較。你能夠在類中也進行一樣的操做。


template <class T>

class mypair {

      T a, b;

       public:

               mypair (T first, T second){

                   a=first;

                   b=second;

               }

           T getmax () {

                   return a>b? a : b;

           }

};

int main () {
       mypair <int> myobject (100, 75);

       cout << myobject.getmax();

       return 0;

}


一個實際例子


我將經過一個實際的例子來引導你如何將c/c++庫集成到Objective-C代碼中。一個例子是集成SQLite庫,另外一個例子是講c++和Objective-C代碼進行混編。


SQLite


SQLite是CoreData的底層實現。SQLite性能仍是不錯的,由於比較輕量級和更加直接。可是,CoreData在這些方法提供了更好的支持:模型對象匹配,undo和redo功能,批量操做,線程。


使用SQLite的最大好處是:若是你的團隊已經很熟悉相關的數據庫和SQL,學習的曲線也是很是輕量級的。SQLite一樣很容易移植到Android中,這樣比就能夠在iPhone和Android應用共享相同的數據了。


你能夠閱讀SQLiteSample工程的源代碼。這裏我只給你一些重要的提示。


你須要打開命令行,而後書輸入sqlite3 students.sql 來建立數據庫。這會建立一個新的數據庫,而後就能夠在這個數據庫中建立表,插入和查詢數據。你可使用SQL語句建立表和插入數據。


sqlite> CREATE TABLE students (pk INTEGER PRIMARY KEY, name VARCHAR(25));

sqlite> INSERT INTO students (name) VALUES ('khang');
sqlite> INSERT INTO students (name) VALUES ('vo');
sqlite> INSERT INTO students (name) VALUES ('duy');

sqlite> .quit


圖9-5更加清楚的展現了在命令行中發生的事情。


你能夠在Home文件夾(這個是默認的文件夾,若是你在終端操做時沒有修改的話)下找到students.sql文件。你須要把這個文件添加到Xcode中,而後要添加libsqlite3.0.dylib這個庫。


- (void)viewDidAppear:(BOOL)animated {
       NSString *path = [[NSBundle mainBundle] pathForResource:@"students" ofType:@"sql"];

       if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {

       // Get the primary key for all books.
       const char *sql = "SELECT pk, name FROM students";

       sqlite3_stmt *statement;

       // Preparing a statement compiles the SQL query into a byte-code program in theSQLite library.

       // The third parameter is either the length of the SQL string or -1 to read up tothe first null terminator.

       if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {

           // We "step" through the results - once for each row.
           while (sqlite3_step(statement) == SQLITE_ROW) {

                   const unsigned char *name = sqlite3_column_text(statement, 1);

                   NSString *nameString = [NSString stringWithCString:(char *)nameencoding:NSASCIIStringEncoding];

           }

       }

   // "Finalize" the statement - releases the resources associated with thestatement.

   sqlite3_finalize(statement);

} else {

       // Even though the open failed, call close to properly clean up resources.

       sqlite3_close(database);
       NSAssert1(0, @"Failed to open database with message '%s'.",

       sqlite3_errmsg(database));

}

[self.tableView reloadData];

}


這裏有一些方法你須要記住:


  • sqlite3_open([path UTF8String], &database) == SQLITE_OK 初始化和將新的數據庫對象賦值給你的指針。

  • sqlite3_prepare_v2(database, sql, -1, &statement, NULL) ==SQLITE_OK  執行sql命令和把結果放入statement中。

  • while (sqlite3_step(statement) == SQLITE_ROW): 從執行的statement中遍歷結果。




在你的應用中集成c++


你能夠絕不費力的將c集成到你的應用中,由於Objective-C是c的超集。可是,當你在應用中集成c++的時候,你須要注意一些細節。


首先,你須要像正常同樣的建立c++類的頭文件和實現文件(.h和.cpp文件)。而後,你須要把包裹c++文件的Objective-C文件的後綴改成.mm(在iOS環境中這些是Objective-C++文件)。任何想要使用.mm文件的文件也必須像a.mm這樣。


你能夠查看一下TestC_CPlus這個示例工程。首先,你會看到兩個文件,Foo_Cpp.h 和Foo_Cpp.cpp,他們聲明瞭Foo_Cpp這個c++類。而後,經過MyObject.h和MyObject.m這兩個Objective-c++文件進行包裹。


最後一步,把這些邏輯集成到你的view controller中。你能夠在工程中像正常同樣調用MyObject對象。你只須要將把調用的文件後綴改成.mm。


總結


在編程領域中,已經有不少高性能的和由特殊任務的c/c++庫。你永遠不須要重複發明輪子,或努力的將c/c++代碼轉換成Objective-c代碼。理解c/c++可以幫助你更好的理解庫的使用,這樣你就可以把他們集成到你的應用中。


Objective-C是c的超集,因此不少c的特性你都已經知道了。我討論了c編程的一些主要的點,包括指針的概念。你也學到了函數指針,位操做,這些都是c的特性,可以很好的提高性能。


若是你沒有編程經驗的話,c++比較難以學習和理解。我僅僅討論了c++一些主要的知識點,c++和Objective-C的主要不一樣之處。對於編寫一個高要求的c++程序可能信息還不夠,可是對於如何將一個已經存在的c++庫集成到iPhone應用中已經足夠了。


在最後一部分,我使用了一個SQLite的真實的例子展現瞭如何在Objective-C中使用C。這個並不難,除了一些語法和內存管理上的不一樣。對於c++庫我並無深刻討論;我只是展現瞭如何將c++代碼集成到你的iPhone應用中。

相關文章
相關標籤/搜索