some tips of object c

43
Hướng dẫn lập trình Objective C Trên Ubuntu 1. Một vài dòng về lịch sự ra đời và sự phát triển. Lịch sử ra đời The wiky thì Bard Cox và Tom Love (không biết có thằng cha này không nhưng thấy wiki nó nói thì mình cũng đưa ra đây chắc lão Tom Love này cũng có chút ít công sức) thuộc Stepstone là cha đẻ của Objective - C. Ngôn ngữ này đc giới thiệu năm 1981 và chúng ta cũng nhớ rằng thập niên 80s của thế kỷ trước là sự lên ngôi của khái niệm lập trình OOP cũng là giai đoạn cực thịnh của sự ra đời các ngôn ngữ OOP trên nên C. Tiếp tục hoàn thiện trong vài năm đến 1986 Cox chính thức ấn hành chuẩn của Objective - C trong các cuốn sách Object-Oriented Programming, An Evolutionary Approach. Sự kế tiếp của Steve Jobs Cái tên này đã chở nên qua nổi tiếng từ cuộc đời ông cho đến sự nghiệp lẫy lừng và cả việc ông làm điên đảo cộng đồng IT thế giới cho đến chính bạn (chẳng phải bạn cũng đang diên đảo vì IPhone đây !_! ) Khi bi out khỏi Apple, năm 1988 Steve Jobs đã mua lại bản quyên của Obejctive - C từ Stepstone để viết cho ứng dụng phần cứng của mình. vọi là NeXTstep hay còn gọi là OpenStep công ty lúc này của Jobs là NeXT. Cho đến 1996 khi Apple mua lại NeXT thì họ tiếp tục sử dụng công nghệ OpenStep để phát triên hệ điều hành Mac của họ và xây dựng một loạt công cụ XCode, Interface Builder .. và các Cocoa API ra đời... Đến giờ thì đã hiểu là tại sao lại là Objective - C chứ không phải bất cứ ngôn ngữ nào khác để build các App trên nền Apples. 2. Tìm hiểu ngôn ngữ Ob-c Đặc điểm cơ bản. - Là ngôn ngữ hướng đối tượng - Mở rộng từ C - Nhẹ nhàng (không VM - không quá thực tạp với friend virtuals với template với ....) - Mềm dẻo (mở rộng từ C nên bạn có thể dùng C thuần cấu trúc ngoài ra đây là ngôn ngữ run-time) - Reflection (có hỗ chợ) - nil thay thế cho NULL trong C, bởi vì bạn có thể gửi thông điệp cho nil, nhưng không thể làm như vậy với NULL. - BOOL có 2 giá trị là YES và NO chứ không phải là true và false nữa. - Khái niệm methods và message đc sử dụng mang ý nghĩa như nhau đối với ObC theo đó message có những thuộc tính đặc biệt. Mọto message có thể chuyền động từ obj tới một obj khác. Việc gọi thông điệp trên một obj không có nghĩa là obj đó sẽ thực hiện message nó có thể chuyển tiếp tới một obj khác chưa biết trước tóm lại có khả năng đáp trả thông điệp không trực tiệp thì gián tiếp.

Upload: nam-tan-cntt

Post on 02-Mar-2015

382 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Some Tips of Object C

Hướng dẫn lập trình Objective C Trên Ubuntu

1. Một vài dòng về lịch sự ra đời và sự phát triển.

Lịch sử ra đờiThe wiky thì Bard Cox và Tom Love (không biết có thằng cha này không nhưng thấy wiki nó nói thì mình cũng đưa ra đây chắc lão Tom Love này cũng có chút ít công sức) thuộc Stepstone là cha đẻ của Objective - C. Ngôn ngữ này đc giới thiệu năm 1981 và chúng ta cũng nhớ rằng thập niên 80s của thế kỷ trước là sự lên ngôi của khái niệm lập trình OOP cũng là giai đoạn cực thịnh của sự ra đời các ngôn ngữ OOP trên nên C. Tiếp tục hoàn thiện trong vài năm đến 1986 Cox chính thức ấn hành chuẩn của Objective - C trong các cuốn sách Object-Oriented Programming, An Evolutionary Approach.

Sự kế tiếp của Steve JobsCái tên này đã chở nên qua nổi tiếng từ cuộc đời ông cho đến sự nghiệp lẫy lừng và cả việc ông làm điên đảo cộng đồng IT thế giới cho đến chính bạn (chẳng phải bạn cũng đang diên đảo vì IPhone đây !_! )Khi bi out khỏi Apple, năm 1988 Steve Jobs đã mua lại bản quyên của Obejctive - C từ Stepstone để viết cho ứng dụng phần cứng của mình. vọi là NeXTstep hay còn gọi là OpenStep công ty lúc này của Jobs là NeXT.Cho đến 1996 khi Apple mua lại NeXT thì họ tiếp tục sử dụng công nghệ OpenStep để phát triên hệ điều hành Mac của họ và xây dựng một loạt công cụ XCode, Interface Builder .. và các Cocoa API ra đời...Đến giờ thì đã hiểu là tại sao lại là Objective - C chứ không phải bất cứ ngôn ngữ nào khác để build các App trên nền Apples.

2. Tìm hiểu ngôn ngữ Ob-c

Đặc điểm cơ bản.- Là ngôn ngữ hướng đối tượng- Mở rộng từ C- Nhẹ nhàng (không VM - không quá thực tạp với friend virtuals với template với ....)- Mềm dẻo (mở rộng từ C nên bạn có thể dùng C thuần cấu trúc ngoài ra đây là ngôn ngữ run-time)- Reflection (có hỗ chợ)- nil thay thế cho NULL trong C, bởi vì bạn có thể gửi thông điệp cho nil, nhưng không thể làm như vậy với NULL.- BOOL có 2 giá trị là YES và NO chứ không phải là true và false nữa.- Khái niệm methods và message đc sử dụng mang ý nghĩa như nhau đối với ObC theo đó message có những thuộc tính đặc biệt. Mọto message có thể chuyền động từ obj tới một obj khác. Việc gọi thông điệp trên một obj không có nghĩa là obj đó sẽ thực hiện message nó có thể chuyển tiếp tới một obj khác chưa biết trước tóm lại có khả năng đáp trả thông điệp không trực tiệp thì gián tiếp.

Khi làm việc với Objective C bạn cần chú ý là bởi vì nó dc base trên nền của C cho nên việc bạn sử dụng cú pháp C chộn lẫn với cú pháp chính thống của Objective C là hoàn toàn chấp nhận, tuy nhìn có vẻ hơi củ chuối.

Phương thưc.Cách khai báo phương thức trong Objective - CKhông tham số :Trích dẫn:

Page 2: Some Tips of Object C

<(kiểu trả về)> Tên_phương_thứcví dụ -(void) print;

Có tham số:Trích dẫn:<(kiểu trả về)> Tên_phương_thức :<(kiểu)> Tên_Biến :<(kiểu)> Tên_Biến;ví dụ: -(void) setDenominator: (int) d;Lời gọi phương thức:không trả về: [<đối tượng> ];[<đối tượng> :<(kiểu)> ];[<đối tượng> :<(kiểu)> :<(kiểu)> ];

Trả về kết quả: = [<đối tượng> ];= [<đối tượng> :<(kiểu)> :<(kiểu)> ];

Lớp và đối tượng.Ob-C sử dụng khái niệm Interface và Implementation để phân biêt file Header và file Source của C (*.h và *.c) một lớp trong ObC định nghĩa là trên một giao diện (.h) còn phần thực thi trên file .m các bạn chú ý là m vì nó khác với C và C++.

@interface:Code:

#import @interface Fraction: NSObject {int numerator;@privateint denominator;}-(void) print;-(void) setNumerator: (int) n;-(void) setDenominator: (int) d;-(int) numerator;-(int) denominator;

@end

và đây là phần thực thi.@implementationCode:

#import "Fraction.h"@implementation Fraction

-(void) print {printf( "%i/%i", numerator, denominator );}

-(void) setNumerator: (int) n {numerator = n;}

-(void) setDenominator: (int) d {denominator = d;}

