UIActionSheet关闭动画过程中调用delegate = nil 导致的内存泄露

UIActionSheet在动画期间(ActionSheet button点击之后,到didDismissWithButtonIndex调用完成之前)设置delegate为空会导致delegate无法释放。

先来看个例子:

例子中创建一个UIActionSheet,并在按钮点击之后0.1秒(关闭动画结束前)设置delegate = nil。

#import "LIViewController.h"
@class UIActionSheetDelegateImpl;
static UIActionSheetDelegateImpl * delegateImpl; 

@interface UIActionSheetDelegateImpl : NSObject <UIActionSheetDelegate>

@end

@implementation UIActionSheetDelegateImpl
// Called when a button is clicked. The view will be automatically dismissed after this call returns
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSLog(@"%s", __func__);

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        delegateImpl = nil;
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        actionSheet.delegate = nil;
    });
}
- (void)actionSheetCancel:(UIActionSheet *)actionSheet
{
    NSLog(@"%s", __func__);
}

- (void)willPresentActionSheet:(UIActionSheet *)actionSheet
{
    NSLog(@"%s\n", __func__);
}
- (void)didPresentActionSheet:(UIActionSheet *)actionSheet
{
    NSLog(@"%s\n", __func__);
}
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    NSLog(@"%s\n", __func__);
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    NSLog(@"%s\n", __func__);
}

- (void)dealloc
{
    NSLog(@"%s\n", __func__);
}
@end

@interface LIViewController ()

@end

@implementation LIViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated
{
    delegateImpl = [UIActionSheetDelegateImpl new];

    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"测试" delegate:delegateImpl cancelButtonTitle:@"cancel" destructiveButtonTitle:@"ok" otherButtonTitles:nil, nil];
    [actionSheet showInView:self.view];
}
@end

输出为:

