1. Khai báo
//Trong file .h
@property (thuộc tính 1, thuộc tính 2,...) <Kiểu dư liệu> <Tên Property>;
//Trong file .m
@synthesize <Tên Property>;//Trong file .m
Ví dụ:
//Trong file .h
@property (nonatomic, assign) NSInteger ten;
//Trong file .m
@synthesize tuSo;
Nếu chia nhóm thì có lẽ bao gồm 3 nhóm thuộc tính như sau:
Writability
Nhóm này có 2 thuộc tính là readwrite và readonly. Nhóm thuộc tính này thì khá là dễ hiểu. Với thuộc tính readwrite thì compiler sẽ generate ra cả setter và getter, còn readonly thì compiler chỉ generate ra getter. Mặc định là readwrite (không liên quan đến project dùng ARC hay không).
Setter Semantics
Nhóm này gồm các thuộc tính để chỉ ra cách thức quản lý bộ nhớ, bao gồm các thuộc tính như sau: assign, strong, weak,unsafe_unretained, retain, copy. Khi chúng ta set một trong các thuộc tính này cho property thì setter (getter không liên quan) được tạo ra thay đổi tương ứng với thuộc tính đó. Trước hết chúng ta sẽ nói qua về cách quản lý bộ nhớ trước iOS5 khi mà ARC chưa xuất hiện.
Trước khi ARC xuất hiện thì các lập trình viên iOS đều phải tự quản lý bộ nhớ. Khi chúng ta tạo object với vùng nhớ của nó, đồng nghĩa với việc chúng ta nắm giữ ownership của object đó. Khi không cần dùng nữa thì phải huỷ bỏ ownership đấy đi bằng cách gửi message release. Một object có thể có nhiều ownership và mỗi object sẽ có 1 property tên là retainCount để lưu số lượng owner của nó. Mỗi khi chúng ta tạo object, hay retain thì retainCount lại được tăng lên 1. Khi chúng ta gửi messagerelease tới object đấy thì retainCount lại bị giảm đi 1. Một khi retainCount bằng 0 thì vùng nhớ của nó sẽ bị giải phóng. Chúng ta có thể gửi message retain để tạo thêm ownership như ví dụ dưới đây. Khi đó car1 và car2 cùng trỏ đến 1 vùng nhớ và retainCount bây giờ bằng 2.
Ngoài ra để copy sang vùng nhớ mới chúng ta có thể gửi message copy như ví dụ dưới đây. Khi đó retainCount ở vùng nhớ mới có giá trị khởi tạo là 1.
Quay trở lại với thuộc tính của property. Thuộc tính đầu tiên là retain. Như ví dụ dưới đây khi ta set thuộc tính retain cho property name thì compiler sẽ sinh ra setter setName như bên dưới.
Nhìn vào setter ta thấy đầu tiên là tạo ownership (hay tăng retainCount thêm 1) của newName bằng cách gọi [newNmane retain]. Tiếp theo là việc gửi message release tới _name ban đầu để xoá ownership ban đầu đi. Sau đó mới gán contrỏ trỏ đến object mới. Vậy nên thuộc tính retain giúp tạo ra setter trong đó tạo ownership mới và trỏ đến vùng nhớ mới. Chú ý rằng thuộc tính retain chỉ dùng cho những project không dùng ARC.
Và từ iOS5 trở đi Apple giới thiệu ARC giúp cho việc quản lý bộ nhớ đơn giản hơn. ARC không hoạt động như các Garbage Collection khác mà thực ra chỉ là phần front-end của compiler nhằm mục đich tự động chèn thêm các đoạn code gọi message như retain hay release. Từ đấy lập trình viên không phải gọi các message này nữa. Ví dụ như 1 object được tạo trong 1 method thì sẽ chèn thêm đoạn gửi message release tới object đó ở gần cuối method. Hay trong trường hợp là property của 1 class Car ở trên thì tự động chèn [_name release] trong method dealloc của class Car chẳng hạn.
Khi project của bạn dùng ARC thì chúng ta sẽ dùng thuộc tính strong thay cho thuộc tính retain. strong cũng tương tự nhưretain sẽ giúp tạo ra setter, mà trong setter đó tạo ra ownership mới (tăng retainCount thêm 1). Và ngoài ra ARC sẽ thêm các đoạn gửi message release tới các property này trong method dealloc của class.
Thế nhưng xuất hiện vấn đề có tên là Strong Reference Cycles. Mình sẽ lấy 1 ví dụ để thấy rõ hơn về vấn đề này. Một object A nào đấy có ownership của 1 object B. Object B lại có ownership của 1 object C. Object C lại có ownership của object B.
Một khi object A ko cần thiết nữa thì trong method dealloc của A sẽ gửi message release tới object B. retainCount của object B giảm đi 1 nhưng vẫn còn 1 ( do object C retain ) thế nên method dealloc của object B không bao giờ được gọi, kéo theo message release cũng không bao giờ được gửi tới object C. Từ đó dẫn đến vùng nhớ của object B và object C không được giải phóng => xuất hiện hiện tượng Leak Memory. Vì vậy để tránh hiện tượng này ta sẽ dùng thuộc tính weak thay vì dùng thuộc tínhstrong trong class của object C. Với thuộc tính weak thì trong setter được sinh ra sẽ không retain (không tăng retainCount thêm 1) mà chỉ đơn thuần gán con trỏ trỏ đến vùng nhớ mới. Thuộc tính weak cũng chỉ dùng trong trường hợp bạn đang dùng ARC. Và một cái hay của weak nữa là khi vùng nhớ bị giải phóng thì con trỏ được set bằng nil. Mà trong Objective C thì gửi message đến nil sẽ không vấn đề gì, app của bạn không bị crash. Điển hình nhất của việc dùng thuộc tính weak đó là cho cácdelegate, datasource.
Tuy nhiên vẫn còn một vài class như NSTextView, NSFont, NSColorSpace chưa hỗ trợ khai báo thuộc tính weak nên với những class này bạn có thể dùng thuộc tính unsafe_unretained thay cho weak. Thế nhưng chú ý 1 điều rằng sau khi vùng nhớ nó trỏ tới bị xoá thì con trỏ không được set la nil.
Tiếp theo là thuộc tính copy. Với việc thiết lập thuộc tính này compiller sẽ tạo ra setter như sau:
Như ở trên ta thấy 1 vùng nhớ mới được copy ra và _name giờ chiếm giữ 1 ownership của vùng nhớ đó. Tại sao chúng ta không dùng strong ở đây mà lại dùng copy. Giả sử ở trên chúng ta dùng thuộc tính strong và xem qua 2 ví dụ dưới đây.
Trong trường hợp này car1.name vẫn có giá trị là “Toyota” và name1 giờ chuyển thành “Honda”. Hoàn toàn không có vấn đề gì. Thế nhưng trong ví dụ thứ 2 dưới đây thay vì dùng NSString mà dùng subclass của nó là NSMutableString.
Trong trường hợp này giá trị của car1.name là “Toyota2” mặc dù ban đầu chúng ta set là “Toyota”. Vì vậy mặc dù property nametrong class Car với kiểu NSString nhưng nếu dùng strong giá trị của name vẫn có thể bị append như trên. Để tránh những trường hợp như thế ta dùng copy để mỗi lần gán sẽ copy 1 vùng nhớ mới tránh được những trường hợp như trên. Đối với những class có subclass là Mutable... thì chúng ta nên chú ý dùng thuộc tính copy. Ngoài ra block cũng phải dùng copy.
Thuộc tính cuối cùng trong nhóm này là assign thì dùng cho các property kiểu không phải là object. Tức là các kiểu dữ liệu nhưint, NSInteger, float,…
Với nhóm thuộc tính này thì strong là thuộc tính mặc định trong trường hợp dùng ARC, còn retain là thuộc tính mặc định trong trường hợp không dùng ARC.
II.QUẢN LÝ BỘ NHỚ TRONG OBJECTIVE-C