Page 3: Some Tips of Object C

-(int) denominator {return denominator;}

-(int) numerator {return numerator;}@end

Trích dẫn:- Sử dụng #import để thay thế cho #include (đây là cơ chế thông minh hơn #include của C/C++ bạn chỉ phải thêm 1 lần thôi )- ObC chỉ cho phép đơn kế thừa. Mặc định tất cả các lớp sẽ kế thừa từ NSObject.- Cặp @.... và @end là cặp giới hạn phạm vi một lớp.- Các thuộc tính dc khái báo trong cặp { ..... } và khai báo phương thức ở bên ngoài.- Nếu phương thức bắt đâu bằng "+" có nghĩa nó là thuộc phạm vi lớp (static), còn nếu "-" thì nó ở phạm vi object.- Các phạm vi truy xuất public,protected và private giống như C++ mặc định là protected.- Các truy nhập phần tử cũng sử dụng toán tử "." đối với object và "->" nếu là con trỏ.- không có các tầm vực truy xuất đối với phương thức (tức là trong ObC các phương thức có cùng một tầm vực là public)

Exception và handler.Ngôn ngữ cũng hỗ chợ các cấu trúc try - catch - throw - finally giống như ngôn C++ @try - @catch - @throw - @finally cách thức sử dụng cũng hoàn toàn tương tự.

CategoriesLà đặc điểm nếu bạn muốn mở rộng lớp bằng cách thêm mới vào lớp một phương thức. Khi bạn làm việc quen với OOP thì bạn sẽ thấy đây là một trong những thuộc tính vô cùng hữu ích của Objective C, kể cả ngay khi bạn không có mã nguồn của lớp nhưng bạn vẫn hoàn toàn có thể thêm phương thức cho lớp như thường thông qua thuộc tính này. Đặc điểm này làm giảm đi đáng kể sự kế thừa phức tạp trong C++ khi việc kế thừa chỉ để phục vụ cho việc thêm mới một phương thức. Mặt khăc việc chia mã nguồn trên nhiều files cũng giúp ích đáng kể trong việc phát triên.

Code:

#import "Fraction.h"

@interface Fraction (Math)-(Fraction*) add: (Fraction*) f;-(Fraction*) mul: (Fraction*) f;-(Fraction*) div: (Fraction*) f;-(Fraction*) sub: (Fraction*) f;@end

File thực thi.

Code:

#import "FractionMath.h"

@implementation Fraction (Math)-(Fraction*) add: (Fraction*) f {return [[Fraction alloc] initWithNumerator: numerator * [f denominator] +

Page 4: Some Tips of Object C

denominator * [f numerator]denominator: denominator * [f denominator]];}

-(Fraction*) mul: (Fraction*) f {return [[Fraction alloc] initWithNumerator: numerator * [f numerator]denominator: denominator * [f denominator]];

}

-(Fraction*) div: (Fraction*) f {return [[Fraction alloc] initWithNumerator: numerator * [f denominator]denominator: denominator * [f numerator]];}

-(Fraction*) sub: (Fraction*) f {return [[Fraction alloc] initWithNumerator: numerator * [f denominator] -denominator * [f numerator]denominator: denominator * [f denominator]];}@end

Trích dẫn:- Tên của category phải là duy nhất- Có thể thêm bao nhiêu lần mở rộng lơp từ category là không giới hạn nhưng với tên là duy nhất.- Thông thể bổ xung biến thành phần bằng category.- Có thể sử dụng category để tạo ra các phương thức private. Nếu cần.MyClass.hCode:

#import

@interface MyClass: NSObject-(void) publicMethod;@end

MyClass.mCode:

#import "MyClass.h"#import

@implementation MyClass-(void) publicMethod {printf( "public method\n" );}@end

// private methods@interface MyClass (Private)-(void) privateMethod;@end

@implementation MyClass (Private)-(void) privateMethod {

Page 5: Some Tips of Object C

printf( "private method\n" );}@end

main.mCode:

#import "MyClass.h"

