iOS 单例模式,你真的写对了吗?

几年前,笔者曾对 iOS 单例模式作过一番阐述,包括其优点以及 Apple 自身对单例模式的实现示例,详情可参考:iOS 单例模式详解。这里再做一个简短的总结,

单例的用处:主要用在封装网络请求,播放器,存放常用数据等。
单例的特点:只初始化一次,生命周期和程序的生命周期相同,访问方便。

下面一步一步以循序渐进的方式来将单例模式完善化。为了多线程安全,用 GCD 创建单例更加方便,由于 dispatch_once 只执行一次,在外面调用 sharedInstance 这个方法,返回的对象始终是一个。

static InstanceManager *_instance = nil;

@implementation InstanceManager

+ (instancetype)sharedInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}
@end

在创建对象时,不小心调用了 alloc 或者 new,那这就不是单例了。我们知道,在对象创建的时候,allocnew 都会调用到 allocWithZone 方法,此时只需要重写 allocWithZone 方法即可。

+ (id)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}

此时,如果不慎调用了 copymutableCopy 方法就会导致程序运行崩溃,自定义的类,要实现 copy 就要默认实现 NSCopying 协议,同理实现 mutablecopy 就要实现 NSMutableCopying 协议。NSCopying 协议中有一个唯一的方法- (id)copyWithZone:(nullable NSZone *)zone; 需要实现,同样的,NSMutableCopying也有唯一一个方法- (id)mutableCopyWithZone:(nullable NSZone *)zone;需要实现。

- (nonnull id)copyWithZone:(nullable NSZone *)zone {
    return _instance;
}

- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
    return _instance;
}

最终的 InstanceManager.m 完整代码如下:

#import "InstanceManager.h"

@interface InstanceManager() <NSCopying,NSMutableCopying>

@end

static InstanceManager *_instance = nil;

@implementation InstanceManager
    
+ (instancetype)sharedInstance {
            return [[self alloc] init];
        }
        
+ (id)allocWithZone:(struct _NSZone *)zone {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
        if (!_instance) {
            _instance = [super allocWithZone:zone];
            }
        });
        return _instance;
    }
    
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
            return _instance;
        }
        
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
            return _instance;
        }
        
@end

到此,一个完整的 iOS 单例就创建完成了!

上文中出现了 copy 与 mutableCopy 属性,也就自然涉及到了 Objective-C 中浅拷贝与深拷贝的知识,由于本文的核心内容是单例模式,这里只对 copy 与 mutableCopy 相关要点作简要描述。

NSString *string = @"huangfei";
NSString *stringCopy = [string copy];
NSMutableString * stringMCopy = [string mutableCopy];
[stringMCopy appendString:@"Hero"];

运行上述代码查看内存地址可知,string 和 stringCopy 指向的是同一块内存区域,而系统则为 stringMCopy 分配了一个新的内存地址。由此可见,copy 是指针复制(浅拷贝),mutableCopy 是对象复制(深拷贝)。

值得注意的是,在 iOS 中并不是所有的对象都支持copymutableCopy,遵守NSCopying协议的类可以发送copy消息,遵守NSMutableCopying协议的类才可以发送mutableCopy消息。否则,会发生异常。

发布了225 篇原创文章 · 获赞 648 · 访问量 89万+

猜你喜欢

转载自blog.csdn.net/huangfei711/article/details/89640258