V.Objective-C – Singleton Pattern
Ghi chú: Dùng từ khóa synthesize để trình biên dịch tự động tạo hai hàm getter và setter (trong điều kiện là ta chưa viết 2 hàm này), nếu ta đã tự viết các hàm setter và getter sẵn rồi thì trình biên dịch sẽ không tạo thêm getter và setter nữa.
2. Truy cập
<Tên Object>.<Tên Property>;
Ví dụ:
phanSoA.tuSo; //Get
phanSoA.tuSo = 2; //Set
Tương đương với:
[phanSoA tuSo]; //Get
[phanSoA setTuSo:2]; //Set
3. Các thuộc tính
Các thuộc tính của propertyNếu chia nhóm thì có lẽ bao gồm 3 nhóm thuộc tính như sau:
Writability
Nhóm này có 2 thuộc tính là readwrite và readonly. Nhóm thuộc tính này thì khá là dễ hiểu. Với thuộc tính readwrite thì compiler sẽ generate ra cả setter và getter, còn readonly thì compiler chỉ generate ra getter. Mặc định là readwrite (không liên quan đến project dùng ARC hay không).
-readwrite: property có thể đọc và ghi vào biến thể hiện, đây là thuộc tính mặc định.
-read-only: property chỉ có thể đọc.
Nhóm này gồm các thuộc tính để chỉ ra cách thức quản lý bộ nhớ, bao gồm các thuộc tính như sau: assign, strong, weak,unsafe_unretained, retain, copy. Khi chúng ta set một trong các thuộc tính này cho property thì setter (getter không liên quan) được tạo ra thay đổi tương ứng với thuộc tính đó. Trước hết chúng ta sẽ nói qua về cách quản lý bộ nhớ trước iOS5 khi mà ARC chưa xuất hiện.
-assign: không cần giữ lại tham số truyền vào trước khi set, dùng cho các kiểu dữ liệu vô hướng, đây là thuộc tính mặc định.
-strong: tham chiếu mạnh đến tham số truyền vào, là thuộc tính mặc định.
-weak: tham chiếu yếu đến tham số truyền vào.
-strong: tham chiếu mạnh đến tham số truyền vào, là thuộc tính mặc định.
-weak: tham chiếu yếu đến tham số truyền vào.
-retain: giữ lại tham số truyền vào trước khi set, dùng cho kiểu dữ liệu là các lớp đối tượng.
-copy: tạo một bản sao của tham số truyền vào trước khi set, thường dùng khi là lớp NSString.
1 2 3 |
|
1 2 |
|
1 2 |
|
1 2 3 4 5 |
|
1 2 3 4 5 |
|
Nhìn vào setter ta thấy đầu tiên là tạo ownership (hay tăng retainCount thêm 1) của newName bằng cách gọi [newNmane retain]. Tiếp theo là việc gửi message release tới _name ban đầu để xoá ownership ban đầu đi. Sau đó mới gán contrỏ trỏ đến object mới. Vậy nên thuộc tính retain giúp tạo ra setter trong đó tạo ownership mới và trỏ đến vùng nhớ mới. Chú ý rằng thuộc tính retain chỉ dùng cho những project không dùng ARC.
Và từ iOS5 trở đi Apple giới thiệu ARC giúp cho việc quản lý bộ nhớ đơn giản hơn. ARC không hoạt động như các Garbage Collection khác mà thực ra chỉ là phần front-end của compiler nhằm mục đich tự động chèn thêm các đoạn code gọi message như retain hay release. Từ đấy lập trình viên không phải gọi các message này nữa. Ví dụ như 1 object được tạo trong 1 method thì sẽ chèn thêm đoạn gửi message release tới object đó ở gần cuối method. Hay trong trường hợp là property của 1 class Car ở trên thì tự động chèn [_name release] trong method dealloc của class Car chẳng hạn.
1 2 3 4 5 6 |
|
Khi project của bạn dùng ARC thì chúng ta sẽ dùng thuộc tính strong thay cho thuộc tính retain. strong cũng tương tự nhưretain sẽ giúp tạo ra setter, mà trong setter đó tạo ra ownership mới (tăng retainCount thêm 1). Và ngoài ra ARC sẽ thêm các đoạn gửi message release tới các property này trong method dealloc của class.
Thế nhưng xuất hiện vấn đề có tên là Strong Reference Cycles. Mình sẽ lấy 1 ví dụ để thấy rõ hơn về vấn đề này. Một object A nào đấy có ownership của 1 object B. Object B lại có ownership của 1 object C. Object C lại có ownership của object B.
Một khi object A ko cần thiết nữa thì trong method dealloc của A sẽ gửi message release tới object B. retainCount của object B giảm đi 1 nhưng vẫn còn 1 ( do object C retain ) thế nên method dealloc của object B không bao giờ được gọi, kéo theo message release cũng không bao giờ được gửi tới object C. Từ đó dẫn đến vùng nhớ của object B và object C không được giải phóng => xuất hiện hiện tượng Leak Memory. Vì vậy để tránh hiện tượng này ta sẽ dùng thuộc tính weak thay vì dùng thuộc tínhstrong trong class của object C. Với thuộc tính weak thì trong setter được sinh ra sẽ không retain (không tăng retainCount thêm 1) mà chỉ đơn thuần gán con trỏ trỏ đến vùng nhớ mới. Thuộc tính weak cũng chỉ dùng trong trường hợp bạn đang dùng ARC. Và một cái hay của weak nữa là khi vùng nhớ bị giải phóng thì con trỏ được set bằng nil. Mà trong Objective C thì gửi message đến nil sẽ không vấn đề gì, app của bạn không bị crash. Điển hình nhất của việc dùng thuộc tính weak đó là cho cácdelegate, datasource.
Tuy nhiên vẫn còn một vài class như NSTextView, NSFont, NSColorSpace chưa hỗ trợ khai báo thuộc tính weak nên với những class này bạn có thể dùng thuộc tính unsafe_unretained thay cho weak. Thế nhưng chú ý 1 điều rằng sau khi vùng nhớ nó trỏ tới bị xoá thì con trỏ không được set la nil.
Tiếp theo là thuộc tính copy. Với việc thiết lập thuộc tính này compiller sẽ tạo ra setter như sau:
1 2 3 4 5 |
|
1 2 3 4 |
|
1 2 3 |
|
1 2 3 |
|
Trong trường hợp này giá trị của car1.name là “Toyota2” mặc dù ban đầu chúng ta set là “Toyota”. Vì vậy mặc dù property nametrong class Car với kiểu NSString nhưng nếu dùng strong giá trị của name vẫn có thể bị append như trên. Để tránh những trường hợp như thế ta dùng copy để mỗi lần gán sẽ copy 1 vùng nhớ mới tránh được những trường hợp như trên. Đối với những class có subclass là Mutable... thì chúng ta nên chú ý dùng thuộc tính copy. Ngoài ra block cũng phải dùng copy.
Thuộc tính cuối cùng trong nhóm này là assign thì dùng cho các property kiểu không phải là object. Tức là các kiểu dữ liệu nhưint, NSInteger, float,…
Với nhóm thuộc tính này thì strong là thuộc tính mặc định trong trường hợp dùng ARC, còn retain là thuộc tính mặc định trong trường hợp không dùng ARC.
Atomicity
Nhóm thuộc tính này bao gồm 2 thuộc tính là atomic và nonatomic. Thuộc tính mặc định là atomic. Nhóm thuộc tính này liên quan đến vấn đề multithread. Chưa bàn đến atomic hay nonatomic, mà chúng ta cùng xem ví dụ sau:
khi đấy chúng ta có setter/getter như sau:
và bởi vì struct CGPoint có 2 thành phần CGFloat x, CGFloat y nên thực ra setter sẽ thực hiện các bước như sau:
Trong trường hợp chúng ta chạy multithread thì có thể xảy ra khả năng như sau:
Như trường hợp ở trên ta thấy giá trị center là (1.f, 2.f) nhưng tại thread 2 giá trị lấy được lại là (1.f, -8.f) dẫn đến kết quả không được như mong muốn. Vì vậy trong trường hợp multithread để tránh những tình huống như trên ta set thuộc tính atomic cho property. Khi đấy compiler sẽ sinh ra các setter/getter như sau:
Bên trong setter/getter sử dụng lock để tránh việc nhiều thread truy cập đồng thời. Thế nhưng việc dùng lock sẽ mất chi phí cũng như cản trở tốc độ của chương trình. Vì vậy nên trong trường hợp bạn không dùng multithread hoặc không thể xảy ra những vấn đề như trên thì bạn nên dùng thuộc tính nonatomic để tăng tốc độ cho chương trình.
1 2 3 4 5 6 |
|
1 2 3 4 5 6 7 |
|
1 2 3 4 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
-automic: kiểm tra đảm bảo đồng bộ khi nhiều tiểu trình cùng truy cập, là thuộc tính mặc định.
-nonatomic: không kiểm tra nếu nhiều tiểu trình cùng truy cập, nhanh hơn automic.
4. Mẹo sử dụng
a. Nên khai báo property cho biến thể hiện, và đặt tên cho property khác với biến thể hiện theo quy tắc sau :
//Trong file .h
@interface MyClass : NSObject {
PhanSo* _phanSo;
}
@property (nonatomic, retain) PhanSo* phanSo;
@end
//Trong file .m
@implementation MyClass
@synthesize phanSo = _phanSo;
@end
b. Nên sử dụng self.property để truy cập property thay vì truy cập biến thể hiện.
c. Property nào có thuộc tính retain hoặc copy đều cần phải giải phóng trong hàm dealloc :
- (void) dealloc {
self.phanSo = nil;
[super dealloc];
}
Ghi chú : Ở cách trên(self.phanSo = nil), trong hàm setter sẽ xảy ra ba việc, một là giữ lại đối tượng nil (thật ra giống như chẳng làm gì), hai là release giải phóng biến thể hiện cũ đi, và ba là gán biến mới bằng nil, cách giải phóng trên sẽ ngắn gọn hơn so với cách không dùng property là :
- (void) dealloc {
[_phanSo release];
_phanSo = nil;
[super dealloc];
}
d. Trong quá trình sử dụng property tránh sử dụng câu lệnh sau :
self.phanSo = [[PhanSo alloc] init];
Nguyên nhân: do khi thực hiện câu lệnh trên ta sở hữu đối tượng tới 2 lần, retain count = 2, và như vậy là không cần thiết và phung phí bộ nhớ.
Giải pháp :
Giải pháp :
+ Vẫn sử dụng property :
self.phanSo = [[[PhanSo alloc] init] autorelease];
+ Sử dụng biến thể hiện :
_phanSo = [[PhanSo alloc] init];
II.QUẢN LÝ BỘ NHỚ TRONG OBJECTIVE-C
1. Quản lý bộ nhớ là gì ?
Quản lý bộ nhớ là quản lý việc khởi tạo vùng nhớ cho ứng dụng, sử dụng nó và giải phóng nó khi ta đã hoàn thành công việc. Một chương trình quản lý bộ nhớ tốt sẽ sử dụng ít vùng nhớ nhất có thể.
2. Các phương pháp quản lý bộ nhớ trong Objective-C
Objective-C cung cấp 3 cách quản lý bộ nhớ :
• Manual Reference Counting (MRC) : quản lý bộ nhớ thủ công, có nghĩa là chúng ta sẽ tự quản lý bộ nhớ bằng cách đánh dấu, theo vết vòng đời của object. Cụ thể là ta sẽ đếm việc khởi tạo, sử dụng và giải phóng object trong chương trình.
• Autiomatic Reference Counting (ARC) : quản lý bộ nhớ tự động, hệ thống sẽ dùng bộ đếm tương tự như MRC nhưng nó sẽ thêm vào phương thức quản lý bộ nhớ tự động tại thời điểm compile.
• Garbage Collection (GC) : hệ thống sẽ tự động lưu vết và tự động giải phóng vùng nhớ của object không cần thiết nữa. Nó sử dụng kỹ thuật quản lý bộ nhớ khác với MRC và ARC, tuy nhiên GC chỉ hỗ trợ Mac OS X mà không hỗ trợ iOS.
3. Reference Counting
• Reference Counting(hoặc Retain Counting) : là một kỹ thuật quản lý bộ nhớ của Objective-C nhằm theo vết vòng đời của đối tượng(object), đếm số lượng tham chiếu đến một đối tượng.
• Reference Count(hoặc Retain Count) : số lượng tham chiếu đến một đối tượng.
• Khi đối tượng được khởi tạo bằng các từ khóa như alloc, new, copy thì reference count của nó sẽ tự động là 1.
• Để tăng reference count lên 1, ta gửi thông điệp retain cho đối tượng.
• Để giảm reference count xuống 1, ta gửi thông điệp release cho đối tượng.
• Để có thể biết được reference count hiện tại, ta gửi thông điệp retainCount cho đối tượng.
• Khi reference count giảm xuống 0 thì đối tượng sẽ được hủy, vùng nhớ được giải phóng, lúc này hệ thống sẽ tự động gửi thông điệp dealloc cho đối tượng.
4. Từ khóa và ví dụ
• alloc hoặc new
Mục đích :
Cấp phát bộ nhớ cho đối tượng.
Reference count : bằng 1
Ví dụ:
NSString *str = [[NSString alloc] init];
NSString *str = [NSString new];
• copy
Mục đích :
Cấp phát bộ nhớ cho đối tượng thông qua một đối tượng khác.
Reference count : bằng 1
Ví dụ:
NSString *str = [someString copy];
• retain
Mục đích :
Gọi khi ta cần sử dụng một đối tượng, mà đối tượng đó đã được cấp phát ở đâu đó, ta không chắc là tại thời điểm ta sử dụng thì đối tượng đó có bị release hay không. Gọi retain giống như ta thông báo cho chủ sở hữu của nó là “Đừng xóa nó cho đến khi tôi sử dụng nó xong nhé !”. Khi gọi retain thì lúc này chúng ta là chủ sở hữu mới của nó(bên cạnh chủ sở hữu cũ). Khi nào sử dụng xong ta cần phải release nó.
Reference count : tăng thêm 1
Ví dụ:
NSString *str = [[NSString alloc] initWithString:@”Hello”];
NSString *str2 = str;
[str release];
//Sau vài giây thì str2 không còn lưu đúng nội dung
Trong ví dụ trên, biến str2 chỉ đơn giản là trỏ đến vùng nhớ của biến str, một khi ta giải phóng biến str thì biến str2 sẽ lưu không còn đúng nội dung nữa. Giải pháp :
NSString *str = [[NSString alloc] initWithString:@”Hello”];
NSString *str2 = [str retain];
[str release];
//str2 vẫn còn lưu đúng nội dung cho tới khi ta giải phóng nó
[str2 release];
• release
Mục đích:
Báo cho hệ thống biết là ta đã sử dụng xong 1 đối tượng nào đó và cần giải phóng nó. Đối tượng sẽ thực sự được giải phóng chỉ khi không còn ai sở hữu nó nữa (reference count = 0)
Reference count : Giảm xuống 1 ngay lập tức.
• autorelease
Mục đích:
- Khi ta không muốn giải phóng một đối tượng nào đó ngay lập tức.
- Khi ta không muốn phải ghi nhớ lúc nào nên release những đối tượng mà ta đã cấp phát khi sử dụng xong.
- Khi ta muốn thêm một đối tượng vào autorelease pool.
Reference count : Giảm xuống 1 nhưng không ngay lập tức.
• Convenience methods
Khái niệm :
Là những phương thức static, dùng để cấp phát và khởi tạo đối tượng một cách trực tiếp. Đối tượng được tạo ra từ phương thức convenience gọi là autorelease object, và ta không sở hữu đối tượng này.
NSString *str2 = [NSString stringWithFormat:@”%d”, 4];
- Tất cả các autorelease objects được tạo ra sẽ được thêm vào autorelease pool hiện tại.
• Autorelease Pool
Khái niệm :
Là nơi chứa và giải phóng các autorelease objects . Khi autorelease pool được giải phóng (thường là hết vòng lặp hay kết thúc hàm) thì các object nằm bên trong nó cũng sẽ được tự động giải phóng.
Khi nào nên sử dụng :
- Khi ta muốn tự hủy một loạt các autorelease objects.
- Trong trường hợp cụ thể khi ta sử dụng nhiều biến tạm và muốn tránh phải cấp phát và release tất cả những biến này khi sử dụng xong.
- Hoặc ta không muốn đợi autorelease pool của hệ thống được giải phóng, ta sẽ sử dụng đối tượng thuộc lớpNSAutoreleasePool
for (int i = 0; i < 99999; i++){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *str1 = [NSString stringWithFormat:@”%d”, i];
NSString *str2 = [NSString stringWithFormat:@”%d”, i];
//...
[pool release];//Các autorelease objects ở trên sẽ đc giải phóng
}
Như ví dụ trên đây, nếu như ta không dùng lớp NSAutoreleasePool thì khi chạy hết 99999 vòng lặp, cácautorelease objects nằm trong autorelease pool mới được giải phóng, và điều đó sẽ gây ra hiện tượng tràn bộ nhớ.
• Hệ quả từ Autorelease Pool
Khi trong hàm ta sử dụng nhiều biến được cấp phát, và không muốn suy nghĩ lúc nào nên release biến này, lúc nào nên release biến kia, ta chỉ cần kẹp đoạn code giữa NSAutoreleasePool , rồi cho biến đó autorelease, sau đó ta vẫn sử dụng nó được bình thường.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Animal *animal = [[Animal alloc] initWithName:@"Animal"];
[animal autorelease];
Zoo *zoo = [[Zoo alloc] init];
[zoo autorelease];
[zoo add:animal];
[pool release];
5. Các mẹo quản lý bộ nhớ
• Khi nào thì mới thực sự sở hữu một đối tượng :
Khi ta cấp phát bộ nhớ cho đối tượng bằng các từ khóa : alloc, new, copy, retain
• Các luật cơ bản :
- Nếu sở hữu đối tượng nào thì mới được release đối tượng đó.
- Nếu không sở hữu thì không được release.
- Override lại hàm dealloc trong lớp để release các fields đã sở hữu.
- Không được gọi dealloc trực tiếp.
- Nên sử dụng autorelease ngay sau khi cấp phát và khởi tạo :
self.thing = [[[SomeThing alloc] init] autorelease];
- Nên sử dụng autorelease ngay sau khi cấp phát và khởi tạo :
self.thing = [[[SomeThing alloc] init] autorelease];
• Trong hàm có cấp phát 1 đối tượng, và hàm lại trả về đối tượng đó thì dùng autorelease :
-(NSString *) DTB {
NSString *str = [[NSString alloc] initWithFormat:@”%d, 4];
[str autorelease];
return str;
}
III.THIẾT KẾ ỨNG DỤNG ĐA KÍCH THƯỚC MÀN HÌNH
1.Points và Pixels
- Một point xấp xỉ gần bằng 1/72 inch, kích thước của 1 point là cố định dù trên bất cứ màn hình hay độ phân giải nào.
- Một pixel là một điểm ảnh trên màn hình, kích thước của một pixel sẽ tùy thuộc vào kích thước của màn hình và độ phân giải.
- Hệ tọa độ của iOS sử dụng đơn vị là point.
Chiều rộng của status bar là 20 points dù là ở iPhone 4S hay iPhone
2. Thiết kế ứng dụng đa kích thước màn hình
- Để xác định tọa độ khi thiết kế giao diện, ta chỉ cần quan tâm đến points là đủ
- Ta chỉ quan tâm đến giá trị pixels khi muốn hình ảnh hiển thị sắc nét nhất có thể
Như vậy, để một ứng dụng có thể hiển thị tốt trên nhiều kích cỡ màn hình, nhiều độ phân giải khác nhau ta cần làm như sau
+ Với mỗi hình ảnh, ta cần cung cấp các phiên bản với độ phân giải khác nhau cho ứng dụng dạng Non-retina hoặc Retina hoặc Portrait-Landscape (đối với iPad). Ví dụ đối với file ảnh lúc load ứng dụng Default.png
+ Với mỗi hình ảnh, ta cần cung cấp các phiên bản cho iPhone hoặc iPad.
=>Tổng hợp lại, từ một file ảnh gốc myImage.png ban đầu, ta cần có các phiên bản sau :
Đối với ứng dụng chỉ chạy trên iPhone
a. Không hỗ trợ Retina :
myImage.png
b. Có hỗ trợ Retina :
myImage.png
myImage@2x.png
Đối với ứng dụng chạy trên iPhone lẫn iPad
a. Không hỗ trợ Retina :
myImage~iphone.png
myImage~ipad.png
b. Có hỗ trợ Retina :
myImage~iphone.png
myImage~ipad.png
myImage@2x~iphone.png
myImage@2x ~ipad.png
3. Các phương thức bổ trợ
a. Xác định có thể dùng một lớp nào đó :
Class notificationClass = NSClassFromString(@"UILocalNotification");
if (notificationClass != nil)
{
//
}
b. Xác định có thể dùng một hàm nào đó :
BOOL isScheduleLocalNotificationSupported = [UIApplication
instancesRespondToSelector:@selector(scheduleLocalNotification:)];
if (isScheduleLocalNotificationSupported == YES)
{
//
}
3. Xác định thiết bị là iPhone hay iPad :
UIUserInterfaceIdiom deviceIdiom = [[UIDevice currentDevice]userInterfaceIdiom];
if (deviceIdiom == UIUserInterfaceIdiomPad)
{
//
}
else if (deviceIdiom == UIUserInterfaceIdiomPhone)
{
//
}
c. Xác định thiết bị có hỗ trợ Retina không :
BOOL isScaleSupported = [[UIScreen mainScreen]
respondsToSelector:@selector(scale)];
if (isScaleSupported == YES)
{
CGFloat screenScale = [[UIScreen mainScreen] scale];
if (screenScale == 2.0)
{
//Does Retina display stuff
}
else if (screenScale == 1.0)
{
//Does Non-Retina display stuff
}
}
d. Xác định kích thước màn hình :
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
e. Xác định model thiết bị :
BOOL isWideScreen = (screenSize.height == 568);
BOOL isiPhone = ([[[UIDevice currentDevice] model] isEqualToString:@"iPhone"]);
BOOL isiPhone5 = (isiPhone && isWideScreen);
f. Thay đổi màn hình khi xoay dọc hay xoay ngang :
- (void)awakeFromNib
{
isShowingLandscapeView = NO;
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice
currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) &&
!isShowingLandscapeView)
{
[self performSegueWithIdentifier:@"DisplayAlternateView" sender:self];
isShowingLandscapeView = YES;
}
else if (UIDeviceOrientationIsPortrait(deviceOrientation) &&
isShowingLandscapeView)
{
[self dismissViewControllerAnimated:YES completion:nil];
isShowingLandscapeView = NO;
}
}
}
Nguồn:http://laptrinhiphone.blogspot.com
IV.OBJECTIVE-C Block

