There is a small toast utility source code (https://github.com/Joyfl/JLToast) which seems to use ARC. But I want to use it in manual retain-release (MRR) mode.
In particular, I'm not sure if the center = [[JLToastCenter alloc] init];
(for ARC mode) in +(id)defaultCenter
of JLToastCenter.m
should be re-writing to center = [[[JLToastCenter alloc] init] autorelease];
(for MRR mode), where the center
is declared as static id center = nil;
.
In this post, the answer given by @mipadi says that "If the variable is initialized only once, and should stay around for the lifetime of the application, then no, it shouldn't be released (its memory will essentially be freed when the application exits, anyway)". I guess this is the case when the static variable center
in JLToastCenter.m
but not sure about it.
My own version for MRR mode listed below added release/autorelease/dealloc things. I also changed all dot notation into messaging style.
Source code
Source code list:
JLToastCenter.h
JLToastCenter.m
JLToast.h
JLToast.m
JLToastView.h
JLToastView.m
The JLToastCenter.h
file:
#import <Foundation/Foundation.h>
@class JLToast;
@interface JLToastCenter : NSObject
{
NSOperationQueue *_queue;
}
+ (id)defaultCenter;
- (void)addToast:(JLToast *)toast;
@end
The JLToastCenter.m
file:
#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
The JLToast.h
file:
#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
The JLToast.m
file:
#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
The JLToastView.h
file:
#import <UIKit/UIKit.h>
@interface JLToastView : UIView
@property (nonatomic, strong) UIView *backgroundView;
@property (nonatomic, strong) UILabel *textLabel;
@property (nonatomic) UIEdgeInsets textInsets;
@end
The JLToastView.m
file:
#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