2011-10-14 46 views
0

我是Cocoa的新手,我試圖通過掃描我們的DHCP服務器日誌查詢以太網硬件地址並注意最近的事務來創建一個應用程序來審計我們的網絡設備。我創建了一個基於文檔的程序,它將執行該操作,並在綁定到數組控制器的tableView中顯示其結果(可變字典的可變數組)。如何重新加載鏈接到數組控制器的tableview?

我現在想要讀取從網絡IPAM管理器導出的文件,該文件由分配給我們的虛擬LAN的以太網硬件地址列表組成,並將它與原始數組進行比較,並在缺少適當註釋的設備中添加。我已經通過文檔上的按鈕(readSecondFile)觸發了一個操作。還有添加和刪除按鈕用於手動輸入,或者更有用的是,從列表中刪除設備。還有一個用於導出編輯數組的保存例程。

這一切都正常工作,只有一個例外。當讀入第二個文件時,tableView將不會重新繪製,重新加載其數據或其他內容,直到您單擊列標題來排序或添加:或使用ArrayController方法刪除:。發生這種情況時,附加條目將出現在tableView中。

我的問題是如何讓tableView重新繪製readSecondFile例程末尾的擴展數組?我已經使用了KeyValueObserving,但我不確定這是否是必需的,儘管它可以工作(如果我可以得到它的工作!我已經要求ArrayController觀察數組並通知數組的更改,但我'不敢肯定,如果我需要做的更多。

誰能幫助?非常感謝。

[其他]只注意到,它並重繪,因爲它在一些標註添加了對陣列中現有的設備,但它不會顯示具有IPAM條目的附加設備,但沒有DHCP活性

。[附加 - 請求的代碼]

//MyDocument.h 
#import <Cocoa/Cocoa.h> 
#import <stdio.h> 
#import <regex.h> 
#import <AppKit/AppKit.h> 

typedef enum _dataFormat { 
    Unknown = 0, 
    PlainText = 1, 
    RichText = 2, 
    RTFD = 3, 
} DataFormat; 

int count, b, i, q, p; 


@interface MyDocument : NSDocument 
{ 
    NSMutableArray *macs; 
    NSMutableDictionary *machines; 
    NSArray *filteredLines; 
    NSArray *lines; 
    IBOutlet NSTextField *processing; 
    IBOutlet NSProgressIndicator *myProgress; 
    IBOutlet NSButton *removeButton; 
    IBOutlet NSButton *readSecondFile; 
    IBOutlet NSTableView *tableView; 
    NSString *fileString; 
} 

-(IBAction)readSecondFileFromURL:(id)sender; 

-(NSMutableArray *)macs; 
-(NSMutableDictionary *)machines; 
-(void)setMacs:(NSArray *)newMacs; 
-(void)setMachines:(NSMutableDictionary *)newMachines; 
-(NSArray *)filteredLines; 
-(NSArray *)lines; 


@end 


// MyDocument.m 

#import "MyDocument.h" 
#import "dataArrayController.h" 

@implementation MyDocument 

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     [self setMacs:[NSMutableArray array]];  
    } 
    //[dataArrayController bind:@"macs" // see update 
    //    toObject:self 
    //   withKeyPath:@"mac" 
    //    options:0]; 

    // set up an observer for arrangedObjects 
    [dataArrayController addObserver:self 
         forKeyPath:@"mac" 
         options:0 
         context:nil]; 

    return self; 
} 

-(void)dealloc 
{ 
    [self setMacs:nil]; 
    [super dealloc]; 
} 

-(NSArray *)macs 
{ 
    return macs; 
} 

-(NSArray *)filteredLines 
{ 
    return filteredLines; 
} 

-(NSArray *)lines 
{ 
    return lines; 
} 

-(NSMutableDictionary *)machines 
{ 
    return machines; 
} 

-(void)setMacs:(NSMutableArray *)newMacs 
{ 
    [newMacs retain]; 
    [macs release]; 
    macs=newMacs; 
    //[macs setArray:newMacs]; 
} 

-(void)setMachines:(NSMutableDictionary *)newMachines 
{ 
    [newMachines retain]; 
    [machines release]; 
    machines = newMachines; 
    [newMachines release]; 
} 