Nếu một lần nào đó bạn đọc 1 đoạn code của xcode project, bạn bắt gặp một dòng lệnh “lạ lùng”. Nó có vẻ giống 1 function được khai báo bên trong một fuction khác, văn phạm của nó không giống một phạm trù nào mà bạn đã đọc qua khi học lập trình C, C++ hay Objective-C… Đó là BLOCK.
Block là một mở rộng của ngôn ngữ C, ở Objective-C block được Apple giới thiệu trong lần ra mắt OS X Snow Leopard & iOS 4. Nó giống như Anonymous Function của Javascript, hay Lambda của Python, LISP. Mạnh mẽ và hữu ích. Đối với lập trình đa luồng (multithread) iOS với GCD (Grand Central Dispatch) bạn càng không thể bỏ qua khái niệm này.
Ở bài viết trước về ngôn ngữ ObjC mình có giới thiệu về khái niệm Delegate. Đây là một điểm mạnh, rất hay của ObjC nó cho phép một lớp bất kỳ có thể implement phương thức của lớp nào đó mà nó nhận delegate. Nếu bạn đã từng xem qua UIALertView, UIActionSheet, UITableView ắc hẳn bạn đã có va chạm với Delegate methods của chúng. Những delegate đc được implement trong controller và nhận biết các sự kiện mà chúng tạo ra ngay chính trên controller đó.
Tuy nhiên, có nhiều vấn đề bất cập đối với phương pháp này một trong những vấn đề đó đã được liệt kê ở đây. Sử dụng delegate – protocol làm cho người lập trình mất thời gian hơn, dễ lỗi hơn, mã code sẽ rối reng hơn… bởi vì tính độc lập của đối tượng bị ảnh hưởng. Cụ thể, tất cả các biền Alert đc gán delegate vào controller sẽ tạo ra cách sự kiện trên controller đó – hàm alertDismiss… sẽ được gọi bởi các alert, có nghĩa là bạn sẽ phải tìm cách kiểm ra alert nào đã dẫn đến hàm delegate đó được gọi thực thi…
Block giải quyết vấn đề này rất tuyệt với, trong bài the API return 4 redeemsmình đã có nói sơ về nó.
Sự kiện touch của button trên alertview đc xử lý ngay trong đoạn code, bạn sẽ không phải bận tâm về delegate có được gọi hay không, đối tượng nào đang gọi…
Đây là một trong những lý do nên dùng Block. Xa hơn 1 chút bạn sẽ lập trình với hỗ trợ GCD của iOS/Mac OS ở đó bạn sẽ thấy hầu hết code được gọi dưới dạng các block. ví dụ:
Kinh nghiệm của mình Block thường đc sử dụng trong các trường hợp cần callback sau khi thực thi 1 hoặc 1 chuỗi các biểu thức, hàm…
Khai báo Block:
- Đầy đủ
- Khuyết
Tham khảo ví dụ:
Block trả về kiểu NSInteger , đối số là 2 tham số kiểu NSInteger
Phạm vi của biến khi dùng block
- Biến địa phương của block chỉ hoạt động trong thân của block đó.
- Đối với block inline khai niệm biến địa phương còn bao hàm cả những biết được khai báo trong phần hiện thực của phương thức chưa block đó.
Ví dụ này outsideVariable khi tham gia vào biển thức bên trong block giá trị của nó có thể thay đổi nhưng sau khi ra khỏi block nó giữ nguyên giá trị trước đó.
Để giữ đc giá trị của outsideVariable đã thay đổi trong block sử dụng từ khóa __block
- Không được sử dụng tham chiếu self trong thân của block.
- Trong GCD có đối tượng là lớp con của NSObject thì đối tượng đó đc phép truy xuất read-write bên trong thân block.
Gọi thực thi block
1
2
3
4
5
6
|
void (^simpleBlock)(NSString *) = ^(NSString *paramString){
/* Implement the block object here and use the paramString parameter */
}
- (void) callSimpleBlock{
simpleBlock(@"O'Reilly");
}
|
UIAlertView, UIActionSheet với Block