int main( int argc, const char *argv[] ) {MyClass *obj = [[MyClass alloc] init];

// this compiles[obj publicMethod];

// this throws errors when compiling//[obj privateMethod];

// free memory[obj release];return 0;}

cái này thật thú vị phải không, thực ra đây là một hệ quả trực tiếp từ đăc tính run-time của Objective C.

Protocals: Giao diện.Đây hoàn toàn tương đồng với khái miện lớp ảo trong C++ hoặc gọi là giao diện trong C# và Java. Bản thân @protocals không có sự thực thi. Nếu lớp nào cam kết thực thi nó thì trong phần thực thi sẽ implement các phương thức mà protocals khai báo.

Code:

@protocol Printing-(void) print;@end

Fraction.hCode:

#import#import "Printing.h"

@interface Fraction: NSObject {int numerator;int denominator;}

-(Fraction*) initWithNumerator: (int) n denominator: (int) d;-(void) setNumerator: (int) d;-(void) setDenominator: (int) d;-(void) setNumerator: (int) n andDenominator: (int) d;-(int) numerator;-(int) denominator;@end

Fraction.m

Page 6: Some Tips of Object C

Code:

#import "Fraction.h"#import

@implementation Fraction-(Fraction*) initWithNumerator: (int) n denominator: (int) d {self = [super init];

if ( self ) {[self setNumerator: n andDenominator: d];}

return self;}

-(void) print {printf( "%i/%i", numerator, denominator );}

-(void) setNumerator: (int) n {numerator = n;}

-(void) setDenominator: (int) d {denominator = d;}

-(void) setNumerator: (int) n andDenominator: (int) d {numerator = n;denominator = d;}

-(int) denominator {return denominator;}

-(int) numerator {return numerator;}

-(Fraction*) copyWithZone: (NSZone*) zone {return [[Fraction allocWithZone: zone] initWithNumerator: numeratordenominator: denominator];}@end

Complex.hCode:

#import#import "Printing.h"

@interface Complex: NSObject {double real;double imaginary;

Page 7: Some Tips of Object C

}

-(Complex*) initWithReal: (double) r andImaginary: (double) i;-(void) setReal: (double) r;-(void) setImaginary: (double) i;-(void) setReal: (double) r andImaginary: (double) i;-(double) real;-(double) imaginary;@end

Complex.mCode:

#import "Complex.h"#import

@implementation Complex-(Complex*) initWithReal: (double) r andImaginary: (double) i {self = [super init];

if ( self ) {[self setReal: r andImaginary: i];}

return self;}

-(void) setReal: (double) r {real = r;}

-(void) setImaginary: (double) i {imaginary = i;}

-(void) setReal: (double) r andImaginary: (double) i {real = r;imaginary = i;}

-(double) real {return real;}

-(double) imaginary {return imaginary;}

-(void) print {printf( "%_f + %_fi", real, imaginary );}@end

main.mCode:

Page 8: Some Tips of Object C

#import#import "Fraction.h"#import "Complex.h"

int main( int argc, const char *argv[] ) {// create a new instanceFraction *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];Complex *comp = [[Complex alloc] initWithReal: 5 andImaginary: 15];id printable;id copyPrintable;

// print itprintable = frac;printf( "The fraction is: " );[printable print];printf( "\n" );

// print complexprintable = comp;printf( "The complex number is: " );[printable print];printf( "\n" );

// this compiles because Fraction comforms to both Printing and NSCopyablecopyPrintable = frac;

// this doesn't compile because Complex only conforms to Printing//copyPrintable = comp;

// test conformance

// trueif ( [frac conformsToProtocol: @protocol( NSCopying )] == YES ) {printf( "Fraction conforms to NSCopying\n" );}

// falseif ( [comp conformsToProtocol: @protocol( NSCopying )] == YES ) {printf( "Complex conforms to NSCopying\n" );}

// free memory[frac release];[comp release];

return 0;}

PropertiesThuộc tính gần như bất cứ một ngôn ngữ mới hiện đại nào cũng hỗ chợ khái niệm này, đây là một khái niệm bảo toàn tính đóng gói của tư tưởng OOP.Đối vơi ngôn ngữ ObC có mốt số những hỗ chợ đặc biệt hơn một chút bạn khai báo sử dụng bằng @properties cũng giống như những ngôn ngữ khác khi bạn sử dụng thuộc tính với ObC bạn sẽ có 2 lựa chọn là @synthesize và @dynamic, với lựa chọn là @synthesize thì mặc

Page 9: Some Tips of Object C

nhiên trình biên dịch sẽ giúp bạn sinh ra các phương thức set và get trên thuộc tính. nhưng nếu bạn lựa chọn là @dynamic thì mọi việc bạn phải tự làm lấy.hãy xem codeCode:

#import

@interface Photo : NSObject {NSString* caption;NSString* photographer;}- (NSString*) caption;- (NSString*) photographer;

- (void) setCaption: (NSString*)input;- (void) setPhotographer: (NSString*)input;

@end

vàCode:

#import

@interface Photo : NSObject {NSString* caption;NSString* photographer;}@property (retain) NSString* caption;@property (retain) NSString* photographer;

@end

Kiểu idid trong ObC gần tương tự như void* trong C. bạn không cần phải biết rõ kiểu của object khi bạn gọi phương thức trong ObC điều này hoàn toàn khác với C++ bơi đơn giản khi gọi phương thức cũng giống như bạn truyền thông điệp trong ObC. Nếu đối tượng nó có phương thức thì sẽ đáp lại thông điệp mà bạn truyền (gọi phương thức) và phương thức đc gọi. Cũng nguy hiểm đây chứ .. :(

Ép kiểu động.những phương thức dưới đây dùng để kiểm tra kiểu.

Trích dẫn:- (BOOL) isKindOfClass: classObj >> đối tượng là hậu duệ hoặc thể hiện của classObj- (BOOL) isMemberOfClass: classObj >> là một thành phần của objClass- (BOOL) respondsToSelector: selector >> đối tượng có phương thức bởi selector+ (BOOL) instancesRespondToSelector: selector >> đối tượng đc tạo bởi lớp có đáp ứng selector- (id) performSelector: selector >> triệu gọi chính sách selector trên đối tượng.

Constructors - hàm khỏi tạo:Vấn đề là với một lớp thì hàm khởi tạo dùng để sinh đối tượng và cũng là chỗ để tư duy về hàm hủy và cách thức lưu trong bộ nhớ của đối tượng. Về vấn đề hủy đối tượng ta sẽ có một phần riêng và nó hoàn toàn khác biệt với việc viết hàm hủy trong C++ và các ngôn ngữ

Page 10: Some Tips of Object C

khác. Tất nhiên không có gì là không thể viết khi bạn đã hiểu rõ và thông thạo ngôn ngữ. Và hẳn nhiên bạn có thể quên hết những luật về khởi tạo đối tượng hàm tạo và hàm hủy của C++ vơi ObC bạn hoàn toàn có thể tự mình chế biến những hàm đó theo ý thích và cũng không có quy luật gì về tên tuổi của hàm, tuy nhiên theo thoi quen truyền thông để giúp style - code chở nên sáng sủa nên dùng bằng các từ như init hoặc tương tự... để định nghĩa hàm khởi tạo

Fraction.hCode:

...-(Fraction*) initWithNumerator: (int) n denominator: (int) d;...

Fraction.mCode:

...-(Fraction*) initWithNumerator: (int) n denominator: (int) d {self = [super init];

if ( self ) {[self setNumerator: n andDenominator: d];}

return self;}...

main.mCode:

#import#import "Fraction.h"

int main( int argc, const char *argv[] ) {// create a new instanceFraction *frac = [[Fraction alloc] init];Fraction *frac2 = [[Fraction alloc] init];Fraction *frac3 = [[Fraction alloc] initWithNumerator: 3 denominator: 10];

// set the values[frac setNumerator: 1];[frac setDenominator: 3];

// combined set[frac2 setNumerator: 1 andDenominator: 5];

// print itprintf( "The fraction is: " );[frac print];printf( "\n" );

Page 11: Some Tips of Object C

printf( "Fraction 2 is: " );[frac2 print];printf( "\n" );

printf( "Fraction 3 is: " );[frac3 print];printf( "\n" );

// free memory[frac release];[frac2 release];[frac3 release];

return 0;}

- Từ khóa supper để tham chiếu tới lớp cha.- Từ khóa self tác dụng tương đương như this trong C++. (chính bản thân đang thể hiện của lớp - object hiện tại)- Kết thúc hàm khởi tạo (init) sẽ trả về chính đối tượng dc tạo ra thông qua từ khóa self.- Mặc định trong ObC hàm khỏi tạo là - (id) init;- Trong ObC hàm khởi tạo chỉ có ý nghĩa về mặt tư duy, nó không đc đối sử đặc biết giống như C++.

Đa hình.có lẽ với những trình bày ở trên phần nào giúp bạn mường tượng ra dc cơ chế đa hình của ObC. phần này chỉ là viết thêm nhằm củng cố một số những điểm sau đây.1. Trong ObC không có từ khóa virtual và thực sự là không cần thiết vì nó sẽ không tạo ra những thứ quá phức tạp giống như C++ vì việc phủ quyết hàm trong ObC là phủ quyết trắng chợn không liên quan gì tới sự kế thừa. nếu 2 hàm giống hệt nhau ở 2 lớp quan hệ cha con thì cũng chẳng sao cả. cũng cần nói thêm là ObC là đơn kế thừa.2. Quá trình tạo mối liên hệ giữa thể hiện của ObC và phương thức sẽ đc gọi là thời điểm run-time. Điểu này hoàn toàn có ý nghĩa nếu bạn gọi một phương thức mà bản thân đối tượng không có cũng không có lỗi gì. Lỗi chỉ xảy ra khi lời gọi đó dc thực hiện. Tuy nhiên ban cũng dc cung cấp những cơ chế để kiểm soát việc này. đây cũng là một đặc tính Run-Time của ObC nếu bạn quan tâm có thể tìm kiếm thông tin từ việc chuyển tiếp thông điệp (forward) tới một đối tượng khác.

Quản ly bộ nhớ.ObC có 2 lựa chọn cho việc quản lý bộ nhớ. Thông thường bộ nhớ dc quản lý bởi lập trình viên, ObC có trình biên dịch chỉ thị nhứ "release", "retain", "autorelease" là những chỉ thị hỗ chợ lập trình viên mạnh mẽ trong việc quản lý bộ nhớ.ObC sử dụng một tham chiếu đếm để dò tìm ra những thay đổi trên một đối tượng. Biến đếm này sẽ tăng lên một khi đối tượng dc cấp phát bộ nhớ bằng phương thức alloc, biến đếm này sẽ giảm đi một khi đối tượng dc giải phóng bằng phương thức dealloc. Như vậy nguyên lý cấp phát và duy trì bộ nhớ của đối tượng trong ObC dc sử dụng thông qua phương thức alloc và dealloc.

Mặt khác tiện ích khác từ kiểu dữ liệu nil đã nói ở trên đó việc giải phóng bộ nhơ. Trong ngữ cảnh một đối tượng của bạn là bao gồm nhiều những đối tượng khác. những đối tượng khác đó có thể đã dc giải phóng hoặc chưa. như thế bạn sẽ thực hiện lời gọi dealloc trên tập đối tượng mà bạn có, nếu con thì nó sẽ thực hiện giải phóng trong trường hợp bằng nil cũng

ok   không vấn đề gì (no error) vì nil cũng có thể truyền thông điệp.

Page 12: Some Tips of Object C

hãy xem ví dụ

# AddressCard.hCode:

#import#import

@interface AddressCard: NSObject {NSString *first;NSString *last;NSString *email;}

-(AddressCard*) initWithFirst: (NSString*) flast: (NSString*) lemail: (NSString*) e;-(NSString*) first;-(NSString*) last;-(NSString*) email;-(void) setFirst: (NSString*) f;-(void) setLast: (NSString*) l;-(void) setEmail: (NSString*) e;-(void) setFirst: (NSString*) flast: (NSString*) lemail: (NSString*) e;-(void) setFirst: (NSString*) f last: (NSString*) l;-(void) print;@end

# AddressCard.m

Code:

#import "AddressCard.h"#import

@implementation AddressCard-(AddressCard*) initWithFirst: (NSString*) flast: (NSString*) lemail: (NSString*) e {self = [super init];

if ( self ) {[self setFirst: f last: l email: e];}

return self;}

-(NSString*) first {return first;}

-(NSString*) last {return last;}

Page 13: Some Tips of Object C

-(NSString*) email {return email;}

-(void) setFirst: (NSString*) f {[f retain];[first release];first = f;}

-(void) setLast: (NSString*) l {[l retain];[last release];last = l;}

-(void) setEmail: (NSString*) e {[e retain];[email release];email = e;}

-(void) setFirst: (NSString*) flast: (NSString*) lemail: (NSString*) e {[self setFirst: f];[self setLast: l];[self setEmail: e];}

-(void) setFirst: (NSString*) f last: (NSString*) l {[self setFirst: f];[self setLast: l];}

-(void) print {printf( "%s %s <%s>", [first cString],[last cString],[email cString] );}

-(void) dealloc {[first release];[last release];[email release];

[super dealloc];}@end

# main.m

Code:

#import "AddressCard.h"

Page 14: Some Tips of Object C

#import#import

int main( int argc, const char *argv[] ) {NSString *first =[[NSString alloc] initWithCString: "Tom"];NSString *last = [[NSString alloc] initWithCString: "Jones"];NSString *email = [[NSString alloc] initWithCString: "[email protected]"];AddressCard *tom = [[AddressCard alloc] initWithFirst: firstlast: lastemail: email];

// we're done with the strings, so we must dealloc them[first release];[last release];[email release];

// print to show the retain countprintf( "Retain count: %i\n", [[tom first] retainCount] );[tom print];printf( "\n" );// free memory[tom release];

return 0;}

# output

Retain count: 1Tom Jones

Trích dẫn:Mặt khác ObC cũng cung cấp một cớ chế thông minh thường thường trong việc bạn gọi và sử dụng đối tượng mà không phải quan tâm lo lắng đến việc cấp phát và giải phóng bộ nhớ đó là cơ chế NSAutoreleasePool. Để dùng dc cơ chế này bạn chỉ việc nhớ 2 điều kẹp đoạn code mà bạn muốn kiểm soát vào trong giữa NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init] và [pool release]. Nguyên lý của đồng chí này cũng giống như lời gọi hàm vì thế bạn hoàn toàn có thể sử dụng lồng nhau và sự đóng mở hợp lý (giống như thẻ đóng và mở của XML đó) tự nhiên ObC sẽ đẩy các lời gọi pool sau xuống stack và đặt pool mới trên cùng sau đó nhét các object dc tạo ra trong lòng nó vào cái pool vừa mới tạo nếu song thì giải phóng rồi lại đẩy tiếp thằng pool ở dưới lên cứ thế... kinh chưa  .

1.1. Association ReferencesSử dụng tham chiếu liên kết để giả lập việc bổ sung các biến thể hiện đối tượng vào một đối tượng khác.

Việc khởi tạo một tham chiếu liên kết chủ yếu dựa trên một key, ta có thể thêm nhiều liên kết nếu ta muốn với nhiều key khác nhau, sử dụng hàm runtime của Objective-C là objc_setAssociatedObject

void objc_setAssociatedObject (id object, void *key, id value, objc_AssociationPolicy policy)

object: đối tượng nguồn của liên kết

key: khóa của liên kết. Khóa này thường là đối tượng static.

Page 15: Some Tips of Object C

value: giá trị liên kết với khóa cho đối tượng. Đưa giá trị nil để xóa một liên kết đã tồn tại

policy: các policy cho liên kết

static char overviewKey;

NSArray *array = [[NSArray alloc] initWithObjects:@”One”, @”Two”, @”Three”, nil];

NSString *overview = [[NSString alloc] initWithFormat:@”%@”, @”First three numbers”];

objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN);