[UIActionSheetTest[62028:60b] -[UIActionSheetDelegateImpl willPresentActionSheet:]
[UIActionSheetTest[62028:60b] -[UIActionSheetDelegateImpl didPresentActionSheet:]
[UIActionSheetTest[62028:60b] -[UIActionSheetDelegateImpl actionSheet:clickedButtonAtIndex:]
[UIActionSheetTest[62028:60b] -[UIActionSheetDelegateImpl actionSheet:willDismissWithButtonIndex:]

可以看到 UIActionSheetDelegateImpl delloc和actionSheet:willDismissWithButtonIndex并未调用, 也就是说UIActionSheetDelegateImpl对象并未释放。

当去掉delegate = nil调用时输出结果如下:

[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl willPresentActionSheet:]
[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl didPresentActionSheet:]
[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl actionSheet:clickedButtonAtIndex:]
[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl actionSheet:willDismissWithButtonIndex:]
[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl actionSheet:didDismissWithButtonIndex:]
[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl dealloc]

或者延长delegate = nil 调用时间为0.5秒之后(UIActionSheet关闭动画结束)也会输出上面结果, 也就是delegate = nil在actionSheet:didDismissWithButtonIndex:之后调用也能释放delegate。

如果直接在actionSheet:clickedButtonAtIndex:调用delegate = nil(actionSheet:willDismissWithButtonIndex:和actionSheet:didDismissWithButtonIndex:不会调用),dealloc能正常调用,
输出结果如下:

[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl willPresentActionSheet:]
[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl didPresentActionSheet:]
[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl actionSheet:clickedButtonAtIndex:]
[UIActionSheetTest[62086:60b] -[UIActionSheetDelegateImpl dealloc]

问题分析, 对动画期间设置delegate = nil导致不能释放retain/release如下:

#   Event Type  ? RefCt RefCt   Timestamp   Responsible Library Responsible Caller
0   Malloc  +1  1   00:06.236.929   UIActionSheetTest   -[LIViewController viewDidAppear:]
1   Retain  +1  2   00:10.498.951   UIKit   +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:]
2   Release -1  1   00:11.286.073   libdispatch.dylib   _dispatch_client_callout

如果在动画之后调用delegate = nil

#   Event Type  ? RefCt RefCt   Timestamp   Responsible Library Responsible Caller
0   Malloc  +1  1   00:01.150.499   UIActionSheetTest   -[LIViewController viewDidAppear:]
1   Retain  +1  2   00:02.919.288   UIKit   +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:]
2   Release -1  1   00:03.328.439   UIKit   -[UIViewAnimationState sendDelegateAnimationDidStop:finished:]
3   Release -1  0   00:04.286.073   libdispatch.dylib   _dispatch_client_callout

动画之前调用delegate= nil,结果如下:

#   Event Type  ? RefCt RefCt   Timestamp   Responsible Library Responsible Caller
0   Malloc  +1  1   00:06.236.929   UIActionSheetTest   -[LIViewController viewDidAppear:]
1   Release -1  0   00:04.286.073   libdispatch.dylib   _dispatch_client_callout

从中可以看到, 当_setupAnimationWithDuration和sendDelegateAnimationDidStop不配对时delegate就不能释放。

UIActionSheet关闭动画过程中调用delegate = nil 导致的内存泄露

时间: 2024-10-13 23:25:19

UIActionSheet关闭动画过程中调用delegate = nil 导致的内存泄露的相关文章

只能在执行Render() 的过程中调用 RegisterForEventValidation;

October 17, 2008  3:28 PMMarch 29, 2013  8:28 PM Aillo 编程   0 Comments 在实现"将GridView中的数据导出到Excel中"的时候出现了如下错误: 用户代码未处理 InvalidOperationException 只能在执行 Render() 的过程中调用 RegisterForEventValidation: EnableEventValidation属性是 .NET Framework 2.0 中是新增的属性

只能在执行 Render() 的过程中调用 RegisterForEventValidation(RegisterForEventValidation can only be called during Render();

只能在执行 Render() 的过程中调用 RegisterForEventValidation(RegisterForEventValidation can only be called during Render(); 当用GridView导出Execl的时候,会发生只能在执行 Render() 的过程中调用 RegisterForEventValidation的错误提示.有两种方法可以解决以上问题:1.修改web.config(不推荐)<pages enableEventValidatio

Error 之 只能在执行Render() 的过程中调用 RegisterForEventValidation;

在实现"将GridView中的数据导出到Excel中"的时候出现了如下错误: 用户代码未处理 InvalidOperationException 只能在执行 Render() 的过程中调用 RegisterForEventValidation: EnableEventValidation属性是 .NET Framework 2.0 中是新增的属性,默认的情况下该属性的值为true:通过这个新增的功能ASP.NET会检查 POST方法中的所带的参数,如果认为不合法,就会抛出异常.这个设计

Effective C++ 条款九、十 绝不在构造和析构过程中调用virtual函数|令operator=返回一个reference to *this

  1.当在一个子类当中调用构造函数,其父类构造函数肯定先被调用.如果此时父类构造函数中有一个virtual函数,子类当中也有,肯定执行父类当中的virtual函数,而此时子类当中的成员变量并未被初始化,所以无法调用子类与之对应的函数.即为指向虚函数表的指针vptr没被初始化又怎么去调用派生类的virtual函数呢?析构函数也相同,派生类先于基类被析构,又如何去找派生类相应的虚函数? 2.做法:将子类的某个函数改为non-virtual,然后在子类构造函数中传递参数给父类函数.然后父类的构造函数

Effective C++ Item 9 绝不在构造和析构过程中调用virtual函数

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层) 示例: <pre name="code" class="cpp">#include <iostream> #include <string> using namespace std; c

"只能在执行Render()的过程中调用RegisterForEventValidation" 解决方案

开发中遇到令人蛋疼的问题: 只能在执行Render()的过程中调用RegisterForEventValidation 当出现的异常的提示: 异常详细信息: System.InvalidOperationException:只能在执行Render()的过程中调用RegisterForEventValidation: 不要伤心,下面是解决办法: 出现这个异常,解决问题只需要在<%@ Page %> 指令中加上 EnableEventValidation="false" &qu

Effective C++_笔记_条款09_绝不在构造和析构过程中调用virtual函数

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为方便采用书上的例子,先提出问题,在说解决方案. 1 问题 1: class Transaction{ 2: public: 3: Transaction(); 4: virtual void LogTransaction() const = 0 ; 5: ... 6: }; 7:  8: Transaction::Transaction() //Base cl

问题:只能在执行 Render() 的过程中调用 RegisterForEventValidation;结果:只能在执行 Render() 的过程中调用 RegisterForEventValidation

只能在执行 Render() 的过程中调用 RegisterForEventValidation 当在导出Execl或Word的时候,会发生只能在执行 Render() 的过程中调用 RegisterForEventValidation的错误提示.下面的2中方法是在网上找到的,没有应用:1.修改web.config(不推荐) <pages enableEventValidation ="false" ></pages> 2.直接在导出Execl的页面修改 <

SQL Server 2005 sa登录失败。已成功与服务器建立连接 但是在登录过程中发生错误。 provider 共享内存提供程序 error 0 管道的另一端上无任何进程。

SQL Server 2005 Express版 用户 'sa' 登录失败.该用户与可信 SQL Server 连接无关联.提示错误:已成功与服务器建立连接 但是在登录过程中发生错误. provider 共享内存提供程序 error 0 管道的另一端上无任何进程. 解决方案: 1.首先选中服务器(右键)->属性->安全性->服务器身份验证修改为"SQL SERVER和WINDOWS身份验证模式"2.其次展开服务器下面的"安全性"文件夹->登陆