Speicher wird nicht Freigegeben während CSV-Datei Verarbeitet wird (CHCSV mit Blocks)

  • Speicher wird nicht Freigegeben während CSV-Datei Verarbeitet wird (CHCSV mit Blocks)

    Hallo,

    ich möchte in meiner Anwendung große CSV-Dateien verarbeiten können. Da ich den Parser nicht komplett selber schreiben möchte habe ich folgenden verwendet:
    github.com/davedelong/CHCSVParser

    Dann wollte ich zur Übersicht (und weil ich damit noch nicht so viel Erfahrung habe) den CSV-Parse-Vorgang über Blocks steuern.
    Das ganze funktioniert wie es soll, nur leider habe ich das Problem wenn ich eine 88MB CSV einlese wird der Speicher während der Verarbeitung nicht freigegeben. Die Kurve bei Memory steigt also stetig an...
    Um das ganze zu testen habe ich eine kleine Testanwendung gebaut.

    Der CSV-Parser sieht nun wie folgt aus:

    .h

    Quellcode

    1. @class CHCSVParser;
    2. @interface SAMOACSVParser : NSObject
    3. @property (assign, nonatomic) BOOL recognizesBackslashesAsEscapes; // default is NO
    4. @property (assign, nonatomic) BOOL sanitizesFields;
    5. @property (assign, nonatomic) BOOL stripsLeadingAndTrailingWhitespace; // default is NO
    6. //> define callback blocks
    7. typedef void (^CSVDocumentBegin) (CHCSVParser *parser);
    8. typedef void (^CSVDocumentEnd) (CHCSVParser *parser);
    9. typedef void (^CSVBeginLine) (CHCSVParser *parser, NSUInteger lineNumber);
    10. typedef void (^CSVEndLine) (CHCSVParser *parser, NSUInteger lineNumber);
    11. typedef void (^CSVReadField) (CHCSVParser *parser, NSUInteger fieldIndex, NSString *fieldValue);
    12. typedef void (^CSVFailed) (CHCSVParser *parser, NSError *error);
    13. - (id)initWithFileAtPath:(NSString*)filePath useEncoding:(NSStringEncoding)encoding useDelimiter:(NSString*)delimiter areFieldsEscaped:(BOOL)escaped;
    14. - (void)start:(CSVDocumentBegin)docBegin docEnded:(CSVDocumentEnd)docEnd lineBegin:(CSVBeginLine)beginLine lineEnd:(CSVEndLine)endLine readField:(CSVReadField)fieldRead failed:(CSVFailed)failed;
    15. @end
    Alles anzeigen



    .m

    Quellcode

    1. //
    2. // SAMOACSVParser.m
    3. // SAMOA
    4. //
    5. // Created by Heiko on 07.02.14.
    6. // Copyright (c) 2014 Kommdirekt GmbH. All rights reserved.
    7. //
    8. #import "SAMOACSVParser.h"
    9. @interface SAMOACSVParser () <CHCSVParserDelegate>
    10. @property (assign, nonatomic) NSStringEncoding csvEncoding;
    11. @property (assign, nonatomic) unichar csvDelimiter;
    12. @property (strong, nonatomic) NSString *csvFilePath;
    13. //> callbacks
    14. @property (copy) CSVDocumentBegin cbBeginDoc;
    15. @property (copy) CSVDocumentEnd cbEndDoc;
    16. @property (copy) CSVBeginLine cbBeginLine;
    17. @property (copy) CSVEndLine cbEndLine;
    18. @property (copy) CSVReadField cbReadField;
    19. @property (copy) CSVFailed cbFailed;
    20. @end
    21. @implementation SAMOACSVParser
    22. -(id)initWithFileAtPath:(NSString *)filePath useEncoding:(NSStringEncoding)encoding useDelimiter:(NSString *)delimiter areFieldsEscaped:(BOOL)escaped {
    23. if (filePath==nil) {
    24. return nil;
    25. }
    26. self = [super init];
    27. if (self) {
    28. BOOL isDir;
    29. if ([FileManager fileExistsAtPath:filePath isDirectory:&isDir]) {
    30. if (isDir==NO) {
    31. //> csv file Exist
    32. self.csvFilePath = filePath;
    33. self.csvEncoding = encoding;
    34. self.csvDelimiter = [delimiter characterAtIndex:0];
    35. self.sanitizesFields = escaped;
    36. self.recognizesBackslashesAsEscapes = NO;
    37. self.stripsLeadingAndTrailingWhitespace = NO;
    38. return self;
    39. }
    40. }
    41. }
    42. return nil;
    43. }
    44. - (void)start:(CSVDocumentBegin)docBegin docEnded:(CSVDocumentEnd)docEnd lineBegin:(CSVBeginLine)beginLine lineEnd:(CSVEndLine)endLine readField:(CSVReadField)fieldRead failed:(CSVFailed)failed {
    45. //> init callback blocks
    46. self.cbBeginDoc = docBegin;
    47. self.cbEndDoc = docEnd;
    48. self.cbBeginLine = beginLine;
    49. self.cbEndLine = endLine;
    50. self.cbReadField = fieldRead;
    51. self.cbFailed = failed;
    52. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    53. @autoreleasepool {
    54. //> init chcsvparser
    55. NSInputStream *reader = [NSInputStream inputStreamWithFileAtPath:self.csvFilePath];
    56. NSStringEncoding encoding = self.csvEncoding;
    57. CHCSVParser *csvParser = [[CHCSVParser alloc] initWithInputStream:reader usedEncoding:&encoding delimiter:self.csvDelimiter];
    58. csvParser.delegate = self;
    59. csvParser.sanitizesFields = self.sanitizesFields;
    60. csvParser.recognizesBackslashesAsEscapes = self.recognizesBackslashesAsEscapes;
    61. csvParser.recognizesComments = NO;
    62. csvParser.stripsLeadingAndTrailingWhitespace = self.stripsLeadingAndTrailingWhitespace;
    63. // start Parser
    64. [csvParser parse];
    65. }
    66. });
    67. }
    68. #pragma mark -
    69. #pragma mark PARSER Delegates
    70. #pragma mark -
    71. -(void)parserDidBeginDocument:(CHCSVParser *)parser {
    72. self.cbBeginDoc(parser);
    73. }
    74. -(void)parserDidEndDocument:(CHCSVParser *)parser {
    75. self.cbEndDoc(parser);
    76. }
    77. -(void)parser:(CHCSVParser *)parser didBeginLine:(NSUInteger)recordNumber {
    78. @autoreleasepool {
    79. self.cbBeginLine(parser, recordNumber);
    80. }
    81. }
    82. -(void)parser:(CHCSVParser *)parser didEndLine:(NSUInteger)recordNumber {
    83. @autoreleasepool {
    84. self.cbEndLine(parser, recordNumber);
    85. }
    86. }
    87. -(void)parser:(CHCSVParser *)parser didReadField:(NSString *)field atIndex:(NSInteger)fieldIndex {
    88. @autoreleasepool {
    89. self.cbReadField(parser, fieldIndex, field);
    90. }
    91. }
    92. -(void)parser:(CHCSVParser *)parser didFailWithError:(NSError *)error {
    93. self.cbFailed(parser, error);
    94. }
    95. @end
    Alles anzeigen





    Aufgerufen wird das ganze wie folgt - wie gesagt ist erstmal zu Testzwecken

    Quellcode

    1. @autoreleasepool {
    2. //> Block Vars
    3. __block NSMutableArray *row;
    4. NSString *csvPath = [[File getDocPath] stringByAppendingPathComponent:@"Test.csv"];
    5. SAMOACSVParser *parser = [[SAMOACSVParser alloc] initWithFileAtPath:csvPath useEncoding:NSUTF8StringEncoding useDelimiter:@"," areFieldsEscaped:YES];
    6. parser.recognizesBackslashesAsEscapes = YES;
    7. parser.stripsLeadingAndTrailingWhitespace = YES;
    8. [parser start:^(CHCSVParser *parser){
    9. NSLog(@"Start Doc");
    10. [Utils startTimer];
    11. }
    12. docEnded:^(CHCSVParser *parser){
    13. NSLog(@"End Doc");
    14. [Utils stopTimer];
    15. }
    16. lineBegin:^(CHCSVParser *parser, NSUInteger line){
    17. //NSLog(@"Start: %i", line);
    18. row = [NSMutableArray new];
    19. }
    20. lineEnd:^(CHCSVParser *parser, NSUInteger line){
    21. NSLog(@"ROW-Output: %@", [row componentsJoinedByString:@"!!"]);
    22. row = nil;
    23. }
    24. readField:^(CHCSVParser *parser, NSUInteger fieldIndex, NSString *value){
    25. [row addObject:value];
    26. }
    27. failed:^(CHCSVParser *parser, NSError *parserError){
    28. NSLog(@"Parser Failed: %@", parserError);
    29. }];
    30. }
    Alles anzeigen



    Die ganzen @autorelease sind mehr oder weniger ein Versuch gewesen das Problem zu lösen... leider ohne Erfolg :(

    Kann mir hier jemand weiterhelfen?
  • Hab es nun hinbekommen :)

    Lag wohl wirklich an der CHCSV Klasse.
    Ich habe dort in der Methode die die Felder Parsed ebenfalls ein autorelease pool hinzugefügt.

    Das CSVkit werde ich mir trotzdem einmal ansehen, gibt es dazu vielleicht Benchmark-Tests? Da ich sehr große CSV-Dateien haben werde ist jeder Performance-Boost wertvoll :)