Tạo liên kết overview vào đối tượng array, ta truy xuất đối tượng liên kết overview thông qua đối tượng array và khóa

NSString *associatedObject = (NSString *)objc_getAssociatedObject(array, &overviewKey);

1.2. SelectorTrong Objective-C, khái niệm Selector có 2 nghĩa. Nó có thể được dùng đơn giản là chỉ đến tên của một phương thức khi nó được sử dụng trong mã nguồn một thông điệp gửi đến một đối tượng. Bên cạnh đó nó còn chỉ đến một định danh duy nhất mà thay thế cho một tên khi mã nguồn được biên dịch

Selector khi được biên dịch có kiểu là SEL. Tất cả các phương thức có cùng tên thì có cùng selector. Ta có thể sử dụng một selector để triệu gọi một phương thức trên một đối tượng, điều này thể hiện khá căn bản mẫu thiết kế target-action trong Cocoa.

Code:

SEL setWidthHeight;

setWidthHeight = @selector(setWidth:Height:);

Chỉ thị @selector cho phép ta tham chiếu đến một selector được biên dịch hơn là tên một phương thức đầy đủ. Đoạn mã trên là một selector cho phương thức setWidth:Height được assign cho biến SEL là setWidthHeight.

Ở trên trường hợp ta tham chiếu đến một selector thông qua chỉ thị @selector, trong một số trường hợp ta có thể chuyển từ một chuỗi ký tự thành một selector trong thời điểm runtime bằng phương thức NSSelectorFromString.

setWidthHeight = NSSelectorFromString(aBuffer);

Và ngược lại ta có thể lấy tên phương thức từ một selector thông qua phương thức NSStringFromSelector.Code:

NSString *method;

method = NSStringFromSelector(setWidthHeight);

Một selector đã biên dịch chị định một tên phương thức chứ không thực thi phương thức. Ví dụ, có một phương thức Display cho một lớp, có nhiều selector giống nhau cho phương thức Display ở các lớp khác, với mục đích đa hình và ràng buộc động. Nếu có một selector cho mỗi phương thức thực thi, thì một thông điệp sẽ không khác một lời gọi phương thức.

Page 16: Some Tips of Object C

Một phương thức lớp và một phương thức thể hiện có cùng tên được assign bởi một selector. Tuy nhiên, bởi vì 2 phương thức này phân biệt domain (phạm vi lớp, phạm vi đối tượng của lớp) nên sẽ không có sự nhầm lẫn giữa 2 phương thức này.

Việc định tuyến một thông điệp truy xuất vào một phương thức chỉ thông qua một selector duy nhất, vì vậy nó đối xử như nhau đối với các phương thức có cùng selecor. Nó phát hiện kiểu trả về của phương thức và kiểu dữ liệu của các tham số thông qua selector. Vì vậy, ngoại trừ thông điệp truyền vào các bộ nhận kiểu tĩnh, còn lại với các ràng buộc động nó yêu cầu tất cả các tên phương thức thực thi phải có cùng kiểu trả về và có tham số cùng kiểu. (các bộ nhận kiểu tĩnh là ngoại lệ, trình biên dịch có thể biết về các phương thức thực thi từ kiểu lớp).

Mặc dù định danh của phương thức lớp và phương thức thể hiện là cùng một selector, nhưng chúng có thể có kiểu trả về và kiểu của các tham số khác nhau.

Ba phương thức performSelector:, performSelector:withObject:, và performSelector:withObject:withObject:, định nghĩa trong protocol NSObject lấy các định danh SEL như là các tham số khởi tạo. Tất cả các phương thức này ánh xạ trực tiếp vào thông điệp phương thức. Ví dụ:

Code:

[friend performSelector:@selector(gossipAbout:) withObject:aNeighbor];

Tương đương với

Code:

[friend gossipAbout:aNeighbor];

Hoặc ví dụ:

Code:

id helper = getTheReceiver();

SEL request = getTheSelector();

[helper performSelector:request];

Các phương thức có thể biến đổi tại thời điểm runtime, như ở trên bộ nhận helper được chọn thông qua phương thức getTheReceiver và bộ nhận này được yêu cầu thực hiện phương thức request thông qua phương thức getTheSelector tại thời điểm runtime.

