如何重新加载链接到数组控制器的tableview?

时间:2011-10-14 10:06:21

标签: cocoa nstableview nsarraycontroller

我是Cocoa的新手,我正在尝试创建一个应用程序来审核我们的网络设备,方法是扫描我们的DHCP服务器日志以查找以太网硬件地址并注意最近的事务。我已经创建了一个基于文档的程序,它会在一个绑定到数组控制器的tableView中显示其结果(一个可变数组的可变数组)。

我现在想要读取从网络IPAM管理器导出的文件,该文件由分配给我们的虚拟LAN的以太网硬件地址列表组成,并将其与原始阵列进行比较,添加缺少合适注释的设备。我已经通过文档上的按钮触发了一个动作(readSecondFile)。还有用于进行手动输入的添加和删除按钮,或者更有用的是,用于从列表中删除设备。还有一个用于导出已编辑数组的保存例程。

除了一个例外,一切正常。读入第二个文件时,tableView将不会重绘,重新加载其数据或其他任何内容,直到您单击列标题进行排序或添加:或删除:使用ArrayController方法。当发生这种情况时,附加条目会出现在tableView中。

我的问题是如何在readSecondFile例程结束时使用扩展数组重绘tableView?我已经用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;
                listItem=@"";
                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

2 个答案:

答案 0 :(得分:1)

正确的方法是使用ArrayController将新对象添加到tableView正在显示的数组中。

ArrayController没有响应这些方法,因为我在开发工作期间已将其子类化,并且无意中未在以后删除该子类。 诺亚的评论在提示我重新检查我的代码时证明是非常宝贵的。非常感谢。

答案 1 :(得分:0)

这很简单:

[tableView reloadData];

如果您没有指向表视图的指针,请在控制器类中创建并连接插座。


你问了一些建议。以下是一些:

  1. 在所有异常抛出上添加断点。
  2. 看看Objective-C属性。它使您不必编写访问器和setter方法。
  3. 尝试重构代码以消除数组控制器子类。 (我认为你对数组控制器/表视图更新的问题可能与两个类之间的接口有关。如果它们都是使用标准数组控制器在你的控制器类中处理的,那么它可能更容易理解。)< / LI>
  4. 为了与Apple的命名约定保持一致,请使用大写字母开始您的班级名称,并使用小写字母开始您的实例变量。