ios - 如何在非ARC模式下手动释放此静态变量

标签 ios objective-c memory-management

有一个小的Toast实用程序源代码(https://github.com/Joyfl/JLToast),它似乎使用ARC。但是我想在手动保留释放(MRR)模式下使用它。
特别是,我不确定center = [[JLToastCenter alloc] init];+(id)defaultCenter中的JLToastCenter.m(用于ARC模式)是否应该重写为center = [[[JLToastCenter alloc] init] autorelease];(用于MRR模式),其中center声明为static id center = nil;
在此post中,@ mipadi给出的答案是:“如果变量仅初始化一次,并且应在应用程序的整个生命周期内保持不变,则否,则不应释放它(它的内存实际上将在释放时释放。应用程序退出,无论如何)”。我猜这是当center中的静态变量JLToastCenter.m但不确定的情况。
下面列出的我自己的MRR模式版本添加了release / autorelease / dealloc功能。我还将所有点符号都更改为消息传递样式。
源代码
源代码列表:JLToastCenter.hJLToastCenter.mJLToast.hJLToast.mJLToastView.hJLToastView.mJLToastCenter.h文件:

#import <Foundation/Foundation.h>

@class JLToast;

@interface JLToastCenter : NSObject
{
    NSOperationQueue *_queue;
}

+ (id)defaultCenter;

- (void)addToast:(JLToast *)toast;

@end
JLToastCenter.m文件:
#import "JLToastCenter.h"
#import "JLToast.h"

@implementation JLToastCenter

+ (id)defaultCenter
{
    static id center = nil;
    static dispatch_once_t onceToken; // It makes singleton object thread-safe
    dispatch_once(&onceToken, ^{
        center = [[[JLToastCenter alloc] init] autorelease]; // Added autorelease by me, originally as center = [[JLToastCenter alloc] init];
        [[NSNotificationCenter defaultCenter] addObserver:center selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
    });
    return center;
}

// Added by me
- (void)dealloc
{
    [_queue release];
    
    [super dealloc];
}

- (id)init
{
    self = [super init];
    if( self )
    {
        _queue = [[NSOperationQueue alloc] init];
        [_queue setMaxConcurrentOperationCount:1];
    }
    return self;
}

- (void)addToast:(JLToast *)toast
{
    [_queue addOperation:toast];
}

- (void)deviceOrientationDidChange:(id)sender
{
    if( [[_queue operations] count] )
    {
        [[[[_queue operations] objectAtIndex:0] view] layoutSubviews];
    }
}

@end
JLToast.h文件:
#import <UIKit/UIKit.h>

#define JLToastShortDelay   2.0f
#define JLToastLongDelay    3.5f

@class JLToastView;

@interface JLToast : NSOperation
{
    BOOL _isExecuting;
    BOOL _isFinished;
}

@property (nonatomic, strong) JLToastView *view;
@property (nonatomic, copy) NSString *text; // added by me
@property (nonatomic) NSTimeInterval delay;
@property (nonatomic) NSTimeInterval duration;

+ (id)makeText:(NSString *)text;
+ (id)makeText:(NSString *)text duration:(NSTimeInterval)duration;
+ (id)makeText:(NSString *)text delay:(NSTimeInterval)delay duration:(NSTimeInterval)duration;

- (void)show;
- (void)cancel;

@end
JLToast.m文件:
#import "JLToast.h"
#import "JLToastView.h"
#import "JLToastCenter.h"
#import <dispatch/dispatch.h>

@implementation JLToast

@synthesize view = _view; // added by me
@synthesize text = _text; // added by me

+ (id)makeText:(NSString *)text
{
    return [JLToast makeText:text delay:0 duration:JLToastShortDelay];
}

+ (id)makeText:(NSString *)text duration:(NSTimeInterval)duration
{
    return [JLToast makeText:text delay:0 duration:duration];
}

+ (id)makeText:(NSString *)text delay:(NSTimeInterval)delay duration:(NSTimeInterval)duration
{
    JLToast *toast = [[[JLToast alloc] init] autorelease]; // added autorelease by me
    [toast setText:text];
    [toast setDelay:delay];
    [toast setDuration:duration];
    
    return toast;
}

// added by me
- (void)dealloc
{
    [_view release];
    [_text release];
    
    [super dealloc];
}

- (id)init
{
    self = [super init];
    if( self )
    {
        _view = [[JLToastView alloc] init];
    }
    return self;
}

- (void)show
{
    [[JLToastCenter defaultCenter] addToast:self];
}

- (void)cancel
{
    
}


#pragma mark -
#pragma mark Getter/Setter

- (NSString *)text
{
    return [[_view textLabel] text];
}

- (void)setText:(NSString *)text
{
    [[_view textLabel] setText:text];
    //  [_view layoutSubviews];
}


#pragma mark -
#pragma mark NSOperation Overriding

- (BOOL)isConcurrent
{
    return YES;
}

- (void)start
{
    if( ![NSThread isMainThread] )
    {
        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        return;
    }
    [super start];
}

- (void)main{
    [self willChangeValueForKey:@"isExecuting"];
    
    _isExecuting = YES;
    
    [self didChangeValueForKey:@"isExecuting"];
    
    dispatch_async(dispatch_get_main_queue(), ^{ // Non-main thread cannot modify user interface
        [_view layoutSubviews]; // Calls layoutSubviews before being-shown. added by the original creator devxoul at around 20131013
        [_view setAlpha:0];
        [[[UIApplication sharedApplication] keyWindow] addSubview:_view];
        [UIView animateWithDuration:0.5 delay:_delay options:UIViewAnimationOptionBeginFromCurrentState animations:^{
            [_view setAlpha:1];
        } completion:^(BOOL finished) {
            [UIView animateWithDuration:_duration animations:^{
                [_view setAlpha:1.0001];
            } completion:^(BOOL finished) {
                [self finish];
                [UIView animateWithDuration:0.5 animations:^{
                    [_view setAlpha:0];
                }];
            }];
        }];
    });
}

- (void)finish
{
    [self willChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isFinished"];
    
    _isExecuting = NO;
    _isFinished = YES;
    
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

- (BOOL)isExecuting
{
    return _isExecuting;
}

- (BOOL)isFinished
{
    return _isFinished;
}

@end
JLToastView.h文件:
#import <UIKit/UIKit.h>

@interface JLToastView : UIView

@property (nonatomic, strong) UIView *backgroundView;
@property (nonatomic, strong) UILabel *textLabel;
@property (nonatomic) UIEdgeInsets textInsets;

@end
JLToastView.m文件:
#import "JLToastView.h"
#import <QuartzCore/CALayer.h>

#define JLTOAST_LABEL_FONT_SIZE ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) ? 12 : 16)
#define JLTOAST_OFFSET_PORTRAIT_Y ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) ? 30 : 60)
#define JLTOAST_OFFSET_LANDSCAPE_Y ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) ? 20 : 40)

@implementation JLToastView

// added by congliu at 20131031Thu 1000am
- (void)dealloc
{
    [_backgroundView release];
    [_textLabel release];
    
    [super dealloc];
}

- (id)init
{
    self = [super init];
    if( self )
    {
        _backgroundView = [[UIView alloc] initWithFrame:CGRectMake( 0, 0, 100, 100 )];
        [_backgroundView setBackgroundColor:[UIColor colorWithWhite:0 alpha:0.7]];
        [[_backgroundView layer] setCornerRadius:5];
        [_backgroundView setClipsToBounds:YES];
        [self addSubview:_backgroundView];
        
        _textLabel = [[UILabel alloc] initWithFrame:CGRectMake( 0, 0, 100, 100 )];
        [_textLabel setTextColor:[UIColor whiteColor]];
        [_textLabel setBackgroundColor:[UIColor clearColor]];
        [_textLabel setFont:[UIFont systemFontOfSize:JLTOAST_LABEL_FONT_SIZE]];
        [_textLabel setNumberOfLines:0];
        [self addSubview:_textLabel];
        
        _textInsets = UIEdgeInsetsMake( 6, 10, 6, 10 );
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    CGFloat deviceWidth = [[UIScreen mainScreen] bounds].size.width;
    
    UIFont *font = [_textLabel font];
    CGSize constraintSize = CGSizeMake( deviceWidth * (280.0f/320.0f), INT_MAX );
    CGSize textLabelSize = [[_textLabel text] sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
    
    [_textLabel setFrame:CGRectMake( _textInsets.left, _textInsets.top, textLabelSize.width, textLabelSize.height )];
    [_backgroundView setFrame:CGRectMake( 0, 0,
                                       [_textLabel frame].size.width + _textInsets.left + _textInsets.right,
                                       [_textLabel frame].size.height + _textInsets.top + _textInsets.bottom )];
    
    NSInteger x, y, width, height;
    CGFloat angle;
    switch( [[UIDevice currentDevice] orientation] )
    {
        case UIDeviceOrientationPortraitUpsideDown:
            width = [_backgroundView frame].size.width;
            height = [_backgroundView frame].size.height;
            x = ([[UIScreen mainScreen] bounds].size.width - width) / 2;
            y = JLTOAST_OFFSET_PORTRAIT_Y;
            angle = M_PI;
            break;
            
        case UIDeviceOrientationLandscapeLeft:
            width = [_backgroundView frame].size.height;
            height = [_backgroundView frame].size.width;
            x = JLTOAST_OFFSET_LANDSCAPE_Y;
            y = ([[UIScreen mainScreen] bounds].size.height - height) / 2;
            angle = M_PI_2;
            break;
            
        case UIDeviceOrientationLandscapeRight:
            width = [_backgroundView frame].size.height;
            height = [_backgroundView frame].size.width;
            x = [[UIScreen mainScreen] bounds].size.width - width - JLTOAST_OFFSET_LANDSCAPE_Y;
            y = ([[UIScreen mainScreen] bounds].size.height - height) / 2;
            angle = -M_PI_2;
            break;
            
        default:
            width = [_backgroundView frame].size.width;
            height = [_backgroundView frame].size.height;
            x = ([[UIScreen mainScreen] bounds].size.width - width) / 2;
            y = [[UIScreen mainScreen] bounds].size.height - height - JLTOAST_OFFSET_PORTRAIT_Y;
            angle = 0;
            break;
            
    }
    
    [self setTransform:CGAffineTransformMakeRotation( angle )];
    [self setFrame:CGRectMake( x, y, width, height )];
}

#pragma mark - hit test

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    //    NSLog(@"%@ hitTest", [self class]);
    return nil;
}

@end

最佳答案

摆脱自动发布!
[JLToastCenter defaultCenter]应该始终返回相同的对象。第一次调用它时,它将创建对象。 (之所以称为“惰性初始化”,是因为仅在需要时才创建对象),然后它将指向共享对象的指针存储在静态变量中以使其保持不变。

如果要添加autorelease,则将在下一次当前的autoreleasepool耗尽时创建并释放该对象。然后,静态变量将包含指向已释放对象的指针。下次您再调用[JLToastCenter defaultCenter],然后向已发布的对象发送消息时,可能会发生各种事情(您的应用程序可能会崩溃)。

关于ios - 如何在非ARC模式下手动释放此静态变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19752081/

相关文章:

ios - 由于资源未随机正确加载而导致应用程序崩溃

ios - 禁用从导航 Controller 返回到上一个 View Controller

c++ - 使用 new 运算符返回指针。删除该放在哪里?

iphone - 关于CGPoint的内存管理

Android 堆大小限制,我们真的还需要在设计应用程序时牢记 16 MB 的限制吗?

ios - 球体 : openConnectionWithSpecificRobot or analog in official SDK for iOS

ios - UICollectionView 性能 - _updateVisibleCellsNow

ios - 无法从 UICollectionViewCell 中删除图像

objective-c - 自定义 NSCursor 以黑色四边形闪烁

objective-c - UIWebView不会隐藏键盘