Lưu ý, khi một bộ nhận buộc phải thực hiện một phương thức mà không thuộc phạm vi của mình thông qua selector thì đương nhiên sẽ xuất hiện lỗi. Vì những công việc này thực thi ở thời điểm runtime nên hiển nhiên lỗi sẽ chỉ xuất hiện tại thời điểm chương trình được thực thi.

1.3. Xử lý ngoại lệCode:

@try {

Page 17: Some Tips of Object C

}

@catch (CustomException *ce) { // 1

}

@catch (NSException *ne) { // 2

// Perform processing necessary at this level.

}

@catch (id ue) {

}

@finally { // 3

// Perform processing necessary whether an exception occurred or not.

}

Bắt các kiểu ngoại lệ cụ thể nhấtBắt các kiểu ngoại lệ chung1.4. Quản lý bộ nhớ1.4.1. Các nguyên tắc quản lý bộ nhớNguyên tắc cơ bản

Khi bạn nắm quyền sở hữu một đối tượng, khởi tạo đối tượng bằng các phương thức mà trong tên bắt đầu với với alloc hoặc new hoặc copy (ví dụ, alloc, newObject hoặc mutableCopy…) hoặc gửi một thông điệp retain, bạn phải có trách nhiệm giải phóng quyền sở hữu đối tượng đó bằng cách sử dụng release hoặc autorelease. Bất kỳ khi nào bạn nhận được một đối tượng (không phải tự mình khởi tạo), bạn không được release nó.Các nguyên tắc khác

Khi bạn cần lưu trữ một đối tượng được nhận như một property trong một biến thể hiện, bạn phải retain hoặc copy nó. (Điều này không đúng cho cho tham khảo yếu, nhưng đây là điển hình hiếm).Một đối tượng được nhận thường đảm bảo vẫn có hiệu lực trong phương thức mà nó đã được nhận (ngoại trừ trong các ứng đa luồng và vài trường hợp Distributes Objects). Phương thức đó có thể trả về an toàn đối tượng mà nó được triệu gọi. Sử dụng retain trong việc kết hợp với release hoặc autorelease khi cần thiết để bảo vệ một đối tượng khỏi hiệu lực của các thông điệp không hợp lệ bên ngoài.autorelease có nghĩa là “gửi một thông điệp release sau đó1.4.2. Vấn đề khi khởi tạo đối tượngXét đoạn code khởi tạo đối tượng sauCode:

Page 18: Some Tips of Object C

TestClass *ptr = [TestClass alloc];

[ptr init];

// Do something with the object

if (ptr)

Thoạt nhìn qua có vẻ vô hại, phương thức alloc khởi tạo một vùng nhớ cho đối tượng ptr, sau đó gửi thông điệp gọi phương thức init cho đối tượng ptr, lỗi có thể xảy ra ở đây. Giả sử dòng lệnh ở dòng lệnh [ptr init], phương thức init có lỗi xảy ra và trả về nil, tiếp theo ở dòng lệnh if, lúc này ptr vẫn khác nil mặc dù phương thức init trả về nil (nhưng nó không được gán cho ptr), dẫn tới ý nghĩa của phương thức tạo thực sự không còn nữa.

Khi đó, đoạn code sau sẽ giải quyết được khả năng lỗi ở trênCode:

TestClass *ptr = [[TestClass alloc] init];

// Do something with the object

if (ptr)

Giả sử init có lỗi và trả về nil thì ptr vẫn sẽ được gán là nil, do đó dòng lệnh if sẽ thực hiện đúng ý nghĩa của nó.

Ý nghĩa từ ví dụ này đó là, ta luôn luôn phải trả về nil trong phương thức init nếu có lỗi khởi tạo xảy ra và lưu ý phải kết hợp 2 lời gọi phương thức alloc và init.

1.4.3. ReleaseNếu bạn khởi tạo một đối tượng sử dụng cách thủ công alloc, bạn cần phải release đối tượng sau đó. Bạn không nên thực hiện release thủ công một đối tượng autorealse bởi vì ứng dụng có thể sẽ bị crash nếu bạn làm điều đó.

Code:

// string1 will be released automatically

NSString* string1 = [NSString string];

// must release this when done

NSString* string2 = [[NSString alloc] init];

…..

[string2 release];

1.4.4. RetainTrong Objective-C, mỗi đối tượng có một bộ đếm được sử dụng để kiểm soát tất cả các tham chiếu bởi đối tượng hoặc nó có.

Để biết được giá trị của bộ đếm này ta sử dụng thuộc tính retainCount [object retainCount]. Phương thức alloc, new, copy và retain đều tăng bộ đếm này lên 1 và phương thức release

Page 19: Some Tips of Object C

giảm bộ đếm này đi 1, khi bộ đếm có giá trị bằng 0 thì phương thức dealloc của đối tượng sẽ được gọi.

Bất cứ khi nào một đối tượng có nhu cầu được sử dụng bởi một đối tượng khác nó phải retain để tăng bộ đếm retainCount lên 1, và khi nó không còn sử dụng nữa thì phải release để giảm bộ đếm retainCount đi 1. Khi bộ đếm có giá trị bằng 0 có nghĩa là nó không còn nhu cầu để sử dụng nữa, nó sẽ tự hủy bằng phương thức dealloc.

1.4.5. DeallocPhương thức dealloc được gọi khi đối tượng đang được remove khỏi bộ nhớ. Nó thường được sử dụng nhất khi giải phóng tất cả các tham chiếu của các biến thể hiện con của đối tượng khỏi bộ nhớ. Hay nói cách khác, một lớp nếu có các biến thể hiện là các đối tượng thì trong phương thức dealloc của lớp phải thực hiện giải phóng các biến thể hiện này.

Code:

- (void)dealloc {

[childVar1 release];

[childVar2 release];

[super dealloc];

}

Phương thức [super dealloc] được sử dụng để thông báo cho lớp cha thực hiện việc “dọn dẹp” (đây là phương thức được định nghĩa trong NSObject), nếu không gọi phương thức này, có thể đối tượng sẽ không được remove khỏi bộ nhớ, sẽ gây nên tình trạng chiếm bộ nhớ.Code:

#import <Foundation/Foundation.h>

#import “Address.h”

@interface Person : NSObject {

Address* address;

}

-(Person*) constructor: (Address*) add;

-(void) show;

@end

Code:

#import “Person.h”

@implementation Person

-(Person*) constructor: (Address*) add{

self = [super init];

if(self){

Page 20: Some Tips of Object C

address = add;

}

return self;

}

-(void) show{

printf(“Person: \n”);

printf(“ — “);

[address show];

}

-(void) dealloc{

printf(“Dealloc method of Person\n”);

[address release];

[super dealloc];

}

@end

Code:

#import <Foundation/Foundation.h>

#import “Person.h”

#import “Name.h”

@interface VNPerson : Person {

Name* name;

}

-(VNPerson*) constructor: (Address*) add : (int) idn;

@end

Code:

#import “VNPerson.h”

#import “Name.h”

@implementation VNPerson

Page 21: Some Tips of Object C

-(VNPerson*) constructor: (Address*) add : (int) idn{

self = [super constructor: add];

if(self){

name = [[Name alloc] constructor: idn];

}

return self;

}

-(void) show{

[super show];

printf(“ — “);

[name show];

}

-(void) dealloc{

printf(“Dealloc method of VNPerson\n”);

[name release];

[super dealloc];// Mục đích giải phóng biến thể hiện address trong lớp cha

}

@end

Code:

#import “Address.h”

#import “VietnamAddress.h”

#import “Person.h”

#import “VNPerson.h”

int main(int argc, char *argv[]) {

Address *address = [[Address alloc] constructor: 01 : 02];

[address show];

VietnamAddress *vnaddress = [[VietnamAddress alloc] constructor: 03 : 04];

[vnaddress show];

Page 22: Some Tips of Object C

[vnaddress changeAddress: 05];

Person *person = [[VNPerson alloc] constructor: address : 1915];

[person show];

[address release];

[vnaddress release];

[person release];

return 1;

}

