1

ARC を使用していると思われる小さなトースト ユーティリティ ソース コード ( https://github.com/Joyfl/JLToast ) があります。しかし、手動保持解放 (MRR) モードで使用したいです。

特に、center = [[JLToastCenter alloc] init];(ARC モードの場合)+(id)defaultCenterのを(MRR モードの場合) に書き換えるJLToastCenter.mべきかどうかはわかりません。ここで、は として宣言されています。center = [[[JLToastCenter alloc] init] autorelease];centerstatic id center = nil;

この投稿では、@mipadi の回答によると、「変数が 1 回だけ初期化され、アプリケーションの存続期間中存続する必要がある場合は、いいえ、解放されるべきではありません (そのメモリは、とにかく、アプリケーションは終了します)」。centerこれは、静的変数がJLToastCenter.m入っているが、それについてはわからない場合に当てはまると思います。

以下にリストされているMRRモードの私自身のバージョンは、リリース/自動リリース/割り当て解除を追加しました。また、すべてのドット表記をメッセージング スタイルに変更しました。

ソースコード

ソースコード一覧:

JLToastCenter.h

JLToastCenter.m

JLToast.h

JLToast.m

JLToastView.h

JLToastView.m

JLToastCenter.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
4

2 に答える 2

3

リンク先の質問への回答は、まさにあなたのケースに当てはまります。により dispatch_once()

center = [[JLToastCenter alloc] init]; // correct

defaultCenterが最初に呼び出されたときに、アプリケーションの存続期間中に 1 回だけ実行されます。後続の呼び出しdefaultCenterは変数の内容を返すだけcenterなので、そのオブジェクトを存続させたいとします。

center = [[[JLToastCenter alloc] init] autorelease]; // wrong

プログラム制御がメイン イベント ループに戻り、現在の自動解放プールが終了するとすぐに、オブジェクトが解放されます (場合によっては解放されます)。

したがって、autoreleaseここではありません!(しかし、なぜプロジェクトを ARC から MRC に変換したいのですか??)

于 2013-11-03T11:02:57.723 に答える
3

オートリリースをなくせ!

[JLToastCenter defaultCenter]常に同じオブジェクトを返す必要があります。初めて呼び出すと、オブジェクトが作成されます。(必要な場合にのみオブジェクトを作成するため、これは「遅延初期化」と呼ばれます) 次に、共有オブジェクトへのポインターを静的変数に格納して保持します。

を追加するautoreleaseと、オブジェクトが作成され、次に現在の autoreleasepool がドレインされたときに解放されます。次に、静的変数には、解放されたオブジェクトへのポインターが含まれます。次に を呼び出し[JLToastCenter defaultCenter]て、解放されたオブジェクトにメッセージを送信すると、あらゆる種類のことが発生する可能性があります (アプリがクラッシュする可能性があります)。

于 2013-11-03T11:04:26.753 に答える