iOS中的单例模式

来源:互联网 发布:数值的整数次方 java 编辑:程序博客网 时间:2024/06/10 00:00

单例模式是指一个类中只能有一个实例的时候,下面是我学习单例中遇到的情况。

单例模式有两种创建方式,一种是将创建代码包裹在同步块里,另一种是使用GCD的一项特性。

我们先看第一种:

+(id)sharedInstance{    static MyClass *className = nil;        // 同步,线程安全    @synchronized(self)    {        if (!className)        {            className = [[self alloc] init];        }    }        return className;}
第二种方法是苹果官方推荐的方法,使用GCD的dispatch_once(dispatch_once_t *token, dispatch_block_t block)方法,该方法中有两个参数,第一个参数类型为dispatch_once_t的特殊参数,可以将其称为“标记”(token),该参数的作用是保证相关的块必须执行,而且只能执行一次,既区分作用。因为只能执行一次,所以每次调用函数时传入的“标记”都必须完全相同,所以通常将dispatch_once_t的参数声明在static或global作用于里。
+(id)sharedInstance{    static MyClass *className = nil;    static dispatch_once_t onceToken;        dispatch_once(&onceToken, ^{        className = [[self alloc] init];    });        return className;}
使用dispatch_once可以简化代码并且彻底保证线程安全。下面是我要测试的demo

我要执行的任务是,点击按钮,弹出一个登录框,登录框有用户名的输入框和一个确定按钮,点击确定按钮,登陆界面消失。

这是主界面代码

#import "ViewController.h"#import "LoginDetailsView.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad{    [super viewDidLoad];        [self createButton];    // Do any additional setup after loading the view, typically from a nib.}#pragma mark - 设置跳转按钮-(void)createButton{    UIButton *button = [UIButton buttonWithType:0];    button.frame = CGRectMake(100, 350, 120, 50);    button.backgroundColor = [UIColor redColor];    [button setTitle:@"跳转登陆界面" forState:UIControlStateNormal];    [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];    [button addTarget:self action:@selector(jumpAnotherView) forControlEvents:UIControlEventTouchUpInside];    [self.view addSubview:button];}#pragma mark - 跳转下一个页面-(void)jumpAnotherView{    LoginDetailsView __block *loginView = [LoginDetailsView sharedInstance:CGRectMake(20, 30, self.view.frame.size.width - 40, 300)];        //LoginDetailsView *loginView = [[LoginDetailsView alloc] initWithFrame:CGRectMake(20, 30, self.view.frame.size.width - 40, 300)];        [loginView setSureButtonClick:^(NSString *title, NSInteger count) {        NSLog(@"title = %@, count = %ld", title, (long)count);    }];    [self.view addSubview:loginView];}
这是登陆界面代码
#import <UIKit/UIKit.h>typedef void (^sureButtonClickBlock) (NSString *title, NSInteger count);@interface LoginDetailsView : UIView@property (nonatomic, strong) sureButtonClickBlock sureButtonClick;// 采用单例模式创建界面+(id)sharedInstance:(CGRect)frame;// 销毁单例//-(void) destroySingleTon;-(void)setSureButtonClick:(sureButtonClickBlock)sureButtonClick;@end
#import "LoginDetailsView.h"#import "GBDIYButton.h"#import "GBDIYTextfield.h"@interface LoginDetailsView (){    GBDIYButton *sureButton; // 确定按钮    GBDIYTextfield *userNameField; // 用户名输入框}@end@implementation LoginDetailsView-(id)initWithFrame:(CGRect)frame{    if (self == [super initWithFrame:frame])    {        self.backgroundColor = [UIColor yellowColor];        // 初始化界面        [self createLoginView];    }    return self;}static LoginDetailsView *loginDetailsView;+(id)sharedInstance:(CGRect)frame{    static LoginDetailsView *loginDetailsView = nil;        // 同步,线程安全    @synchronized(self)    {        if (!loginDetailsView)        {            loginDetailsView = [[self alloc] initWithFrame:frame];        }    }        return loginDetailsView;}//#pragma mark - 使用dispatch_once创建单例//+(id)sharedInstance:(CGRect)frame//{//    static LoginDetailsView *loginDetailsView = nil;//    static dispatch_once_t onceToken;//    //    dispatch_once(&onceToken, ^{//        loginDetailsView = [[self alloc] initWithFrame:frame];//    });//    //    return loginDetailsView;//}#pragma mark - 初始化界面-(void)createLoginView{    userNameField = [[GBDIYTextfield alloc] initWithFrame:CGRectMake(30, 50, self.frame.size.width - 60, 40) withPlaceHolder:@"请输入用户名"];    [self addSubview:userNameField];        sureButton = [[GBDIYButton alloc] initWithFrame:CGRectMake(self.frame.size.width / 2.0 - 30, self.frame.size.height - 100, 60, 40) withTitle:@"确定"];    [sureButton addTarget:self action:@selector(clickTheButton) forControlEvents:UIControlEventTouchUpInside];    [self addSubview:sureButton];}-(void)setSureButtonClick:(sureButtonClickBlock)sureButtonClick{    if (sureButtonClick)    {        _sureButtonClick = sureButtonClick;    }}#pragma mark - 点击按钮操作-(void)clickTheButton{    /*     * 再此处写请求网络之类的代码,请求成功,本登陆界面消失     */        [self removeFromSuperview];        if (self.sureButtonClick)    {        NSNumber *number = [NSNumber numberWithInt:2];        NSInteger count = [number integerValue];        self.sureButtonClick(@"点击确定按钮", count);    }}//-(void) destroySingleTon//{//    loginDetailsView = nil;//}
<span style="color:#ff0000;">自定义输入框代码</span>
<pre name="code" class="objc">#import <UIKit/UIKit.h>@interface GBDIYTextfield : UITextField// 自定义初始化方法-(id)initWithFrame:(CGRect)frame withPlaceHolder:(NSString *)placeHolder;@end
#import "GBDIYTextfield.h"@implementation GBDIYTextfield-(id)initWithFrame:(CGRect)frame withPlaceHolder:(NSString *)placeHolder{    if (self == [super initWithFrame:frame])    {        self.placeholder = placeHolder;        self.layer.borderColor = [UIColor lightGrayColor].CGColor;        self.layer.borderWidth = 1;        self.layer.cornerRadius = 15;    }        return self;}@end
<span style="color:#ff0000;background-color: rgb(255, 255, 255);">自定义按钮代码</span>
<pre name="code" class="objc">#import <UIKit/UIKit.h>@interface GBDIYButton : UIButton// 自定义初始化方法-(id)initWithFrame:(CGRect)frame withTitle:(NSString *)title;@end

#import "GBDIYButton.h"@implementation GBDIYButton-(id)initWithFrame:(CGRect)frame withTitle:(NSString *)title{    if (self == [super init])    {        self.frame = frame;        [self setTitle:title forState:UIControlStateNormal];        [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];        self.backgroundColor = [UIColor greenColor];        self.layer.cornerRadius = 10;    }        return self;}@end

通过打断点可知,用传统的方法,alloc init ,在主界面点击“跳转登陆界面”点击一次,生成一次登陆界面,所以我们现在用单例创建登陆界面。其实正常的话,此登陆界面不能用单例来实现,因为取消单例是个非常难得事情。我们稍后介绍,现在我们先介绍单例的用法。

代码中用了两中方式创建单例,并用block返回了按钮点击的事件。

可是现在如果你在输入框中输入了内容,点击“确定”按钮,登录界面消失,我们再次点击“跳转登陆界面”按钮,发现我们上次在输入框中填写的内容还在。这就是为什么这个登录界面不应该用单例来写的原因,因为单例自己管理自己的生命周期,所以用摧毁单例的方法很难。请参考具体文章点击打开链接。链接的文章写得很好,慎用单例。

要想摧毁单例,在主界面中将其设置为nil也不行,点击按钮还是会出现。为此我在登陆界面中写了个摧毁单例的方法,-(void)destroySingleTon;但是还是不行。因为要想摧毁这个单例,不能使用@synchronized或者dispatch_once。可是这样,和普通的声明初始化方法没什么两样,也就不是单例了。所以还是那句话,在你想使用单例前,仔细考虑一下,是否应该使用单例。因为单例一旦创建,其生命周期就归单例自己掌控了。还是请参考链接文章。


0 0
原创粉丝点击