Lưu ý: Phương thức dealloc sẽ không được gọi trong trường hợp bộ dọn rác được bật.

1.4.6. Tham chiếu yếuRetain một đối tượng tạo ra một tham chiếu “mạnh” đến đối tượng đó. Một đối tượng không thể dealloc cho tới khi tất cả các tham chiếu mạng được giải phóng hết.

Trong một số trường hợp, ta có thể muốn có một tham chiếu đến đối tượng mà không cản trở việc đối tượng tự giải phóng chính nó, lúc này ta có thể thiết lập một tham chiếu “yếu” đến đối tượng.

Một tham chiếu yếu được tạo ra bằng cách chứa một con trỏ trỏ đến một đối tượng mà không retain đối tượng đó. Tham chiếu yếu rất cần thiết trong việc thiết lập các tham chiếu vòng. Ví dụ, đối tượng A và đối tượng B cần trao đổi thông tin với nhau nên mỗi đối tượng cần một tham chiếu đến đối tượng kia (ví như mới quan hệ giữa 2 đối tượng cha – con), nếu như chúng ta retain đối tượng kia khi thiết lập tham chiếu thì mỗi đối tượng chỉ được dealloc khi nào kết nối này đứt, tuy nhiên kết nối này chỉ đứt khi có một đối tượng được dealloc mà thôi. Trong trường hợp này ta thấy được lợi ích của tham chiếu yếu.

Chúng ta phải thật cẩn thận khi truyền thông điệp trong tham chiếu yếu, trong trường hợp đối tượng nhận thông điệp đã dealloc thì ứng dụng có thể sẽ bị crash. Đồng thời, trong mối quan hệ tham chiếu yếu, đối tượng được tham chiếu đến phải có trách nhiệm báo cho đối tượng kia biết khi nó thực hiện dealloc, ví dụ gửi một thông điệp setDelegate: với tham số nil cho đối tượng kia chẳng hạn, trong trường hợp đối tượng dealloc là một Delegate của đối tượng nhận thông điệp.

Thực hành Objective C với GNUStep trên ubuntu

đầu tiên bạn cài đặt các gói sau để lập trình:

Trích dẫn:sudo apt-get -y install build-essentialsudo apt-get -y install gnustepsudo apt-get install gobjc

Page 23: Some Tips of Object C

sudo apt-get install gnustep-makesudo apt-get install libgnustep-base-devgõ lệnh

Trích dẫn:sudo gedit /etc/profilethêm vào cuối file dòng sauTrích dẫn:export GNUSTEP_MAKEFILES=/usr/share/GNUstep/Makefilessau đó khởi động lại máy ubuntu để nhận biến môi trường

1) bài thực hành lập trình C thuần với trình biên dịch objective C GNUstep

tạo file main.c

Code:

#include <stdio.h>

int main(void){ printf("Hello World\n");}

tạo GNUmakefile với nội dung như sau:

Code:

include $(GNUSTEP_MAKEFILES)/common.make

CTOOL_NAME = HelloWorldHelloWorld_HEADERS =HelloWorld_C_FILES = main.cHelloWorld_RESOURCE_FILES =

include $(GNUSTEP_MAKEFILES)/ctool.make

đặt vào cùng thư mục và gõ lệnh makegõ lệnhTrích dẫn:./shared_obj/HelloWorldđể in ra kết quả màn hình

Bài 2) viết 1 chương trình thuần C sử dụng C posix trên MACOS

main.c Code:

#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *Day_la_Thread_Ma_Ban_CanDung( void *ptr ){ char *message; message = (char *) ptr; printf("%s \n", message);}

Page 24: Some Tips of Object C

int main(){ pthread_t thread1, thread2; char *message1 = "Sonnh89: Day la Thread 1"; char *message2 = "Sonnh89: Day la Thread 2"; int iret1, iret2;

iret1 = pthread_create( &thread1, NULL, Day_la_Thread_Ma_Ban_CanDung, (void*) message1); iret2 = pthread_create( &thread2, NULL, Day_la_Thread_Ma_Ban_CanDung, (void*) message2);

pthread_join( thread1, NULL);//day la ham wait cho thread thuc hien xong moi chay tiep pthread_join( thread2, NULL);

printf("GIa Tri Tra ve: %d\n",iret1); printf("GIa Tri Tra ve: %d\n",iret2); return 0;}

gnumakefile

Trích dẫn:include $(GNUSTEP_MAKEFILES)/common.make

CTOOL_NAME = HelloWorldHelloWorld_HEADERS =HelloWorld_C_FILES = main.cHelloWorld_RESOURCE_FILES =ADDITIONAL_TOOL_LIBS += -pthread

include $(GNUSTEP_MAKEFILES)/ctool.makeBài 3) Viết chương trình sử dụng Objective C và C thuần

khai báo 1 kiểu id có dạng Greeter kế thừa từ lớp Object

main.m

Code:

#include <objc/Object.h>@interface Greeter:Object{}- (void)greet;@end#include <stdio.h>@implementation Greeter//thuc hien code- (void)greet{

printf("Hello, World!\n");}@end#include <stdlib.h>

Page 25: Some Tips of Object C

int main(void){

id myGreeter;//khai bao bien ID greetermyGreeter=[Greeter new];//cap phat greeter[myGreeter greet];//goi ham greet[myGreeter free];//giai phong Greeterreturn EXIT_SUCCESS;

}

đối với C thuần và objective C thì thư viện makefile: include $(GNUSTEP_MAKEFILES)/common.make giống hệt nhau, đều cần phải thêm vào

đổi tên CTOOL_NAME = HelloWorld thành tên OBJC_PROGRAM_NAME tương ứng với kiểu objective C

GNUmakefile: 

Code:

include $(GNUSTEP_MAKEFILES)/common.make

OBJC_PROGRAM_NAME = HelloWorldHelloWorld_HEADERS =HelloWorld_OBJC_FILES = main.mHelloWorld_RESOURCE_FILES =

include $(GNUSTEP_MAKEFILES)/objc.make

Bài 4ở objective C và C cũng có sợ khác nhau giống như của C++ và Ctừ khóa +new và +alloc/-init đều cùng là cấp phát động, nó tương tự với new và malloc của C++ và C, thông thường, nếu khôg có gì đặc biệt thì bạn nên dùng new thay vì dùng init/alloc

Headers

để lập trình với ngữ pháp của ngôn ngư C, bạn phải include thư việnTrích dẫn:#include <Foundation/NSArray.h>ngoài ra, bạn cũng có thể sư dụng từ khóa thuần Objective C là importTrích dẫn:#import <Foundation/NSArray.h>để tránh việc đệ quy trong việc include các thư viện, bạn sử dụng #ifdefTrích dẫn:#ifndef HAVE_NSARRAY_H#define HAVE_NSARRAY_H

#include <Foundation/NSArray.h>

#endif

Page 26: Some Tips of Object C

[Objective C] Hướng dẫn tích hợp OpenGL ES Template vào Xcode

OpenGL là gì ?Được phát triển đầu tiên bởi Silicon Graphic, Inc., là một giao diện phần phần mềm hướng thủ tục theo chuẩn công nghiệp hộ trợ đồ họa 3 chiều. Cung cấp khoảng 120 tác vụ để vẽ các primitive trong nhiều mode khác nhau. Với OpenGL, bạn có thể tạo ra ảnh 3 chiều cả tĩnh và động với chất lượng cao.Là một giao diện phần mềm độc lập với phần cứng (hardware – independent software interface) hộ trợ cho lập trình đồ họa. Để làm được điều này, OpenGL không thực hiện các tác vụ thuộc về hệ điều hành cũng như không nhận dữ liệu nhập của người dùng (người dùng giao tiếp với OpenGL thông qua OpenGL API). Nó là lớp trung gian giữa người dùng và phần cứng. Nghĩa là nó giao tiếp trực tiếp với driver của thiết bị đồ họa.GLUT (pronounced like the glut in gluttony) is the OpenGL Utility Toolkit, a window system independent toolkit for writing OpenGL programs. It implements a simple windowing application programming interface (API) for OpenGL. GLUT makes it considerably easier to learn about and explore OpenGL programming. GLUT provides a portable API so you can write a single OpenGL program that works across all PC and workstation OS platforms.

