使用 self.delegate 子类化 UITextField 会导致应用程序冻结,CPU 峰值达到 100%

Posted

技术标签:

【中文标题】使用 self.delegate 子类化 UITextField 会导致应用程序冻结,CPU 峰值达到 100%【英文标题】:Subclassing UITextField with self.delegate causes app to freeze, CPU spikes to 100% 【发布时间】:2014-10-21 13:43:43 【问题描述】:

我有一个UITextField 的子类,它设置self.delegate = self。该子类用于防止将特殊字符输入UITextField。起初它工作正常,但在按下几个键后,CPU 飙升至 100% 并冻结应用程序。 Xcode 中没有崩溃日志,因为该应用程序实际上从未崩溃,它只是保持冻结状态,直到我停止它。经过一番研究,我确定问题在于将delegate 设置为self - 显然我应该为UITextField 创建一个单独的delegate?我在网上搜索过,但找不到任何有用的方法。

我的 AcceptedCharacters 子类:

AcceptedCharacters.h

#import <UIKit/UIKit.h>

@interface AcceptedCharacters : UITextField <UITextFieldDelegate>

@end

AcceptedCharacters.m

#import "AcceptedCharacters.h"
#define ACCEPTABLE_CHARACTERS @" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."

@implementation AcceptedCharacters

- (void)awakeFromNib

    [super awakeFromNib];

    if (self) 
        self.delegate = self;
    


- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
    //NSLog(@"AcceptedCharacters");
    // Restrict special characters
    NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS] invertedSet];
    NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];

    return [string isEqualToString:filtered];


@end

我在堆栈溢出中发现的类似问题:

Application freezes after editing custom UITextField

UITextField delegate jumping to 100% CPU usage and crashing upon using keyboard shortcut

Why does UITextField lock up when setting itself to delegate

我已经阅读了解决方案,但对于初学者来说它们非常模糊。我将非常感谢有人在下面修改我的代码,甚至将我指向教程或文档的方向?提前致谢。

编辑:

我已经在我的ViewControllers 之一上测试了此代码,当符合UITextFieldDelegate 协议并且我的文本字段delegates 设置为self 时它工作正常(见下面的sn-p)。

在我的viewDidLoad 方法中:

self.forenameField.delegate = self;
self.surnameField.delegate = self;
self.addLine1Field.delegate = self;
self.addLine2Field.delegate = self;
self.addLine3Field.delegate = self;
self.addLine4Field.delegate = self;
self.addLine5Field.delegate = self;
self.postcodeField.delegate = self;
self.telLandField.delegate = self;
self.telMobField.delegate = self;
self.emailField.delegate = self;
self.dobField.delegate = self;
self.niNumField.delegate = self;

我宁愿创建UITextField 的几个子类并在特定的文本字段上调用它们,而不是像下面那样使用混乱的方法。

#define ACCEPTABLE_CHARACTERS @" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."
#define NUMBERS_ONLY @"1234567890"
#define CHARACTER_LIMIT 11
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
    // Restrict special characters
    NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS] invertedSet];
    NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];

    // Convert postcode field characters to uppercase
    if (textField == self.postcodeField) 
        [textField setText:[textField.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]]];
        return NO;
    

    // Set max character limit on tel fields
    if (textField == self.telMobField || textField == self.telLandField) 
        NSUInteger newLength = [textField.text length] + [string length] - range.length;
        NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:NUMBERS_ONLY] invertedSet];
        NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
        return (([string isEqualToString:filtered])&&(newLength <= CHARACTER_LIMIT));
    
    //return YES;
    return [string isEqualToString:filtered];

【问题讨论】:

我认为这个答案应该可以很好地描述它:***.com/a/8049532/2708650 我对你的建议是永远不要以任何理由在任何课程上致电self.delegate = self。拥有委托的原因是因为类本身无法处理状态。如果您将 self 指定为委托,则意味着您的类确实处理状态。 感谢您的链接,但我已阅读该帖子数次,仍然找不到解决方案。我可以将textField shouldChangeCharactersInRange: 方法添加到我的班级并在其中分配self.myTextField.delegate = self;,它工作正常,尽管我的一些ViewControllers 有许多UITextFields,它们都做不同的事情。与其有一个不整洁的textField shouldChangeCharactersInRange: 方法,不如在不同的文本字段上调用不同的子类更有效吗?请以我更新的帖子为例。 【参考方案1】:

试试这个:

@interface MyTextField : UITextField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range;
@end
@implementation MyTextField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range 
  NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS] invertedSet];
  NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];

  return [string isEqualToString:filtered];

@end

@interface PostalCodeTextField : MyTextField
@end
@implementation PostalCodeTextField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range 
  [self setText:[self.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]]];
  return NO;

@end

.. more subclasses

然后将您的视图控制器分配为委托并在您的视图控制器中:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
  if ([textField isKindOfClass:[MyTextField class]]) 
    return [(MyTextField *)textField stringIsAcceptable:string inRange:range];
  
  return YES;

【讨论】:

我对你的回答有点困惑,你的意思是把这段代码写在我的ViewController 类上,该类包含textfields?我不想在实际的ViewController 类上调用textField delegate 方法,请参阅我更新的帖子,因为我已经这样做了,当有多个textfields 时,该方法最终变得混乱且难以调试做不同的事情。或者你的意思是用你的代码修改我的subclass?抱歉,我还在学习子类和委托,谢谢。 不必那么乱。我会为你进一步扩展我的答案。 抱歉回复晚了,我刚刚实现了你的代码,它运行良好!这是一个比我使用的更优雅的解决方案,现在我可以根据需要创建尽可能多的子类,并在我的 ViewController 类的 shouldChangeCharactersInRange: delegate 方法上调用它们。我真的很喜欢这个解决方案,非常感谢您花时间帮助我:)

以上是关于使用 self.delegate 子类化 UITextField 会导致应用程序冻结,CPU 峰值达到 100%的主要内容,如果未能解决你的问题,请参考以下文章

“self.delegate = self”不能在使用 ARC 的 iOS 上运行

在 self.delegate 实现 UIAlertViewDelegate 协议的 alertView 中设置 delegate:self.delegate

以核心数据实体作为属性的子类 UITextField

如何在许多子类中使用 UITableViewDelegate 子类化 UIViewController

使用 Swift 子类化 SKShapeNode

子类化 UIAlertController