inputAssistantItem 和 inputAccessoryView 停止使用 ARC

inputAssistantItem and inputAccessoryView stopped working with ARC

我正在使用此代码在我的 iOS 应用程序的屏幕键盘上方添加一些按钮。请注意,我在手机和 pre-iOS 9 设备上使用较旧的 inputAccessoryView 方法,在 iOS 9 平板电脑上使用较新的 inputAssistantItem 方法:

UITextView *textInputMultiline = [[UITextView alloc] initWithFrame:frame];
TextInputToolbar *textInputToolbar = [TextInputToolbar alloc]; // custom class
(void)[textInputToolbar initWithNibName:@"TextInputToolbar" bundle:nil];
textInputToolbar.textView = textInputMultiline;
if ((self.appDelegate.isTablet)&&([[[UIDevice currentDevice] systemVersion] compare:@"9.0"] != NSOrderedAscending)) {
    NSMutableArray *barButtonItems = [NSMutableArray array];
    [barButtonItems addObject:[[UIBarButtonItem alloc] initWithTitle:@"button 1" style:UIBarButtonItemStylePlain target:textInputToolbar action:@selector(button1)]];
    [barButtonItems addObject:[[UIBarButtonItem alloc] initWithTitle:@"button 2" style:UIBarButtonItemStylePlain target:textInputToolbar action:@selector(button2)]];
    [barButtonItems addObject:[[UIBarButtonItem alloc] initWithTitle:@"button 3" style:UIBarButtonItemStylePlain target:textInputToolbar action:@selector(button3)]];
    UIBarButtonItem *representativeItem = nil;
    UIBarButtonItemGroup *group = [[UIBarButtonItemGroup alloc] initWithBarButtonItems:barButtonItems representativeItem:representativeItem];
    textInputMultiline.inputAssistantItem.trailingBarButtonGroups = [NSArray arrayWithObject:group];
} else {
    textInputMultiline.inputAccessoryView = textInputToolbar.view;
}

我的自定义工具栏 class 如下所示:

@interface TextInputToolbar : UIViewController {
    UITextView *textView;

    IBOutlet UIButton *button1;
    IBOutlet UIButton *button2;
    IBOutlet UIButton *button3;
}

@property (nonatomic, strong) UITextView *textView;

- (void)insertText:(NSString *)text;

- (IBAction)button1;
- (IBAction)button2;
- (IBAction)button3;

@end

还有...

#import "TextInputToolbar.h"

@implementation TextInputToolbar

@synthesize textView;

- (void)viewDidLoad {
    NSLog(@"viewDidLoad");
    [super viewDidLoad];
}

- (void)insertText:(NSString *)text {
    [self.textView insertText:text];
}

- (IBAction)button1 {
    NSLog(@"button1");
    [self insertText:@"1"];
}

- (IBAction)button2 {
    NSLog(@"button2");
    [self insertText:@"2"];
}

- (IBAction)button3 {
    NSLog(@"button3");
    [self insertText:@"3"];
}

@end

当我的应用程序未使用 ARC 时,这按预期工作。我最近更新到 ARC,它只需要对上面的代码进行最小的更改(我之前在 UIBarButtonItems 上有自动释放,并且在 initWithNibName 之前没有 (void) 转换),并且现在按钮仍然按预期显示,但不起作用。在 iOS 8 上,我在点击其中一个按钮时崩溃([CALayer button1]: unrecognized selector sent to instance,我认为这表明内存指针无效),而在 iOS 9 上,当我点击一个按钮时没有任何反应按钮,并且不调用按钮方法中的日志记录。

我在更新到 ARC 之前有一个项目的副本,当我返回 运行 在我的 iOS 8 或 iOS 9 设备上时,工具栏按钮可以工作再次。所以看起来 ARC 要么是问题的根源,要么是另一个问题的触发因素。

如果我将 barButtonItem 指向自己,就像这样...

[barButtonItems addObject:[[UIBarButtonItem alloc] initWithTitle:@"button 3" style:UIBarButtonItemStylePlain target:self action:@selector(test)]];

...按预期收到方法调用。如果我将 barButtonItem 选择器更改为无效方法,就像这样...

[barButtonItems addObject:[[UIBarButtonItem alloc] initWithTitle:@"button 3" style:UIBarButtonItemStylePlain target:textInputToolbar action:@selector(flkjfd)]];

...没有任何反应。这向我表明,当按钮选择器被调用时,textInputToolbar 不知何故变成了 nil,因为如果它不是 nil,这将导致无法识别的选择器崩溃。

但我知道 TextInputToolbar class 及其视图正在加载,因为发生了 viewDidLoad 登录,并且视图显示为 inputAccessoryView 和 iOS 8 平板电脑。

知道发生了什么,或者我还能做些什么来解决问题?

您的代码从来都不是正确的,只是因为泄漏才起作用。你基本上失去/不保留视图控制器。它曾经只是继续存在和工作,但在 ARC 下它被释放了,所以没有什么可以响应按钮。 ARC 已经解决了您的记忆问题,并让您意识到存在问题,尽管不是以理想的方式。

要修复,请在使用视图时保留视图控制器。

另外,我不确定你是从哪里学来的:

TextInputToolbar *textInputToolbar = [TextInputToolbar alloc]; // custom class
(void)[textInputToolbar initWithNibName:@"TextInputToolbar" bundle:nil];

但你不应该。在一条线上完成所有操作。不要忽略从 init 调用返回的对象 - 它可能与您最初调用它的对象不同。

调用这些代码时发生了什么:

UITextView *textInputMultiline = [[UITextView alloc] initWithFrame:frame];

//alloc textInputToolbar (textInputToolbar.retaincount = 1)
TextInputToolbar *textInputToolbar = [TextInputToolbar alloc];
textInputToolbar.textView = textInputMultiline;

if ((self.appDelegate.isTablet)&&([[[UIDevice currentDevice] systemVersion] compare:@"9.0"] != NSOrderedAscending)) {
    NSMutableArray *barButtonItems = [NSMutableArray array];
    //add button items....
    UIBarButtonItem *representativeItem = nil;

    //alloc UIBarButtonItemGroup (group.retaincount = 1)
    UIBarButtonItemGroup *group = [[UIBarButtonItemGroup alloc] initWithBarButtonItems:barButtonItems representativeItem:representativeItem];

    //strong reference group (group.retaincount = 2)
    textInputMultiline.inputAssistantItem.trailingBarButtonGroups = [NSArray arrayWithObject:group];
    //autorelease group 

} else {
    //strong reference textInputToolbar.view (textInputToolbar.view.retaincount = 2)
    textInputMultiline.inputAccessoryView = textInputToolbar.view;
}

//autorelease textInputToolbar (textInputToolbar.retaincount = 0, textInputToolbar.view.retaincount = 1)

在 iOS 8 中,textInputToolbar 将被释放,但它的视图不会。这就是为什么你可以看到按钮,但是当你点击它们时,观察者变成了一个野指针,运行时找不到函数所以它崩溃了。

在iOS9中,textInputToolbar也将被dealloc。由于您创建了按钮项并将观察(弱引用)目标设置在 InputToolbar 之外,因此当 textInputToolbar dealloc 时,观察变为 nil.So 函数将不会被调用。