单例模式
介绍
单例模式可能是所有程序员接触的最早的设计模式,即程序中只存在一个实例。一般在静态语言中会使用static关键字来声明要使用的单例,js中一般使用原型链完成单例的效果。
iOS
+ (instancetype)manager {
static SpeBaseHTTPRequestManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] init];
});
return manager;
}
android
public abstract class SpeSqliteAbstractDBService extends SQLiteOpenHelper {
public static final String TAG = "SpeSqliteDBService";
public static SpeSqliteAbstractDBService instance = null;
public Context context = null;
public SQLiteDatabase db = null;
public Class roomClass;
/**
* 提供回调给调用者,通知其数据库状态
*/
public SpeSqliteBaseInterface listener;
/**
* 利用SQLiteOpenHelper的特性发起升级流程
* @param context context
*/
public SpeSqliteAbstractDBService(Context context) {
super(context, SpeSqliteUpdateManager.getInstance().init(context).currentAppDBSetting().dbName,
null, SpeSqliteUpdateManager.getInstance().init(context).currentAppDBSetting().dbVersion);
}
}
js
MQTTManager.prototype.initMQTTSDK = function(appID, admin,onReceivedMsgCallBack,onSDKStateChanedCallback) {
var deviceId = new DeviceUUID().get();
this.httpposter = new MQTTPoster(appID, deviceId, admin);
this.receivedMsgCallBack = onReceivedMsgCallBack;
this.sdkStateChanedCallback = onSDKStateChanedCallback;
this.getLoginParams();
return this
}
使用时机
存在作用域为全局的工具类或服务类(这个全局可以拆分成子组件中的全局)。
注意点
1、单例可以方便的解决全局数据共享问题或服务共享问题,但是因为是常驻内存中,所以单例的创建要有控制。
2、单例创建时需要注意初始化的时机,防止众多单例一起初始化导致服务或应用卡顿。
3、注意某些动态库和静态库混合编译时的多单例问题。
iOS下单例多次初始化问题
1.由于某app是模块化开发,主工程会依赖多个业务模块(动态库)和BaselibModule(动态库)。
2.各个业务模块也会依赖BaselibModule
3.当动态库依赖动态库时,上一级动态库同样需要确认位置,此时会按这个动态库的依赖顺序去查找谁可以给它确定这个位置。
4.静态库链接时,方法的调用位置也是按链接顺序确认,静态库能找到则取app内的地址偏移。
5.根据编译原理分析: 每个可执行模块(包括可执行文件和动态链接库)都有自己的数据段用来存放自己的全局变量,即每个可执行模块下的static全局变量都有自己的内存区域,所以导致单例初始化两次。蓝知app可执行模块包括主工程和各个子动态库。
6.优化方案:由于之前所有模块都会依赖BaselibModule,所以在主工程下使用单例时要加一层封装类,此封装类创建在BaselibModule中,这样即可保证单例对象来自同一可执行模块。
总结
1.单例使用时机还是比较好识别的,一般情况下会融合其它设计模式一起发挥作用,其实还是单一职责的一个延伸场景。