Quản lý bộ nhớ trong Objecitve C (Memory Management)

Một vấn đề rất được quan tâm trong lập trình ios là quản lý bộ nhớ (Memory Management), chắc các bạn cũng từng đặt câu hỏi là vì sao phần mềm Iphone, Mac  os, IPad… chạy rất mượt sau một thời gian dài sử dụng. Đó là sự khác biệt rất tuyệt vời.Đối với các ngôn ngữ, Android , Windows phone… quản lý bộ nhớ, giải phóng bộ nhớ, thu rọn rác hoàn toàn tự động.

Các phương pháp quản lý bộ nhớ trong Objective C:

  • 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.

IOS bây giờ sử dụng trình ARC để giải phóng bộ nhớ, và rọn rác. Tuy nhiên chúng ta vẫn quản lý từng biến thuộc tính cho phép tham chiếu đến đối  tượng như thế nào? khi nào được phép giải phóng? …

Quản lý bộ nhớ đối với biến:

Trước khi đi vào cụ thể mình có lưu ý một số thuật ngữ sau tiện cho sử dụng.

Lưu ý:

1. Trỏ đến  = tham chiếu đến.

2. Sở hữu đối tượng = có quyền quyết định đến sự tồn tại của đối tượng.

Strong trỏ đến đối tượng và có quyền  sở hữu đối tượng, nghĩa là có quyền quyết định đến sự tồn tại của đối tượng.

Mặc định là __strong: Khi bị gán mới nghĩa là trỏ đến đối tượng mới thì ô nhớ chứa đối tượng cũ bị giải phóng.

__strong NSArray *Arr=@[@(1)]; Arr=@[@(10)];//Arr trỏ đến vùng nhớ mới khi đó Vùng nhớ cũ bị giải phóng. NSLog(@”%@”,Arr);

Ở đây Arr là biến strong, kiểu dữ liệu NSArray, trỏ tới vùng nhớ chứa giá trị:@[@(1)], sở hữu vùng nhớ.

Arr=@[@(10)];  Arr trỏ đến vùng nhớ chứa giá trị @[@(10)] dẫn tới vùng nhớ chứa giá trị @[@(1)] không có ai trỏ tới và sẽ bị giải phóng.

=>Ngay lật tức vùng nhớ cũ bị ARC giải phóng.

kết quả: Arr=10

– Đối với __strong gán bằng __strong :

// biến kiểu strong gán strong
    __strong NSArray *Arr1=@[@(1)];
    __strong NSArray *Arr2=Arr1;// gán và cả 2 mảng cùng trỏ đến 1 vùng nhớ!!! quyền sở hữu như nhau
    Arr1=@[@(10)];// thay đổi vùng nhớ cho Arr1 tuy nhiên thằng Arr2 vẫn trỏ đến, vùng nhớ cũ vẫn tồn tại
    NSLog(@"%@",Arr2);

ở đây strong  Arr1 trỏ vào vùng nhớ của đối tượng  có giá trị @[@(1)], và có quyền sở hữu đối tượng đó.

Arr2=Arr1 => Arr2 trỏ đến cùng vùng nhớ của đối tượng Arr1 trỏ với, và cũng có quyền sở hữu như nhau Vì thế khi gán lại Arr1=@[(10)] vùng nhớ cũ không bị giải phóng.

Kết quả Arr2=1;

-Đối với __weak gán bằng strong:

Weak trỏ tới đối tượng , nhưng không có quyền sở hữu đến đối tượng, nghĩa là không có quyền quyết định đến sự tồn tại của đối tượng.

thêm đoạn mã sau:

// kiểu weak gán strong
    __strong NSArray *Arr3=@[@(1)];
    __weak NSArray *Arr4=Arr3;// Arr4 trỏ vào vùng nhớ của Arr3, phụ thuộc Arr3. khi vùng nhớ Arr3 bị xoá thì Arr4 nil.
    Arr3=@[@(10)];
    NSLog(@"%@",Arr4);

ở đây strong  Arr3 trỏ vào vùng nhớ của đối tượng  có giá trị @[@(1)], và có quyền sở hữu đối tượng đó.

weak Arr4 = Arr3 => Arr 4 trỏ đến cùng vùng nhớ của đối tượng Arr3, nhưng không có quyền sở hữu vùng nhớ đó, chỉ tham chiếu gián tiếp thông qua Arr3.

Khi Arr3=@[@(10)]; thì Arr3 trỏ đến vùng nhớ chứa đối tượng mới, mà Arr3 có quyền sở hữu vùng nhớ cũ dẫn tới vùng nhớ cũ  bị giải phóng , dẫn tới Arr4=nil rỗng.

kết quả Arr4=nil;

unsafe_unretained: unsafe_unretained luôn trỏ về vùng nhớ cũ.khi vùng nhớ bị giải phóng nó vẫn trỏ đến vùng nhớ giải phóng, có thể dẫn đến crash chương trình khi vùng nhớ bị xoá.

Tuy nhiên :giải phóng không có nghĩa là vùng nhớ bị xoá như kiểu thùng rác quản lý trong máy tính lưu lại một thời gian sau đó mới xóa.
Kiểu gán này không làm tăng vùng nhớ, dùng gán biến tạm.

__strong NSArray *Arr5=@[@(1)];
__unsafe_unretained NSArray *Arr6=Arr5;
Arr5=@[@(10)];
NSLog(@"%@",Arr6);

Thằng Arr6 luôn trỏ về vùng nhớ cũ của Arr5 cho dù Arr5 đã  giải phóng vùng nhớ cũ.

May mắn kết quả: Arr6=1.

