我的意图是为带有两个窗格的NSSplitView提供2个单独的 View Controller 。在左窗格中,我需要一个表格 View ,但是它崩溃了。这是场景:
我有一个带有MainMenu.xib,AppDelegate.h / m的简单项目。在此项目中,我添加了LeftPaneViewController.h / .m / .xib。
在MainMenu.xib中,我将NSSplitView添加到默认 View 。
在AppDelegate.h中:
@property (weak) IBOutlet NSSplitView *splitView;
在AppDelegate.m中:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
LeftPaneViewController *lpvc = [[LeftPaneViewController alloc] initWithNibName:@"LeftPaneViewController" bundle:nil];
[self.splitView replaceSubview:[[self.splitView subviews] objectAtIndex:0] with:lpvc.view];
}
我将MainMenu.xib中的NSSplitView连接到AppDelegate中的splitView。
当我运行时,这很好,但是当然没有什么可看的了。
在LeftPaneViewController.xib中,我将NSTableView添加到默认的自定义 View 。我删除其中一列(列表底部的一列)。
在LeftPaneViewController.h中:
@interface LeftPaneViewController : NSViewController <NSTableViewDelegate, NSTableViewDataSource>
@property (weak) IBOutlet NSTableView *tableView;
@property (nonatomic, retain) NSMutableArray *tableDataSource;
在LeftPaneViewController.m中:
#import "LeftPaneViewController.h"
@interface LeftPaneViewController ()
@end
@implementation LeftPaneViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
}
return self;
}
- (void)awakeFromNib
{
self.tableDataSource = [[NSMutableArray alloc] initWithObjects:@"Row1", @"Row2", nil];
[self.tableView setNeedsDisplay];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
// This is a defensive move
// There are cases where the nib containing the NSTableView can be loaded before the data is populated
// by ensuring the count value is 0 and checking to see if the namesArray is not nil, the app
// is protecting itself agains that situation
NSInteger count=0;
if (self.tableDataSource)
count=[self.tableDataSource count];
return count;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// Get an existing cell with the MyView identifier if it exists
NSTextField *result = [tableView makeViewWithIdentifier:@"LeftPaneCell" owner:self];
// There is no existing cell to reuse so create a new one
if (result == nil) {
// Create the new NSTextField with a frame of the {0,0} with the width of the table.
// Note that the height of the frame is not really relevant, because the row height will modify the height.
NSTextField *textField;
textField = [[NSTextField alloc] initWithFrame:NSMakeRect(2, 456, 125, 20)];
[textField setStringValue:@"My Label"];
[textField setBezeled:NO];
[textField setDrawsBackground:NO];
[textField setEditable:NO];
[textField setSelectable:NO];
[self.view addSubview:textField];
// The identifier of the NSTextField instance is set to MyView.
// This allows the cell to be reused.
result.identifier = @"LeftPaneCell";
}
// result is now guaranteed to be valid, either as a reused cell
// or as a new cell, so set the stringValue of the cell to the
// nameArray value at row
result.stringValue = [self.tableDataSource objectAtIndex:row];
// Return the result
return result;
}
@end
在LeftPaneViewController.xib中,我钩住TableView数据源并委托(delegate)给“文件的所有者”(当然是LeftPaneViewController的)。并将tableView引用出口挂钩到LeftPaneViewController.h中的tableView。我检查了这些至少一千遍;-)
然后,当我运行时,我得到以下崩溃日志:
* thread #1: tid = 0x23427, 0x00007fff82893250 libobjc.A.dylib`objc_msgSend + 16, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x00007fff82893250 libobjc.A.dylib`objc_msgSend + 16
frame #1: 0x00007fff88f8d972 AppKit`-[NSTableRowData _addViewToRowView:atColumn:row:] + 324
frame #2: 0x00007fff88f8d63f AppKit`-[NSTableRowData _addViewsToRowView:atRow:] + 151
frame #3: 0x00007fff88f8bbd5 AppKit`-[NSTableRowData _addRowViewForVisibleRow:withPriorView:] + 415
frame #4: 0x00007fff88f8b95a AppKit`-[NSTableRowData _addRowViewForVisibleRow:withPriorRowIndex:inDictionary:withRowAnimation:] + 272
frame #5: 0x00007fff88f8ac29 AppKit`-[NSTableRowData _unsafeUpdateVisibleRowEntries] + 740
frame #6: 0x00007fff88f8a7c1 AppKit`-[NSTableRowData updateVisibleRowViews] + 119
frame #7: 0x00007fff88f625a7 AppKit`-[NSTableView layout] + 165
frame #8: 0x00007fff88f15e65 AppKit`-[NSView _layoutSubtreeHeedingRecursionGuard:] + 112
frame #9: 0x00007fff8d11b4a6 CoreFoundation`__NSArrayEnumerate + 582
frame #10: 0x00007fff88f15fc6 AppKit`-[NSView _layoutSubtreeHeedingRecursionGuard:] + 465
frame #11: 0x00007fff8d11b4a6 CoreFoundation`__NSArrayEnumerate + 582
frame #12: 0x00007fff88f15fc6 AppKit`-[NSView _layoutSubtreeHeedingRecursionGuard:] + 465
frame #13: 0x00007fff8d11b4a6 CoreFoundation`__NSArrayEnumerate + 582
frame #14: 0x00007fff88f15fc6 AppKit`-[NSView _layoutSubtreeHeedingRecursionGuard:] + 465
frame #15: 0x00007fff8d11b4a6 CoreFoundation`__NSArrayEnumerate + 582
frame #16: 0x00007fff88f15fc6 AppKit`-[NSView _layoutSubtreeHeedingRecursionGuard:] + 465
frame #17: 0x00007fff8d11b4a6 CoreFoundation`__NSArrayEnumerate + 582
frame #18: 0x00007fff88f15fc6 AppKit`-[NSView _layoutSubtreeHeedingRecursionGuard:] + 465
frame #19: 0x00007fff8d11b4a6 CoreFoundation`__NSArrayEnumerate + 582
frame #20: 0x00007fff88f15fc6 AppKit`-[NSView _layoutSubtreeHeedingRecursionGuard:] + 465
frame #21: 0x00007fff88f15cfe AppKit`-[NSView layoutSubtreeIfNeeded] + 615
frame #22: 0x00007fff88f114ac AppKit`-[NSWindow(NSConstraintBasedLayout) layoutIfNeeded] + 201
frame #23: 0x00007fff88e0b0a8 AppKit`_handleWindowNeedsDisplayOrLayoutOrUpdateConstraints + 446
frame #24: 0x00007fff893d6901 AppKit`__83-[NSWindow _postWindowNeedsDisplayOrLayoutOrUpdateConstraintsUnlessPostingDisabled]_block_invoke_01208 + 46
frame #25: 0x00007fff8d0e9417 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
frame #26: 0x00007fff8d0e9381 CoreFoundation`__CFRunLoopDoObservers + 369
frame #27: 0x00007fff8d0c47b8 CoreFoundation`__CFRunLoopRun + 728
frame #28: 0x00007fff8d0c40e2 CoreFoundation`CFRunLoopRunSpecific + 290
frame #29: 0x00007fff8c17aeb4 HIToolbox`RunCurrentEventLoopInMode + 209
frame #30: 0x00007fff8c17ab94 HIToolbox`ReceiveNextEventCommon + 166
frame #31: 0x00007fff8c17aae3 HIToolbox`BlockUntilNextEventMatchingListInMode + 62
frame #32: 0x00007fff88e08533 AppKit`_DPSNextEvent + 685
frame #33: 0x00007fff88e07df2 AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 128
frame #34: 0x00007fff88dff1a3 AppKit`-[NSApplication run] + 517
frame #35: 0x00007fff88da3bd6 AppKit`NSApplicationMain + 869
frame #36: 0x0000000100001872 TableView_In_SplitView`main(argc=3, argv=0x00007fff5fbff7e8) + 34 at main.m:13
frame #37: 0x00007fff84d4d7e1 libdyld.dylib`start + 1
通过断点,我确定在LeftPaneViewController.m中,调用了numberOfRowsInTableView并返回2,但未调用viewForTableColumn。
TableView是基于单元格的,因此我希望可以调用viewForTableColumn。
如果我在MainMenu.xib的SplitView中简单地添加一个NSTableView并将相同的代码传输到AppDelegate.h / .m中,它就可以正常工作(当然也可以更改钩子(Hook))。也就是说,它不会崩溃,并且numberOfRowsInTableView和viewForTableColumn都被调用。
那么,我该怎么做导致崩溃?
最佳答案
好的,找到答案了。由于AppDelegate.m中的LeftPaneViewController变量(lpvc)是本地变量,因此它将在replaceSubview调用之后释放。因此,在AppDelegate.h中将其添加为(非原子的,保留)可修复崩溃。打开“僵尸”以找到答案;-)。