-(void)setLines:(NSArray *)newLines 
{ 
    [newLines retain]; 
    [filteredLines release]; 
    filteredLines = newLines; 
} 

- (NSString *)windowNibName 
{ 
     return @"MyDocument"; 
} 

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{ 
    [tableView reloadData]; 
} 

-(BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError //Write a text file of the array (macs) contents 
{ 
    NSMutableString *csvString = [NSMutableString string]; 
    NSEnumerator *macEnum = [macs objectEnumerator]; 
    id data; 
    while (data = [macEnum nextObject]){ 
     NSNumber *mac = [data valueForKey:@"mac"]; 
     NSNumber *ack = [data valueForKey:@"ack"]; 
     NSString *vLan = [data valueForKey:@"vLan"]; 
     [csvString appendString:[NSString stringWithFormat:@"%@,%@,%@\n", mac, ack, vLan]]; 
    } 
    return [csvString writeToURL:absoluteURL atomically:NO encoding:NSUTF8StringEncoding error:outError]; 
} 

-(BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError //Check the log file existance 
{ 
    fileString = [NSString stringWithContentsOfURL:absoluteURL encoding:NSUTF8StringEncoding error:outError]; 
    if (nil == fileString) return NO; 
    return YES; 
} 

-(BOOL)readSecondFileFromURL:(id)sender //error:(NSError **)outError//(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError 
{ 
    double progress; 
    NSString* string = @"00:00:00:00:00:00"; 
    NSString* pattern = @"[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}"; 
    NSString* result = nil; 
    NSString* listItem = nil; 
    NSString* mac = nil; 
    NSArray* extracted = nil; 
    NSMutableDictionary* obj=nil; 
    NSDate *date = NULL; 
    NSOpenPanel* openDlg = [NSOpenPanel openPanel]; 
    [openDlg setCanChooseFiles:YES]; 
    [openDlg setCanChooseDirectories:YES]; 

    if ([openDlg runModalForDirectory:nil file:nil] == NSOKButton) 
    { 
     NSArray* files = [openDlg filenames]; 
//NSLog(@"%@",[files description]); 
     // Loop through all the files and process them. 
     for(b = 0; b < [files count]; b++) 
     { 
      extracted = [macs valueForKey:@"mac"]; 
//NSLog(@"%@", [extracted description]); 
      NSString* fileLoc = [files objectAtIndex:b]; 
//NSLog(@"%@",fileLoc); 
      fileString = [NSString stringWithContentsOfFile:fileLoc encoding:NSUTF8StringEncoding error:nil]; 
      if (fileString == nil) return NO; 

      lines = [fileString componentsSeparatedByString:@"\n"]; // each line, adjust character for line endings 
      [processing setStringValue:@"Processing..."]; 
      [processing display]; 

      filteredLines = lines; //[lines filteredArrayUsingPredicate:pred]; 
      count = [filteredLines count]; 
//NSLog(@"%d",count); 
//NSLog(@"%@",[filteredLines description]); 

      [myProgress setDoubleValue:0.5]; 
      [myProgress setIndeterminate:NO]; 
      [myProgress displayIfNeeded]; 

      i=0; 
      listItem = [[NSString alloc] initWithString: @""]; 

      for (NSString *ent in filteredLines) { 
       string=ent; 
       result=NULL; 
       [email protected]""; 
       i++; 
       progress = 100*((double)i/(double)count); 
       q = progress; 
//NSLog(@"%d",i); 
       if (q > p) { 
        [myProgress setDoubleValue:progress]; 
        [myProgress displayIfNeeded]; 
        p = q; 
       } //draw the progress bar 
//NSLog(@"%@",string);       
       regex_t preg;   
//NSLog(@"B:%@",string); 
       int err=regcomp(&preg,[pattern UTF8String],REG_EXTENDED); 
       if(err) { 
        char errbuf[256]; 
        regerror(err,&preg,errbuf,sizeof(errbuf)); 
        [NSException raise:@"CSRegexException" 
           format:@"Could not compile regex %@: %s",pattern,errbuf]; 
       } //compile the regular expression 
       //NSLog(@"C:%@",string); 

       const char *cstr=[string UTF8String]; 
       regmatch_t match; 
       if(regexec(&preg,cstr,1,&match,0)==0) { 
        result = [[[NSString alloc] initWithBytes:cstr+match.rm_so 
                 length:match.rm_eo-match.rm_so encoding:NSUTF8StringEncoding] autorelease]; 
        //NSLog(@"Result: %@",result); 
       } //pull out the ethernet hardware address by scanning for the regex 
//NSLog(@"D:%@",result); 
       if(result != NULL){ 
        if(result.length < 17) { 
        NSArray *listItems = [result componentsSeparatedByString:@":"]; 
        for (NSString *bytepair in listItems){ 
         //NSLog(@"split: %@", bytepair); 
         unsigned result; 
         if([[NSScanner scannerWithString: bytepair] scanHexInt: &result]) 
          listItem=[listItem stringByAppendingFormat:@"%02x:", (int)result]; 
         } //break address into array at colons 
        result=[listItem substringToIndex:17] ; 
        } //pad out to standard ethernet hardware address 
        mac = result; 
//NSLog(@"%@ %d",mac, [extracted containsObject:mac]); 
        if ([extracted containsObject:mac]){ 
         for(id key in macs) { 
           if ([[key valueForKey:@"mac"] isEqualToString:[NSString stringWithFormat:@"%@", mac]]) { 
            [key setValue:@"vLan OK" forKey:@"vLan"];  
           } //Check if the mac is already registered and if so annotate it. 
         } //Annotate an existing entry 
        } else { 
         date=[NSDate distantPast]; 
         obj=[NSMutableDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%@",mac], @"mac", date, @"ack", @"No DHCP activity", @"vLan", nil]; 
         NSIndexSet *loneIndex = [NSIndexSet indexSetWithIndex:[macs count]]; 
         [dataArrayController willChange:NSKeyValueChangeInsertion valuesAtIndexes:loneIndex forKey:@"mac"]; 
         [macs addObject:obj]; 
         [dataArrayController didChange:NSKeyValueChangeInsertion valuesAtIndexes:loneIndex forKey:@"mac"]; 
        } //Add a new entry   
       } 

      } 

      //[myProgress setIndeterminate:YES]; 
      //[myProgress startAnimation:self]; 
      //[myProgress displayIfNeeded]; 
//NSLog(@"%@",[newMachines description]); 
      //[self setMachines:newMachines]; 
//NSLog(@"%@",[machines description]); 
      //mac = NULL; 
      //for (mac in machines) { 
      // ack = [machines valueForKey:mac]; 
      // NSLog(@"F:%@", mac); 
       //[newMacs addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%@",mac], @"mac", ack, @"ack", @"vLan", @"vLan", nil]]; 
      //} //Convert the machine dictionary into a format suitable for an array controller 
      //[mac release]; 
      //[machines release]; 
      //[newMachines release]; 
      //[self setMacs:newMacs]; 
      //[dateFormat release]; 
      //[myProgress setDisplayedWhenStopped:NO]; 
      //[myProgress stopAnimation:self]; 
      //[myProgress setHidden:YES]; 
      //[processing setHidden:YES]; 
      //if ([macs count]>0){ 
      // [removeButton setHidden:NO]; 
      //} 
      [readSecondFile setHidden:NO]; 
      [readSecondFile display]; 
      //[extracted release]; 
        } 
    } 

    [tableView reloadData]; 

    return YES; 
} 

- (void)windowControllerDidLoadNib:(NSWindowController *) aController 
{ [super windowControllerDidLoadNib:aController]; 
    [self showWindows]; 
    [removeButton setHidden:YES]; 
    [removeButton display]; 
    [readSecondFile setHidden:YES]; 
    [readSecondFile display]; 
    [processing setStringValue:@"Loading..."]; 
    [processing setHidden:NO]; 
    [processing display]; 
    [myProgress setHidden:NO]; 
    [myProgress setUsesThreadedAnimation:YES]; 
    [myProgress setIndeterminate:YES]; 
    [myProgress startAnimation:self]; 

    int count, i, q, p; 
    double progress; 

    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF CONTAINS 'DHCP'"]; 

    NSString* string = @"00:00:00:00:00:00"; 
    NSString* pattern = @"[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}:[0-9A-Fa-f]{1,2}"; 
    NSString* result = nil; 
    NSString* listItem = nil; 
    NSString* mac = nil; 
    NSString* ack = nil;  
    NSString* localeDetect = nil; 
    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; // Convert string to date object 
    NSDate *date = NULL; 
    NSRange rangeForLocaleDetect = NSMakeRange (20, 3); 
    NSMutableArray *newMacs = [NSMutableArray array]; 

    lines = [fileString componentsSeparatedByString:@"\n"]; // each line, adjust character for line endings 
    [processing setStringValue:@"Processing..."]; 
    [processing display]; 

    filteredLines = [lines filteredArrayUsingPredicate:pred]; 
    count = [filteredLines count]; 

    NSMutableDictionary *newMachines = [NSMutableDictionary dictionaryWithCapacity:count]; 

    [myProgress setDoubleValue:0.5]; 
    [myProgress setIndeterminate:NO]; 
    [myProgress displayIfNeeded]; 

    i=0; 
    p=0; 

    for (NSString *ent in filteredLines) { 
     string=ent; 
     i++; 
     progress = 100*((double)i/(double)count); 
     q = progress; 
     if (q > p) { 
      [myProgress setDoubleValue:progress]; 
      [myProgress displayIfNeeded]; 
      p = q; 
     } //draw the progress bar 

     listItem = [[NSString alloc] initWithString: @""]; 
     localeDetect = [NSString stringWithFormat:@"%@",[string substringWithRange:rangeForLocaleDetect]]; 

     if ([localeDetect isEqualToString:@"UTC"]){ 
      [dateFormat setDateFormat:@"yyyy.MM.dd HH:mm:ss"]; 
      ack = [NSString stringWithFormat:@"%@", [string substringToIndex:19]]; 
     } else { 
      [dateFormat setDateFormat:@"MMM dd HH:mm:ss"]; 
      ack = [NSString stringWithFormat:@"%@",[string substringToIndex:15]]; 

     } 

     date = [dateFormat dateFromString:ack]; 
     regex_t preg; 
     int err=regcomp(&preg,[pattern UTF8String],REG_EXTENDED); 
     if(err) { 
      char errbuf[256]; 
      regerror(err,&preg,errbuf,sizeof(errbuf)); 
      [NSException raise:@"CSRegexException" 
         format:@"Could not compile regex %@: %s",pattern,errbuf]; 
     } //compile the regular expression 

     const char *cstr=[string UTF8String]; 
     regmatch_t match; 
     if(regexec(&preg,cstr,1,&match,0)==0) { 
      result = [[[NSString alloc] initWithBytes:cstr+match.rm_so 
               length:match.rm_eo-match.rm_so encoding:NSUTF8StringEncoding] autorelease]; 
     } //pull out the ethernet hardware address by scanning for the regex 

     if(result.length < 17) { 
      NSArray *listItems = [result componentsSeparatedByString:@":"]; 
      for (NSString *bytepair in listItems){ 
       //NSLog(@"split: %@", bytepair); 
       unsigned result; 
       if([[NSScanner scannerWithString: bytepair] scanHexInt: &result]) 
        listItem=[listItem stringByAppendingFormat:@"%02x:", (int)result]; 
      } //break address into array at colons 
      result=[listItem substringToIndex:17] ; 
     } //pad out to standard ethernet hardware address 

     mac = result; 
     NSDate *old = [newMachines valueForKey:mac]; 
      if (([date laterDate:old]==date) || old==NULL){ 
       [newMachines setValue:date forKey:mac];  
      } //Check for and compare dates for existing keys, as the log file might just be out of sequence 
    } //For every line in the log which meets the Predicate, extract the relevant information. 

    [myProgress setIndeterminate:YES]; 
    [myProgress startAnimation:self]; 
    [myProgress displayIfNeeded]; 

    [self setMachines:newMachines]; 

    for (mac in machines) { 
     ack = [machines valueForKey:mac]; 
     [newMacs addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%@",mac], @"mac", ack, @"ack", @"", @"vLan", nil]]; 
     } //Convert the machine dictionary into a format suitable for an array controller 

    [self setMacs:newMacs]; 
    [dateFormat release]; 

    [myProgress setDisplayedWhenStopped:NO]; 
    [myProgress stopAnimation:self]; 
    [myProgress setHidden:YES]; 
    [processing setHidden:YES]; 
    if ([macs count]>0){ 
     [removeButton setHidden:NO]; 
    } 
    [readSecondFile setHidden:NO]; 
    [readSecondFile display]; 


} //Process the file and display the relevant progress bars. Create and populate array. 

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError 
{ 
    // Insert code here to write your document to data of the specified type. If the given outError != NULL, ensure that you set *outError when returning nil. 
    // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead. 
    // For applications targeted for Panther or earlier systems, you should use the deprecated API -dataRepresentationOfType:. In this case you can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead. 

    if (outError != NULL) { 
     *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL]; 
    } 
    return nil; 
} 

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError 
{ 
    // Insert code here to read your document from the given data of the specified type. If the given outError != NULL, ensure that you set *outError when returning NO. 
    // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.  
    // For applications targeted for Panther or earlier systems, you should use the deprecated API -loadDataRepresentation:ofType. In this case you can also choose to override -readFromFile:ofType: or -loadFileWrapperRepresentation:ofType: instead. 

    if (outError != NULL) { 
     *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL]; 
    } 
    return YES; 
} 

@end 
+0

你曾經實例化過一個dataArrayController嗎?也許在筆尖? (你把它綁定到MyDocument.lines?) – paulmelnikow

+0

我在nib中添加了一個arrayController對象,是的。 我認爲問題出在我向數組添加'macs'的方法,因爲隨後的測試顯示readSecondFile的第一次運行將添加到數組,並且tableView在重新排序時跟上它,但隨後的運行'readSecondFile'更新數組,但更改從未反映在tableView中。 – TRT

+0

我在'MyDocument.m'中註釋了'import'dateArrayController.h'',並在'MyDocument.h'文件中添加了一個'IBOutlet NSArrayController * dataArrayController;'。 arrayController現在響應'setContent:'方法並按預期更新tableView。感謝您的幫助,但是如果您有任何其他指標願意分享,我會很感激,因爲我非常喜歡可可! – TRT

回答

0

這很簡單:

[tableView reloadData]; 

如果你沒有一個指針表視圖,創建並在控制器類連接的出口。


你問了一些建議。這裏有幾個:

  1. 在所有異常拋出上添加一個斷點。
  2. 看看Objective-C的屬性。它不需要編寫訪問器和setter方法。
  3. 嘗試重構您的代碼以消除數組控制器子類。 (我認爲你使用數組控制器/表視圖更新的問題可能與兩個類之間的接口有關,如果它們都是使用標準的數組控制器在控制器類中進行處理的話,那麼可能會更容易理解。)
  4. 爲了與Apple的命名約定一致,請使用大寫字母和實例變量以小寫字母開頭。
+0

謝謝,我已經這樣做了,並且我注意到視圖會重繪已經存在於tableView中的行,因爲它在註釋中添加了它們在IPAM文件中,但其他行不顯示。我也不確定它是否僅僅是第一次運行 - 我的垃圾回收很沒用。 – TRT

+0

你會發布你的代碼來設置數組,設置數組控制器和readSecondFile嗎? – paulmelnikow

+0

'@implementation dataArrayController - (ID)NEWOBJECT { \t返回[[的NSMutableDictionary dictionaryWithObjectsAndKeys: \t \t \t [NSNumber的numberWithFloat:0.0],@ 「MAC」, \t \t \t [NSNumber的numberWithFloat:0.0],@」 ACK」, \t \t \t [NSNumber的numberWithFloat:0.0],@ 「VLAN」, \t \t \t零]保留]; \t \t \t } @ end' – TRT

1

正確的做法是使用ArrayControllertableView顯示的陣列添加新對象。

ArrayController沒有對這些方法做出響應,因爲我在開發工作中對它進行了子類化,並在以後無意中不會刪除該子類。 noa的評論在促使我重新審視我的代碼時被證明是無價的。非常感謝。