Để xây dựng một ứng dụng đồ họa 3D thì đòi hỏi bạn phải có một số kiến thức nhất định về hình học không gian, thêm vào đó là các phép toán trên ma trận.

Trong OpenGL ES, một điểm trong không gian người ta dùng thuật ngữ vertex. Và hình phức tạp nhất mà bạn có thể vẽ đó là một tam giác. Một vertex có thể thuộc vào các tam giác khác nhau. Bạn tạo ra các đối tượng phức tạp khác bằng cách vẽ các tam giác này theo một thứ tự nào đó để chúng ghép lại với nhau tạo nên một hình thể hoàn chỉnh. Các đối tượng ba chiều phải được phân giải thành tổng thể với các mảnh tam giác để có thể vẽ ra màn hình.

Mỗi điểm riêng biệt trong không gian có màu riêng của nó. Màu của một vùng tam giác phụ thuộc vào màu của 3 điểm tạo nên nó.

Bạn có thể quyết định phạm vi của hệ trục tọa độ. Theo quy ước trục Z có chiều dương hướng về phía bạn, trục X có chiều dương đi về phía tay phải và trục Y có chiều dương hướng lên trên.

Các phép biến đổi cơ sở có sẵn gồm có phép quay và phép tịnh tiến. Các phép biến đổi này được thực thi trước khi bạn vẽ các điểm của mình lên màn hình.

Tại mỗi thời điểm bạn có một ma trận trạng thái hiện thời. Khi ta thực thi một phép biến đổi tức là ta nhân ma trận trạng thái hiện thời với ma trận biến đổi và lưu kết quả ở ma trận trạng thái hiện thời. Khi vẽ thì các tọa độ điểm vẽ sẽ được biến đổi thông qua ma trận trạng thái hiện thời. Và sẽ có một ngăn xếp lưu giữ các ma trận hiện thời để lưu lại các trạng thái tạm thời, điều này cho phép bạn có thể quay về ma trận đơn vị ban đầu.

Một khái niệm khác nữa đó là các Texture, đó là những bức ảnh mà có thể được áp vào một bề mặt hình học nào đó, OpenGL ES sẽ uốn cong hoặc làm gập chúng lại để cho thích hợp với bề mặt đó. Điều này cũng tương tự như việc bạn mặc một bộ quần áo vậy, cơ thể bạn là bề mặt còn quần áo chính là các texture. Với texture bạn dễ dàng tạo ra các bề mặt giống như mình mong muốn, tuy nhiên nó sẽ chiếm nhiều bộ nhớ hơn so với việc dùng màu sắc đơn thuần.

Vì bề mặt hiển thị của màn hình là mặt phẳng 2 chiều, do đó để hiển thị các đối tượng 3 chiều bạn phải sử dụng đến các phép chiếu. Có 2 loại phép chiếu cơ bản, đó là phép chiếu trực giao và phép chiếu phối cảnh. Phép chiếu trực giao cho kết quả nhanh hơn vì chi phí

Page 27: Some Tips of Object C

tính toán ít nhưng lại không mô tả đối tượng một cách "chân thực", ngược lại phép chiếu phối cảnh cho ta một cái nhìn giống thật hơn nhưng chi phí tính toán nhiều hơn. Tùy vào mục đích ứng dụng mà bạn có thể sử dụng một trong hai phép chiếu này.

OpenGL ES chỉ hỗ trợ texture 2D, nhưng bạn vẫn có thể sử dụng nhiều texture một lần, có thể dùng thêm các hiệu ứng như là sương mù, phản chiếu, trong suốt,...

Bạn chú rằng, mỗi phiên bản OpenGL ES tương ứng với một phiên bản OpenGL. Ví dụ, OpenGL ES 1.0 là tập con của OpenGL 1.3, OpenGL ES 1.1 là tập con của OpenGL 1.5,...

đây là template danh cho Xcode

http://innerloop.biz/code/Empty%20Op...pplication.zip

để tích hợp vào Xcode bạn giải nén ra và copy vào đường dẫn

/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application/Window-Based Application

và bây giờ, sau khi khơi động Xcode, bạn có sẽ 1 template có tên là: Empty OpenGL Application

Sau đây mình sẽ giới thiệu 1 chương trình OpenGL ES đơn giản được convert từ lession 2 nổi tiếng của Nehe

NeHe_Lesson_02AppDelegate.h

PHP Code:////  NeHe_Lesson_02AppDelegate.h//  NeHe Lesson 02////  Created by Jeff LaMarche on 12/11/08.//  Copyright Jeff LaMarche Consulting 2008. All rights reserved.//

#import "GLView.h"

@interface NeHe_Lesson_02AppDelegate : NSObject <UIApplicationDelegate, GLTriangleViewDelegate>{    UIWindow*                window;}@property (nonatomic, retain) IBOutlet UIWindow *window;@end  

PHP Code:////  NeHe_Lesson_02AppDelegate.m//  NeHe Lesson 02

Page 28: Some Tips of Object C

////  Created by Jeff LaMarche on 12/11/08.//  Copyright Jeff LaMarche Consulting 2008. All rights reserved.//

// This is a port of the code from// http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=02//// Because OpenGL ES doesn't support glBegin or glEnd, this code// uses vertex arrays, which the NeHe tutorials don't cover until // much later, but we have no choice.

#import "NeHe_Lesson_02AppDelegate.h"#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)#define kRenderingFrequency 60.0

@implementation NeHe_Lesson_02AppDelegate@synthesize window;

- (void)drawView:(GLView*)view;{    const GLfloat triVertices[] = {         0.0f, 1.0f, 0.0f,         -1.0f, -1.0f, 0.0f,         1.0f, -1.0f, 0.0f     };     const GLfloat quadVertices[] = {        -1.0f,  1.0f, 0.0f,        1.0f,  1.0f, 0.0f,        1.0f, -1.0f, 0.0f,        -1.0f, -1.0f, 0.0f            };    glClear(GL_COLOR_BUFFER_BIT);     glLoadIdentity();     glTranslatef(-2.0f,1.0f,-6.0f);    glEnableClientState(GL_VERTEX_ARRAY);    glVertexPointer(3, GL_FLOAT, 0, triVertices);     glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);     glTranslatef(4.0f, 0.0f, 0.0f);    glVertexPointer(3, GL_FLOAT, 0, quadVertices);    glDrawArrays(GL_TRIANGLE_FAN, 0, 4); }

-(void)setupView:(GLView*)view{    const GLfloat zNear = 0.1,     zFar = 1000.0,     fieldOfView = 60.0;     GLfloat size;         glMatrixMode(GL_PROJECTION);     size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);     CGRect rect = view.bounds;     glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size /                (rect.size.width / rect.size.height), zNear, zFar); 

Page 29: Some Tips of Object C

    glViewport(0, 0, rect.size.width, rect.size.height);     glMatrixMode(GL_MODELVIEW);     glLoadIdentity();     glClearColor(0.0f, 0.0f, 0.0f, 1.0f); }- (void)applicationDidFinishLaunching:(UIApplication*)application{    CGRect    rect = [[UIScreen mainScreen] bounds];        window = [[UIWindow alloc] initWithFrame:rect];        GLView *glView = [[GLView alloc] initWithFrame:rect];    [window addSubview:glView];

    glView.delegate = self;    glView.animationInterval = 1.0 / kRenderingFrequency;    [glView startAnimation];    [glView release];        [window makeKeyAndVisible];    

}

- (void)dealloc{    [window release];    [super dealloc];}@end  

PHP Code:////  GLView.h//  NeHe Lesson 02//Lớp GLivew được sinh ra kế thừa từ lớp của UItoolKit để kết nối giữa OpenGL và UIToolkit//

#import <UIKit/UIKit.h>#import <OpenGLES/EAGL.h>#import <OpenGLES/ES1/gl.h>#import <OpenGLES/ES1/glext.h>

@protocol GLTriangleViewDelegate;