Chúng ta thường xử lý các sự kiện touch của UIActionSheet, UIAlertView bằng cách implement các delegate của chúng. Cách này gặp vấn đề khi screen của bạn có nhiều sheet, alert thì bạn phải xử lý nhiều hơn ví dụ kiểm tra tag của alert sender, context …
Bằng cách bổ sung cho UIAlertView 1 category mới sau đây, sẽ giúp bạn tối ưu, dễ dàng hơn khi làm việc với ActionSheet, Alert.
Add 2 class UIActionSheet+Blocks và UIAlertView+Blocks vào project của bạn. Trong Class cần dùng hãy import cái header này. 

Và bạn không cần phải implement delegate methods của chúng, việc xử lý sự kiện đối với từng nút đã có sẵn trong các block trả về
Nguon: Chúng ta thường xử lý các sự kiện touch của UIActionSheet, UIAlertView bằng cách implement các delegate của chúng. Cách này gặp vấn đề khi screen của bạn có nhiều sheet, alert thì bạn phải xử lý nhiều hơn ví dụ kiểm tra tag của alert sender, context …
Bằng cách bổ sung cho UIAlertView 1 category mới sau đây, sẽ giúp bạn tối ưu, dễ dàng hơn khi làm việc với ActionSheet, Alert.
Add 2 class UIActionSheet+Blocks và UIAlertView+Blocks vào project của bạn. Trong Class cần dùng hãy import cái header này. 

