我是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
答案 0 :(得分:1)
正确的方法是使用ArrayController
将新对象添加到tableView
正在显示的数组中。
ArrayController
没有响应这些方法,因为我在开发工作期间已将其子类化,并且无意中未在以后删除该子类。
诺亚的评论在提示我重新检查我的代码时证明是非常宝贵的。非常感谢。
答案 1 :(得分:0)
这很简单:
[tableView reloadData];
如果您没有指向表视图的指针,请在控制器类中创建并连接插座。
你问了一些建议。以下是一些: