How to Use Instruments in Xcode - Part 3
Dựa trên hướng dẫn từ: http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode Tiếp theo từ phần 2: https://viblo.asia/thevinh92/posts/7eEREJNVMgNj Allocations, Allocations, Allocations Công cụ tiếp theo được đề cập đến trong tutorial này là Allocations instrument. Nó cung cấp cho ...
Dựa trên hướng dẫn từ: http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
Tiếp theo từ phần 2: https://viblo.asia/thevinh92/posts/7eEREJNVMgNj
Allocations, Allocations, Allocations
Công cụ tiếp theo được đề cập đến trong tutorial này là Allocations instrument. Nó cung cấp cho bạn các thông tin chi tiết về tất cả các object đang được tạo ra và các memory sao lưu nó, nó cũng cho bạn thấy retain counts của mỗi object.
Để bắt đầu lại việc instruments profile, quit instruments app ra trước. Build & Run app, và mởi Debug Navigator trong Navigators area. Click vào Memory để hiện thị ra đồ thị sử dụng memory trong main window:
Đồ thị này rất hữu ích để xem app của bạn hoạt động ntn. Nhưng bạn cần xem rõ hơn nữa bằng cách: click và Profile trong nút Instruments rồi Transfer để chuyển về section sang Instruments. Allocations instrument sẽ khởi động tự động.
Bạn sẽ nhìn thấy 2 track ở phía trên: 1 cái là Allocations, 1 cái là Leaks. Chúng ta sẽ thảo luận về Allocations track ở phía dưới, Leaks trachk thường khá hữu dụng trong Objective-C, nhưng sẽ không đề cập đến trong tutorial này. Vậy thì bug nào sẽ được bạn theo dõi tiếp? Có 1 số thứ bị ẩn đi trong project này mà có thể bạn ko hề biết. Bạn có thể đã từng nghe nói về memory leaks. Nhưng những gì bạn không biết đó là thực sự chỉ có 2 loại leaks:
- True memory leaks: Đó là khi 1 object không còn được tham chiếu bởi ai nữa nhưng vẫn allocated - có nghĩa là the memory không bao giờ được re-used nữa, bởi vì không giải phóng được vùng nhớ của đối tượng đó. Ngay vả với swift và ARC helping manage memory, trường hợp phổ biến nhất của kiểu memory leaks này là retain cycle hoặc strong reference cycle. Đó là khi hau object đều giữ 1 strong references tới nhau, và mỗi object đều giữ cho object kia không bị deallocated. Tức là phần memoryc ủa chúng sẽ không bao h được released.
- Unbounded memory growth: Là khi mà memory cứ tiếp tục bị gọi allocated và không bao h có cơ hội để deallocated. Nếu điều này xảy ra tiếp tục mãi mãi, thì đến 1 thời điểm toàn bộ system's memory sẽ bị lấp đầy, khi đó iOS sẽ ngay lập tức killed app.
Với Allocations instrument đang được chạy, thực hiện 5 tìm kiếm khác nhau trong app nhưng không đi sâu vào kết quả nào. Chắc chắn rằng nhưgnx tìm kiếm đó có 1 số kết quả. Bây giờ, để cho app giải quyết 1 chút bằng cách chờ vài giây. Bạn sẽ thấy rằng đồ thị trong Allocated track bị tăng lên. Nó có nghĩa là memory của bạn đã được allocated. Tính năng này sẽ giúp bạn phát hiện được unbounded memory growth. Tiếp theo bạn cần phải làm 1 "generation analysis": press button Mark Generation. Bạn sẽ tièm thấy button ở trên đầu của Display Settings inspector:
Press vào nó và bạn sẽ thấy 1 red flag xuất hiện trong track, như hình sau:
Mục đích của generation analysis là để thực hiện 1 hành động trong nhiều lần, và xem xem bộ nhớ có bị grow theo kiểu unbounded hay ko. Đi sâu vào 1 tìm kiếm, chờ vài giây để load images, rồi quay trở lại main page. Đánh dấu generation thêm 1 lần nữa. RỒi lại thực hiện lại qúa trình đó vài lần nữa với những tìm kiếm khác nhau. Sau 1 số tìm kiếm, Instruments sẽ trông như sau:
Tại thời điểm này, bạn sẽ nhận ra 1 số điều đáng ngờ. Chú ý vào đồ thị mày xanh đã đang đi lên với những tìm kiếm mà bạn thực hiện, rõ ràng là không tốt. Nhưng, bạn vẫn còn nhớ về memory warning chứ? Memory warning là 1 cách để nói lên rằng mọi thứ đang rất chật chội và bạn cần phải clear bớt memory đi. Có thể việc memory allocated tăng trưởng không chỉ là do app của bạn, mà cũng có thể là do cái gì đó trong UIKit đang giữ memory mà ko chịu released. Hãy cho system frameworks và app cảu bạn 1 cơ hội để clear memory.
Mô phỏng 1 memory warning bằng cách select " InstrumentSimulate Memory Warning" trong Instruments' menu bar, hoặc "HardwareSimulate Memory Warning" từ menu bar của simulator. Bạn sẽ thấy rằng việc sử dụng memory sẽ giảm xuống 1 chút hoặc không nhiều. Nhưng rõ ràng là ko hề giảm xuống như mong muốn => Chắc chắn vẫn còn có unbounded memory growth xảy ra ở đâu đó. Lý do của việc đánh dấu 1 generation sau mỗi 1 lần lặp lại việc search là để bạn có thể thấy memory nào được allocated giữa những lần generation. Hãy xem trong detail panel và bạn sẽ thấy 1 loạt các generation.
Talkin’ Bout My Generation
Với mỗi một generation, bạn sẽ thấy tất cả objects mà được allocated vẫn tồn tại ở thời điểm mà generation được đánh dấu. Generation tiếp theo sẽ chỉ chứa các đối tượng kể từ khi generation trước được đánh dấu. Nhìn vào cột Growth và bạn sẽ thấy chắc chắn rằng đã có sự growth xuảy ra ở đâu đó. Mở 1 trong các generation ra và bạn sẽ thấy:
Hơi đen khi mà Swift thể hiện ở view này rất không ngăn nắp, so với Objective-C, Swift điền vào đó với các kiểu dữ liệu internal mà bạn ko thực sự cần thiết phải biết. Bạn có thể làm nó rõ ràng hơn bằng cách chuyển Allocation Type thành All Heap Allocations. Ngoài ra, click vào Growth header để sort by size.
Ngay gần trên đầu là ImageIO_jpg_Data, và dó chắc chắn là thứ được xử lý trong app của bạn. Click vào mũi tên ở bên trái của ImageIO_jpg_Data để hiển thị toàn bộ list. CHọn 1 trong số đó rồi chọn Extended Detail inspector (hoặc nhấn ⌘+3) :
Nó show cho bạn thấy 1 stack trace từ thời điểm khi 1 đối tượng cụ thể được tạo ra. Phần stack trace màu xám là trong system lybraries, màu đen là bên trong code của app. Để hiểu được hơn context của trace này, click đúp vào "second black frame from the bottom". Nó là thứ duy nhất có tiền tố "InstrumentsTutorial" mà chỉ ra rằng đó là từ code swift. Click đúp sẽ dẫn bạn tới code của method đó - collectionView(_:cellForItemAtIndexPath:).
Xem lại các method này và bạn sẽ thấy nó gọi setImage(:forKey:). Như bạn đã thấy khi bạn nhìn Time Profiler, method này caches image trọng trường hợp nó được dùng lại sau đó trong app. Và rõ ràng, nó có vấn đề. Mở file ImageUtilities.swift và xem lại implement của setImage(:forKey:). :
func setImage(image: UIImage, forKey key: String) { images[key] = image }
Method này add 1 image vào dictionary mà key dựa trên photo ID của Flickr photo. Nhưng nếu bạn nhìn hết code, bạn sẽ thấy rằng image không bao h được clear ở trong dictionary cả. Đó chính là vấn đề gây ra unbounded memory growth: mọi thứ hoạt động bình thường nhưng app không hề remove những thứ ở cached đi.