@interface GLView : UIView//kế thừa lớp UIView để hiện thị giao diện OpenGL{    @private    // The pixel dimensions of the backbuffer    GLint backingWidth;    GLint backingHeight;        EAGLContext *context;    GLuint viewRenderbuffer, viewFramebuffer;    GLuint depthRenderbuffer;    NSTimer *animationTimer;

Page 30: Some Tips of Object C

    NSTimeInterval animationInterval;

    id<GLTriangleViewDelegate> delegate;    BOOL delegateSetup;}

@property(nonatomic, assign) id<GLTriangleViewDelegate> delegate;

-(void)startAnimation;-(void)stopAnimation;-(void)drawView;

@property NSTimeInterval animationInterval;

@end

@protocol GLTriangleViewDelegate<NSObject>

@required-(void)drawView:(GLView*)view;

@optional-(void)setupView:(GLView*)view;@end  

PHP Code:////  GLView.m//  NeHe Lesson 02////  Created by Jeff LaMarche on 12/11/08.//  Copyright Jeff LaMarche Consulting 2008. All rights reserved.//

#import <QuartzCore/QuartzCore.h>#import <OpenGLES/EAGLDrawable.h>

#import "GLView.h"

@interface GLView (private)

- (id)initGLES;- (BOOL)createFramebuffer;- (void)destroyFramebuffer;

@end

@implementation GLView

@synthesize animationInterval;+ (Class) layerClass{    return [CAEAGLLayer class];}-(id)initWithFrame:(CGRect)frame{

Page 31: Some Tips of Object C

    self = [super initWithFrame:frame];    if(self != nil)    {        self = [self initGLES];    }    return self;}

- (id)initWithCoder:(NSCoder*)coder{    if((self = [super initWithCoder:coder]))    {        self = [self initGLES];    }        return self;}

-(id)initGLES{    CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;        // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color.    eaglLayer.opaque = YES;    eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:                                        [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,                                        kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,                                        nil];        // Create our EAGLContext, and if successful make it current and create our framebuffer.    context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];    if(!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer])    {        [self release];        return nil;    }        // Default the animation interval to 1/60th of a second.    animationInterval = 1.0 / 60.0;    return self;}

-(id<GLTriangleViewDelegate>)delegate{    return delegate;}

// Update the delegate, and if it needs a -setupView: call, set our internal flag so that it will be called.-(void)setDelegate:(id<GLTriangleViewDelegate>)d{    delegate = d;    delegateSetup = ![delegate respondsToSelector:@selector(setupView:)];

Page 32: Some Tips of Object C

}

// If our view is resized, we'll be asked to layout subviews.// This is the perfect opportunity to also update the framebuffer so that it is// the same size as our display area.-(void)layoutSubviews{    [EAGLContext setCurrentContext:context];    [self destroyFramebuffer];    [self createFramebuffer];    [self drawView];}

- (BOOL)createFramebuffer{    // Generate IDs for a framebuffer object and a color renderbuffer    glGenFramebuffersOES(1, &viewFramebuffer);    glGenRenderbuffersOES(1, &viewRenderbuffer);        glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);    // This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer)    // allowing us to draw into a buffer that will later be rendered to screen whereever the layer is (which corresponds with our view).    [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);        // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer.    glGenRenderbuffersOES(1, &depthRenderbuffer);    glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);

    if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)    {        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));        return NO;    }        return YES;}

// Clean up any buffers we have allocated.

Page 33: Some Tips of Object C

- (void)destroyFramebuffer{    glDeleteFramebuffersOES(1, &viewFramebuffer);    viewFramebuffer = 0;    glDeleteRenderbuffersOES(1, &viewRenderbuffer);    viewRenderbuffer = 0;        if(depthRenderbuffer)    {        glDeleteRenderbuffersOES(1, &depthRenderbuffer);        depthRenderbuffer = 0;    }}

- (void)startAnimation{    animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];}

- (void)stopAnimation{    [animationTimer invalidate];    animationTimer = nil;}

- (void)setAnimationInterval:(NSTimeInterval)interval{    animationInterval = interval;        if(animationTimer)    {        [self stopAnimation];        [self startAnimation];    }}

// Updates the OpenGL view when the timer fires- (void)drawView{    // Make sure that you are drawing to the current context    [EAGLContext setCurrentContext:context];        // If our drawing delegate needs to have the view setup, then call -setupView: and flag that it won't need to be called again.    if(!delegateSetup)    {        [delegate setupView:self];        delegateSetup = YES;    }        glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

    [delegate drawView:self];        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);    [context presentRenderbuffer:GL_RENDERBUFFER_OES];

Page 34: Some Tips of Object C

        GLenum err = glGetError();    if(err)        NSLog(@"%x error", err);}

// Stop animating and release resources when they are no longer needed.- (void)dealloc{    [self stopAnimation];        if([EAGLContext currentContext] == context)    {        [EAGLContext setCurrentContext:nil];    }        [context release];    context = nil;        [super dealloc];}

@end  

Dưới đây, tôi sẽ hướng dẫn các bạn cách porting 1 source code OpenGL sang OpenGL ESBạn nên đọc kỹ bài hướng dẫn của nehe,nhưng bạn cũng nên chú ý là kỹ thuật vẽ của nehe sử dung glBegin và GlEnd là không được hỗ trợ trong OpenGL ES

Trong code của NehePHP Code:glBegin(GL_TRIANGLES);     // Drawing Using Triangles  glVertex3f( 0.0f, 1.0f, 0.0f);  // Top  glVertex3f(-1.0f,-1.0f, 0.0f);  // Bottom Left  glVertex3f( 1.0f,-1.0f, 0.0f);  // Bottom RightglEnd();      // Finished Drawing The Triangle  Trích dẫn:The call to glBegin() tells OpenGL that the code that follows is going to define points (or vertices) in virtual space. Then the three calls to glVertex3f define the vertices using cartesian coordinates (x, y, z). Because GL_TRIANGLES was specified in glBegin(). OpenGL will draw and fill the triangle defined by the three vertices specified before the call to glEnd()Trên Iphone, thay thế vào việc đó, chúng ta vẽ bằng cách tổ chứng chúng thành 1 mảng có 3 phần tử, ứng với 3 điểmPHP Code:const GLfloat triVertices[] = {   0.0f, 1.0f, 0.0f,   -1.0f, -1.0f, 0.0f,   1.0f, -1.0f, 0.0f  };  sau đó, để định dạng cách vẽ, chúng ta gọi hàm sau

Code:

glVertexPointer(3, GL_FLOAT, 0, triVertices);

Hàm này sẽ định nghĩa bién triVerrtices là 1 mảng có kiểu chóp, nghĩa là các phần tử của nó

Page 35: Some Tips of Object C

sẽ có dạng (x, y, z), sẽ là các đỉnh của 1 hình 2D ( ví dụ như tam giác, hình vuông... ), tham số GL_FLOAT nghĩa là chúng ta đã thông báo rằng các dữ lieeuj trong đó sẽ có kiểu GLfloat, tham số thứ 3 không sử dụng, nó sẽ là 0

và cuối cùng, để vẽ nó lên màn hình, chúng ta gọi hàm glDrawArrays(GL_TRIANGLES, 0, 3);Các Thumbnail đã đính kèm

  

[Objective C] Hướng dẫn viết Image view đơn giản

Một tutorial đơn giản cho người mới làm quen với Xcode-Objective C

để hiện thị 1 bức ảnh trong Iphone có lớp UIImageview, ta chỉ cần gọi hàm setimage hoặc gán thuộc tính image cho nó là được

dưới đây là 1 đoạn code đơn giản:

Code:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{ NSURL * imageURL = [NSURL URLWithString:@"http://dantri4.vcmedia.vn/xAMQAUs9LhwrT16Af2k/Image/2011/06/NN21811_557b3.jpg"];//lấy đường dẫn URL là link của trang ảnh về NSData * imageData = [NSData dataWithContentsOfURL:imageURL];//convert nó sang kiểu NSData để cuối cùng sẽ đưa sang kiểu UIImage UIImage * image = [UIImage imageWithData:imageData]; imgViewer.image = image;

// Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait);}