5

Tôi đang phân tích cú pháp tệp JSON trên iPad có dung lượng khoảng 53 MB. Các phân tích đang làm việc tốt, tôi đang sử dụng Yajlparser mà là một phân tích cú pháp SAX và đã thiết lập nó như thế này:Mức tiêu thụ bộ nhớ lớn khi phân tích cú pháp JSON và tạo NSManagedObjects

NSData *data = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedAlways|NSDataReadingUncached error:&parseError]; 
    YAJLParser *parser = [[YAJLParser alloc] init]; 
    parser.delegate = self; 
    [parser parse:data]; 

Tất cả mọi thứ đã làm việc tốt cho đến bây giờ, nhưng JSON-file trở nên lớn hơn và bây giờ tôi đột nhiên gặp cảnh báo bộ nhớ trên iPad 2. Nó nhận được 4 cảnh báo bộ nhớ và sau đó chỉ bị treo. Trên iPad 3 nó hoạt động hoàn hảo mà không có bất kỳ cảnh báo mem.

Tôi đã bắt đầu lược tả bằng Dụng cụ và tìm thấy rất nhiều phân bổ CFNumber (Tôi đã dừng các dụng cụ sau vài phút, tôi đã chạy nó trước khi tai nạn và điều CFNumber ở khoảng 60 mb trở lên).

CFNumber allocations

Sau khi mở chi tiết CFNumber, nó xuất hiện một danh sách lớn các phân bổ. Một trong số đó chỉ cho tôi như sau:

CFNumber alloc 1

và một số khác ở đây:

CFNumber alloc 2

Vì vậy, những gì tôi làm sai? Và con số đó (ví dụ: 72,8% trong hình ảnh cuối cùng) là gì? Tôi đang sử dụng ARC nên tôi không thực hiện bất kỳ Bản phát hành hoặc Giữ lại hoặc bất kỳ điều gì.

Cảm ơn sự giúp đỡ của bạn. Cheers

EDIT: Tôi đã hỏi những câu hỏi về làm thế nào để phân tích các tập tin lớn như vậy ở đây: iPad - Parsing an extremely huge json - File (between 50 and 100 mb)
Vì vậy, các phân tích cú pháp riêng của mình có vẻ là tốt.

+0

Con số này có nghĩa là 72,8% khả năng phần mã này đang gây ra vấn đề lớn mà bạn đang gặp phải. (phần lớn thời gian của nó chỉ hướng đúng, ngoại trừ một vài). Chỉ vì bạn đang sử dụng ARC, điều đó không có nghĩa là mã miễn phí/không bị lỗi 100%. Ví dụ, tham chiếu vòng tròn vẫn có thể gây rò rỉ trong mã của bạn. Hãy thử chạy bộ phân tích bộ nhớ và xem liệu "bộ nhớ ảo" của bạn có quá lớn không. Đăng thông tin phản hồi của bạn – nsuinteger

+0

Từ những gì tôi hiểu CoreFoundation cố gắng giảm thiểu số lượng các đối tượng như số và chuỗi. Tài sản "kundennr" được định nghĩa là "bản sao" bởi bất kỳ cơ hội nào? Có vẻ như bạn có thể tạo bản sao mỗi khi bạn chỉ định thuộc tính đó và "currentWarengruppeVK". Điều đó sẽ phủ nhận hiệu quả tích hợp được cung cấp bởi CoreFoundation. – Saltymule

+0

Một vấn đề tiềm năng với trình phân tích cú pháp YAJL là nó vượt qua NSObjects cho các giá trị JSON nguyên thủy (Chuỗi, Đúng, Sai, Số, Null). Nghĩa là, nội bộ nó phải phân bổ NSObjects cho điều đó. Có khả năng, các đối tượng này cũng sẽ được đưa vào một bể tự động. Một cách tiếp cận tốt hơn sẽ không phân bổ bất cứ điều gì để chuyển các giá trị nguyên thủy JSON tới đại biểu của trình phân tích cú pháp. Thông thường (như để tạo các đối tượng được quản lý bằng CD), điều này cũng không cần thiết. Các đối tượng được quản lý bằng CD sẽ tạo bản sao cho các thuộc tính của chúng. Nói tóm lại: IMO, YAJL có vẻ kém tối ưu cho vấn đề của bạn. – CouchDeveloper

Trả lời

5

Xem tài liệu về Dữ liệu cốt lõi của Apple trên Efficiently Importing Data, đặc biệt là "Giảm dấu chân bộ nhớ tối đa". Bạn cần phải chắc chắn rằng bạn không có quá nhiều thực thể mới trong bộ nhớ cùng một lúc, điều này liên quan đến việc lưu và đặt lại ngữ cảnh của bạn theo khoảng thời gian đều đặn trong khi bạn phân tích cú pháp dữ liệu, cũng như sử dụng các nhóm tự động phát hành tốt.

Mã sudo chung sẽ là một cái gì đó như thế này:

while (there is new data) { 
    @autoreleasepool { 
     importAnItem(); 
     if (we have imported more than 100 items) { 
      [context save:...]; 
      [context reset]; 
     } 
    } 
} 

Vì vậy, về cơ bản, đặt bể bơi autorelease xung quanh vòng lặp chính của bạn hoặc phân tích mã. Đếm số lượng NSManagedObject trường hợp bạn đã tạo và định kỳ lưu và đặt lại bối cảnh đối tượng được quản lý để loại bỏ chúng khỏi bộ nhớ. Điều này sẽ giữ cho bộ nhớ của bạn giảm xuống. Số 100 là tùy ý và bạn có thể muốn thử nghiệm với các giá trị khác nhau.

Vì bạn đang lưu ngữ cảnh cho mỗi lô, bạn có thể muốn nhập vào bản sao tạm thời của cửa hàng trong trường hợp xảy ra sự cố và để bạn nhập một phần. Khi mọi thứ đã hoàn tất, bạn có thể ghi đè lên kho gốc.

+0

Cảm ơn câu trả lời của bạn.Nhưng tôi đang thực hiện' [context reset] 'sau mỗi lần lưu. vòng lặp chính bên trong một 'dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{}', nhưng tôi không sử dụng '@ autoreleasepool'-thingy, vì tôi nghĩ rằng tôi sẽ không cần phải quan tâm đến điều đó khi tôi đang sử dụng ARC. – gasparuff

+1

Hãy thử gói mã trong vòng lặp chính của bạn bằng '@ autoreleasepool' (bên trong vòng lặp, không phải bên ngoài). –

+0

Với' @ autoreleasepool' bên trong GCD? – gasparuff

1

Hãy thử sử dụng [self.managedObjectContext refreshObject:obj refreshChanges:NO] sau một số thao tác chèn nhất định. Điều này sẽ biến NSManagedObjects thành lỗi và giải phóng bộ nhớ.

Apple Docs on provided methods

+0

Tôi đã thử điều này, thật không may điều này đã không thực sự giúp đỡ. Và đó là '[self.managedObjectContext refreshObject: obj mergeChanges: NO]' :-) – gasparuff