Tuy nhiên nếu để một thời  gian không sử dụng biến này có thể gây crash chương trình do vùng nhớ đã được giải phóng lưu lại trong thùng rác mà chưa bị xoá, sau một thời gian đầy thùng rác bị xoá thì gây crash chương trình.

Quản lý vùng nhớ đối với property :

Tính đồng bộ hóa thread:

  • nonatomic: Không độc quyền nghĩa là rất nhiều thread cùng một thời điểm có thể thay đổi giá trị thuộc tính, dẫn tới không an toàn.

          Lưu ý:Nếu chỉ có 1 luồng truy cập vào biến thì tốc độ nhanh.

  • atomic (default): Độc quyền, nghĩa là nhiều thread cùng truy cập thì thread này gán giá trị chỉnh sửa xong mới cho phép thread khác sử dụng biến đó để chỉnh sửa.
    Lưu ý:Truy cập chậm hơn nonatomic nếu chỉ có 1 luồng do có cơ chế khóa mặc định khi có nhiều luồng truy cập!.

Tính cho phép đọc ghi dữ liệu vào thuộc tính Property: (IVar đọc ghi bình thường).

  • readwrite (default): cho phép đọc, ghi vào thuộc tính.
  • readonly : chỉ cho phép đọc, không cho phép ghi (set),”các biến thể hiện vẫn đọc ghi  bình thường”.

Thay đổi tên mặc định cho hàm get, set Property:

         Do tên set get thường là tính từ, thuộc tính là danh từ, or ta muốn quản lý lại get set, nên ta cần nhu cầu thay đổi tên get set mặc định.

  • @property(assign,getter=isAge,setter=setIsAge:) int age; sử dụng “getter = tên getter mới”, “setter=tên setter mới:”

Quản lý bộ nhớ đối với thuộc tính đối tượng

Đối với thuộc tính là kiểu đối tượng trong Objective C:

Lưu ý: nhắc lại

1. Trỏ đến  = tham chiếu đến

2. Sở hữu đối tượng = có quyền quyết định đến sự tồn tại của đối tượng

3. Strong pointer và weak pointer đề cập đến việc tham chiếu mạnh hay yếu tới object.

  • Strong pointer: là một con trỏ, trỏ đến một đối tượng và sở hữu (own) đối tượng đó. biến iVar sẽ là weak pointer
  • Weak pointer: là một con trỏ, trỏ đến một đối tượng nhưng không sở hữu (own) đối tượng đó.weak thường đi kèm với một đối tượng kiểu strong.” biến iVar sẽ là strong pointer, tương đương với retain.

Ví vụ minh họa: Ta khai báo một class sinhVien như sau:

@interface sinhVien : NSObject
@property(strong,nonatomic) NSArray *model;
-(void) infor;
@end

Thực thi hàm infor:

@implementation sinhVien
-(void) infor{
    
    NSArray *arr=@[@(0)];
    self.model=arr;
    arr=nil;
    NSLog(@"strong : %@",self.model);
}
@end

Tương tự kiểu  strong trong biến mà mình đã giải thích ở trên: kết quả model=0 có cùng quyền sở hữu do tham chiếu mạnh.

nếu ta thay đổi strong thành weak.

@property(weak,nonatomic) NSArray *model;

khi đó model=nil do tham chiếu yếu không có quyền sở hữu đối tượng. Tương tự như weak trong biến mà mình đã giải thích ở trên.

  • unsafe_unretained: Tương tự unsafe_unretained trong biến,  Dùng cho kiểu thuộc tính là đối tượng.
  • Copy:
    copy: cố gắng copy khi property được gán.
    copy là sao chép toàn bộ đối tượng của property được gán!  xong lưu đối tượng đó vào vùng nhớ mới.
    Copy phát huy tác dụng khi  muốn tạo một bản sao rồi chỉnh sửa mà không ảnh hưởng đến đối tượng cũ. Sử dụng chủ yếu 
    NSMutableDictionary, NSMutableString, NSMutableArray, lấy bản sao dữ liệu rồi tuỳ chỉnh.

Ví dụ sau:

@interface sinhVien : NSObject
@property(copy,nonatomic) NSMutableArray *model;
-(void) infor;
@end

Thực thi hàm infor:

@implementation sinhVien
-(void) infor{
    
    NSMutableArray *arr=@[@(0)];
    self.model=arr;
    arr=nil;
    NSLog(@"strong : %@",self.model);
}
@end

Khi giải phóng vùng nhớ của arr=nil, kết quả: model=0 do taon bản sao trỏ đến vùng nhớ mới nên vùng nhớ cũ bị giải phóng không ảnh hưởng đến kết quả model.

 

Đối với thuộc tính là kiểu nguyên thủy trong C:

  • Assign(default): trỏ vào ô nhớ tương tự như unsafe_unretained, tuy nhiên dùng nhẹ nhàng hơn  rất nhiều, vì nó dùng cho kiểu nguyên thủy C.

Chú ý: kiểu nguyên thủy C khi khai báo luôn trỏ vào một vùng nhớ mà không bị thay đổi vùng nhớ, chỉ tham chiếu giá trị cho biến. chính vì thế Assign mới phát huy được ưu điểm là trỏ vào một vùng nhớ.

Thế là mình đã giới thiệu cho các bạn quản lý bộ nhớ, Hãy để lại suy nghĩ của bạn!!! thanks all!

Có gì trong phần tiếp theo : Tiếp theo mình sẽ giới thiệu cho các bạn về protocol kế thừa trong Objective C và các cấu trúc điều khiển IF While…

=D

  • Nếu các bạn muốn biết thêm các kỹ thuật khác về ios, các bạn có thể tham khảo thêm các khóa học ios tại đây.

Add a Comment

Your email address will not be published. Required fields are marked *