Quản lý bộ nhớ trong Objective-C



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) Phương pháp đếm số tham chiếu thủ công, có nghĩa là chúng ta sẽ quản lý bộ nhớ bằng cách tự đánh dấu, theo vết vòng đời của một đối tượng, đếm số lượng tham chiếu đến đối tượng đó, kể từ lúc nó được cấp phát, cho đến khi nó được giải phóng.
Automatic Reference Counting (ARC) : Phương pháp đếm số tham chiếu tự động, nguyên tắc cơ bản tương tự như MRC, tuy nhiên chúng ta không cần phải tự đánh dấu vòng đời của đối tượng nữa, trình biên dịch sẽ làm thay chúng ta phần việc đó.
Garbage Collection (GC) : Ở phương pháp này hệ thống sẽ tự động lưu vết và giải phóng vùng nhớ của đối tượng 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) như đã đề cập ở trên, là một phương pháp quản lý bộ nhớ trong Objective-C bằng cách đếm số lượng tham chiếu (Retain Count) đế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. 

3.1. Cấp phát

Khi cấp phát vùng nhớ cho một đối tượng thì ta sở hữu đối tượng đó. Các phương thức để cấp phát:
alloc
  • Mục đích: Cấp phát vùng nhớ cho một đối tượng,
  • Retain count: 1
NSString *str = [[NSString alloc] init];
copy
  • Mục đích: Cấp phát vùng nhớ cho một đối tượng thông qua một đối tượng khác
  • Retain count: 1
NSString *str = [someString copy];

3.2. Tham chiếu

Khi tham chiếu đến một đối tượng thì ta là đồng sở hữu đối tượng đó, bên cạnh chủ sở hữu của nó. Để gọi tham chiếu ta dùng phương thức retain:
retain
  • Mục đích: Tham chiếu đến một đối tượng, 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é !”
  • Retain count: tăng lên 1.
NSString *str = [[NSString alloc] initWithString:@”Hello”];
NSString *str2 = str;
[str release];
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];

3.3. Giải phóng

Một đối tượng sẽ thực sự được giải phóng khi không còn ai sở hữu nó nữa, lúc này retain count sẽ bằng 0. Các phương thức để giải phóng:
release
  • Mục đích: Để giải phóng một đối tượng, báo cho hệ thống biết là ta đã sử dụng xong một đối tượng và cần giải phóng nó.
  • Retain count: giảm đi 1 ngay lập tức.
autorelease
  • Mục đích: Để giải phóng một đối tượng nhưng không 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.
  • Retain count: giảm đi 1 nhưng không ngay lập tức.

3.3.1. Autorelease object

Là những đối tượng được trả về từ việc gọi hàm, hoặc những đối tượng được gọi bởi phương thức autorelease và ta không sở hữu những đối tượng này. Tất cả các autorelease objects  được tạo ra sẽ được thêm vào autorelease pool  hiện tại.

3.3.2. Autorelease Pool

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 Autorelease Pool?
  • 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.
Nếu ta không muốn đợi autorelease pool  của hệ thống được giải phóng, ta có thể sử dụng đối tượng thuộc lớp NSAutoreleasePool
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];


Nguồn: LMinh

Nhận xét

Bài đăng phổ biến từ blog này

Property trong Objective-C

Design pattern là gì?