Và bạn không cần phải implement delegate methods của chúng, việc xử lý sự kiện đối với từng nút đã có sẵn trong các block trả về
Chúng ta thường xử lý các sự kiện touch của UIActionSheet, UIAlertView bằng cách implement các delegate của chúng. Cách này gặp vấn đề khi screen của bạn có nhiều sheet, alert thì bạn phải xử lý nhiều hơn ví dụ kiểm tra tag của alert sender, context …
Bằng cách bổ sung cho UIAlertView 1 category mới sau đây, sẽ giúp bạn tối ưu, dễ dàng hơn khi làm việc với ActionSheet, Alert.
Add 2 class UIActionSheet+Blocks và UIAlertView+Blocks vào project của bạn. Trong Class cần dùng hãy import cái header này. 

Và bạn không cần phải implement delegate methods của chúng, việc xử lý sự kiện đối với từng nút đã có sẵn trong các block trả về

Singleton Pattern là một “kỹ thuật” thường được dùng khi ứng dụng cần duy nhất 1 instance (thể hiện của lớp đối tượng) duy nhất của lớp nào đó hoạt động trong suốt quá trình ứng dụng thực thi trên thiết bị. Ví dụ, để tránh tình trạng nhiều đối tượng khác nhau cùng truy xuất database, files… Trong những trường hợp như vậy lập trình viên thường tạo ra Singleton Object.
Nôm na ở đây là tạo ra đối tượng độc nhất của Class tại thời điểm sử dụng. Điều này đảm bảo đối tượng đó luôn có mặt lúc app cần. Sử dụng Singleton rất có lợi: không tạo ra nhiều objects sẽ tiết kiệm bộ nhớ, quản lý thread, flow dễ hơn, truyền tải thông điệp, dữ liệu an toàn hơn không bị rò rĩ (leak).
Trong Objective-C, bạ thường thấy 2 Singleton mà apple cung cấp:
line 1: đó là sinlgeton của lớp UIApplication,
line 2: singleton lớp NSFileManager
Trường hợp đơn giản về Signleton:
Gọi Singleton
Đặt tên Singleton:
Objective-C hay các ngôn ngữ khác đều không bắt buộc vấn đề này tuy nhiên theo thói quen và quy ước chung khuyến khích các bạn đặt tên theo mẫu sau:
- shared
- shareClassname
- sharedInstance
Signleton và Grand Central Dispatch (CGD)
Trong Objective-C theo kinh nghiệm của mình có hai cách tốt nhất để viết singleton cho 1 class:
- Đối với những Project chưa có hỗ trợ ARC (automatic reference counting) cũng như GCD, developer thường sử dụng cách dưới đây:
- Đối với Project/Class file trong ARC hoặc SDK hỗ trợ CGD (từ iOS 4 trở đi) bạn dùng cách dưới đây:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#import "CreateAccountWindow.h"
@implementation CreateAccountWindow
+ (CreateAccountWindow*)shared{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_Share) {
_Share = [[CreateAccountWindow alloc] initWithWindowNibName:@"CreateAccountWindow"];
}
});
return _Share;
}
|
Nguồn: http://xcodeblog.com
Không có nhận xét nào:
Đăng nhận xét