Fuck self.delegate = self

故事背景如下,我有一个UITextField的子类MyTextField,创建了MyTextField的实例tf。我希望tf对应的键盘显示done按钮。代码如下:

 1 @interface MyTextField : UITextField
 2 @end
 3 
 4 MyTextField* tf = [[MyTextField alloc] initWithFrame:someframe];
 5 tf.returnKeyType = UIReturnKeyDone;
 

然后我希望,当done被按下的时候,键盘关闭,通常的做法是给TextField一个delegate,监听textFieldShouldReturn,当done被按下时,会回调delegate的textFieldShouldReturn方法,代码如下:

 8 @interface TextFieldDelegate : NSObject<UITextFieldDelegate>
 9 @end
10 
11 @implementation TextFieldDelegate
12 - (BOOL)textFieldShouldReturn:(UITextField *)textField
13 {
14     [textField resignFirstResponder];
15     return YES;
16 }
17 @end
18 
19 tf.delegate = [[TextFieldDelegate alloc] init];

这是比较常规的做法,但是要新建一个类,新建一个对象,用完了还要释放这个对象,成本很大。这时我产生了个偷懒的想法,我自己的事情我就自己做吧,我做我自己的代理,self.delegate = self。代码如下,

23 @interface MyTextField : UITextField<UITextFieldDelegate>
24 @end
25 
26 @implementation MyTextField
27 - (BOOL)textFieldShouldReturn:(UITextField *)textField
28 {
29     [textField resignFirstResponder];
30     return YES;
31 }
32 @end
33 
34 MyTextField* tf = [[MyTextField alloc] initWithFrame:someframe];
35 tf.returnKeyType = UIReturnKeyDone;
36 tf.delegate = tf;
37 

干净利落,然后恶梦就开始了。运行这段代码,点击输入框,先是程序就再不响应了,XCode也没什么有用的提示,试一两次,XCode也不再响应了。


这个问题不是我自己解决的,我求助了google。在MyTextField的实现中加入以下函数,

39 - (BOOL)respondsToSelector:(SEL)aSelector
40 {
41     return [super respondsToSelector:aSelector];
42 }

在这个函数中打个断点,再次执行,程序运行到断点后继续执行,会不断的运行到这个断点,然后不断继续,会发现,最终程序在这里无限递归了,参数始终是keyboardInputChangedSelection​,直到栈溢出,程序崩溃。


现在我们知道原因了,为什么呢?猜想一下,这个函数的内部大概会是这样实现

45 - (BOOL)respondsToSelector:(SEL)aSelector
46 {
47     switch(aSelector)
48     {
49         ...
50         case keyboardInputChangedSelection:
51             return [self.delegate respondsToSelector:aSelector];
52         ...
53     }
54 }
 

这个问题已经不在我们能看到的代码层面了,所以即便能解决,也只能非常暴力的重写respondsToSelector,这个和我们要偷懒的初衷不符。


总结一下,self.delegate = self这种写法,在遇到

56 -(void)method
57 {
58     [self.delegate method];
59 }

这种用法的时候,会发生无限递归导致崩溃,一定要避免。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
struct VideoPicker: UIViewControllerRepresentable { @Environment(.presentationMode) private var presentationMode let sourceType: UIImagePickerController.SourceType // let onImagePicked: (UIImage) -> Void let onURLPicked: (URL) -> Void final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { @Binding private var presentationMode: PresentationMode private let sourceType: UIImagePickerController.SourceType private let onURLPicked: (URL) -> Void init(presentationMode: Binding<PresentationMode>, sourceType: UIImagePickerController.SourceType, onURLPicked: @escaping (URL) -> Void) { presentationMode = presentationMode self.sourceType = sourceType self.onURLPicked = onURLPicked } func imagePickerController( picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { // let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage // onImagePicked(uiImage) if let url = info[.mediaURL] as? URL{ onURLPicked(url) } presentationMode.dismiss() } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { presentationMode.dismiss() } } func makeCoordinator() -> Coordinator { return Coordinator(presentationMode: presentationMode, sourceType: sourceType, onURLPicked: onURLPicked) } func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPicker>) -> UIImagePickerController { let picker = UIImagePickerController() picker.sourceType = sourceType picker.delegate = context.coordinator picker.mediaTypes = ["public.movie"] return picker } func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<VideoPicker>) { } }这段代码获取的url中绝对路径不准确
05-24
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值