Bài này mình viết ra để chia sẽ một số cách tìm bug trên Xcode, bạn sẽ sử dụng print(), breakpoint.

Giới thiệu các loại Bug:

  • Bug có thông báo rõ ràng, vd như: “Index out of bounds”.
  • Bug không có thông báo rõ ráng, vd như EXC_BAD_ACCESS.
  • Bug không có thông báo :((

Khi bạn tìm bug và fix bug, bạn sẽ luôn muốn có một thông báo lỗi, việc này giống như một lời chỉ dẫn để mình có thể tìm ra chúng.

Quá trình sửa lỗi:

Bạn có thể định nghĩa quá trình sửa lỗi giống thế này:
1. App bị crash. Đây là 1 bug
2. Thử tìm kiếm thông báo lỗi. Thường thì thông báo lỗi hiển thị trong trình gỡ lỗi của Xcode, có khi chúng ta phải truy cập sâu hơn một chút.
3. Bạn tìm được một ít thông tin giải thích về thông báo lỗi .
4. Bạn thử đưa ra 1 giải pháp để fix bug và thử nó. Nếu kết quả không giải quyết được thì quay lại bước 2 hoặc bước 3.

Quá trình sửa lỗi nghe có vẻ đơn giản phải không? Rõ ràng, rất nhiều công việc ở giữa bước 3 và 4 - quá trình thì đơn giản như không dễ.

Trong nhiều trướng hợp, đặc biệt nếu bạn là người mới, ngay lập tức bạn sẽ dùng google để tìm cách giải quyết thông báo lỗi. Điều này thường đưa tới stackOverflow, blog, Quora… Trong hầu hết các trường hợp, StackOverflow hoặc blog nào đó có liên quan, gần như đã giải quyết hầu hết các bug mà bạn gặp phải.

Dựa trên những phương pháp bạn tìm được google, bạn triển khai các giải pháp trong app của bạn. Hầu hết các câu trả lời trên StackOverflow có thể giải thích rõ nguyên nhân của bug và đó là giải pháp để chúng ta tuỳ chỉnh lại các đoạn code sao cho phù hợp.

Bạn cũng có thể yêu cầu sự giúp đỡ hoặc đăng câu hỏi tương tự bằng cách hỏi một lập trình viên khác.

Sử dụng Print()

ví dụ như:

var names = ["Bob", "Alice", "Arthur", "Ford", "Zaphod"]

let name = names[8] // Error: Index out of range

Trên màng trên có 5 phần từ có số index là 0-4, hiện tại bạn cố gắng truy cập vào phần tử tại index = 8, bạn sẽ thấy trình sửa lỗi ném ra một thông báo lỗi - phần tử mà bạn truy cập không tồn tại.

print(names.count)

Hàm print thường dùng để in các giá trị của biến, trong trường hợp ở trên sẽ ghi ra màn hình là mảng của bạn có bao nhiêu phần tử, và bạn nhận ra số phần tử của nó ít hơn 8 nên gây ra bug.

Trong phương pháp này, khó nhất dĩ nhiên là đặt lệnh print ở dâu? Trong nhiều trường hợp Xcode sẽ báo chính xác dòng bị bug. Vì thế bạn đặt dòng print ở ngay trên dòng code lỗi để kiểm tra.

Trong trường hợp không chắc chắn vị trí gây ra bug, bạn cần đặt nhiều lệnh print ở nhiều vị trí khác nhau để tìm lỗi. Bạn nên in các thông tin một cách khéo léo để có thể dễ tìm ra chúng:
Giống như thế này: print ("\ (# file): \ (# line), \ (# function) !”) Hoặc print (#function).

Sử dụng Breakpoints.

Bằng cách sử dụng breakpoints trình biên dịch có thể dừng chạy ngay tại một vị trí mong muốn trong đoạn code. Ta có thể chủ động điều khiển từng dòng code chạy, có thể quan sát thông tin về các biến, các trạng thái của chúng. Một điểm cộng có phương pháp này là bạn có để tạo hoặc xoá, hay tạm ngừng hoạt động các breakpoints ngay khi đang chạy ứng dụng. Đây là một lợi thế lớn so với cách dùng hàm print, vì mỗi lần bạn chỉnh làm print thì phải chạy lại app.
Khi dùng phương pháp này bạn cần phải thêm breakpoint vào vị trí mà bạn muốn kiểm tra. chương trình sẽ ngừng lại đúng ngay tại dòng code mà bạn thêm breakpoint, bạn có thể nhìn vào cửa sổ để thấy các thông tin khác (thường là các biến cục bộ sẽ hiển thị lên hết). Bạn có thể dùng breakPoint trong một số trường hợp muốn kiểm tra xem app có chạy qua một dòng code nào đó hay không, hoặc có thể dùng để tìm các bug không rõ vị trí cụ thể ( bạn tạo ra nhiều breakpoint ở nhiều vị trí có liên quan, và cho chúng chạy từng bước để kiểm tra).

Trong hình trên bạn thấy:
The stacktrace Bạn có thể thấy hàm gọi trước đó (hàm chứa breakPoint) trong hình là viewDidLoad().

The breakpoint Bạn có thể thiết lập 1 breakpoint bằng cách click vào cột dọc có đánh số hàng . Một mũi tên màu xanh sẽ xuất hiện. Bạn có thể ngừng nó bằng cách click vào nó lại một lần nữa hoặc có thể xoá nó bằng cách click chuột vào nó, giữ im và kéo ra ngoài khu vực khác (hoặc chỉ cần click chuột phải).

The breakpoint actions Bạn có thể thêm một vài hành động khác ngay khi trình biên dịch chạy qua breakpoint này. (sẽ nói ở phần khác)

The app values / state Bạn có thể kiểm tra các giá trị tại thời điểm hiện tại của ứng dụng. Trong vd này bạn sẽ thấy tên các phần tử trong mảng.

Khi bạn nhìn vào bug trong vd bug trước đó, bạn thử truy cập vào phần tử có index = 8, trong khi mảng của bạn chỉ có 5 phần tử, bạn sẽ thấy rằng bằng cách sử dụng breakpoint bạn có thể kiểm tra kích thước của mảng, điều ngày cũng tương tự như dùng lệnh print trước đó.

Giao diện của debugger:
Hình:

  1. Hide The Debug Area: Ẩn vùng dedug .
  2. Deactivate Breakpoints: ngừng hoạt động các breakpoints.
  3. Continue Program Execution: Tiếp tục chạy có cho tới khi gặp breakpoint khác (cũng có thể nó gặp lại chính nó, vd nhưng trường hợp breakpoint ở trong vòng lặp for..).
  4. Step Over: Tiếp tục có code chạy sang 1 dòng kế tiếp (không đi sâu vào các function, vd như khi bạn dùng stepOver để chạy từng dòng code thì khi nó chạy qua dòng kiểu: test() thì nó tiếp tục đi xuống dưới hàng kế tiếp chứ không đi sâu vào từng hàng của hàm test()).
  5. Step Indo: Tiếp tục có code chạy sang 1 dòng kế tiếp (có thể đi sâu vào các function).
  6. Step Out: Tiếp tục chạy code, bằng cách chạy step out trong hàm hiện tại, bạn có thể nhảy tới func cao cấp hơn (vd như trong hàm test1() có gọi hàm test2(), bạn đang ở trong hàm test2 thì gọi step out, nó sẽ di chuyển ra hàm test1().


Chú ý: khi một trình biên gỡ lỗi chạy tới breakPoint, dòng mà nó đang ở đó vẫn chưa được thực hiện. Bạn có thể thấy rõ trong ảnh chụp trên.
Giải thích về vd trên:

  • Vòng lập đang chạy lần thứ 5, (n=5).
  • Ngay tại lúc này, bạn thấy rõ giá trị của sum = 34 = 1 + 3 + 7 + 9 + 14 (số 14 có chỉ số index = 4). Nghĩa là sum chỉ là tổng của các giá trị có index từ 0 tới 4.

Trong một số trường hợp bạn cần xem các giá trị ngay tại điểm breakpoints, trong nhiều trường hợp bạn chỉ cần dùng step Indo hoặc step over để di chuyển sang dòng kế tiếp, lúc này bạn có thể thấy cách giá trị ngay tại dòng trên :] .

Ở bài này, mình chỉ giới thiệu sơ qua cách tìm bug, bằng hàm print hoặc breakpoint, ở một bài khác mình sẽ viết về cách sử dụng cơ bản ddlc, điều này giúp bạn có thể xem thông tin, trạng thái của các biến ngay tại điểm breakpoint.