最近寫接口。因為比較懶。我就MJExtension直接初始化單例。結果發現里面的值是nil.
這個單利是直接copy他們代碼過來的。懶得搞了。催的又比較急。
代碼是這樣的:
@implementation UserCenter
+ (instancetype) getInstance
{
static dispatch_once_t once ;
static id instance = nil;
dispatch_once(&once, ^{
instance = [[[self class] alloc]init];
});
return instance;
}
他的兩個屬性也是單利。也是這樣創建單利的。
結果發現里面的基本屬性全是nil.
我的當時懵逼了。MJExtension,用了這么久難道出毛病了不成。
[self.action postStoreInformationFirstStepWithParameter:dict finish:^(id _Nullable responseObject, NSString * _Nullable error) {
[weakSelf hideLoadingViewInSelf];
if (error) {
[weakSelf reminderUserInfo:error];
}else
{
[UserCenter mj_objectWithKeyValues:responseObject];
[[UserCenter getInstance] saveData];
MakeStoreInformationSecondStepVC *vc = [[MakeStoreInformationSecondStepVC alloc] init];
[weakSelf.navigationController pushViewController:vc animated:YES];
}
}];
網絡請求回來的數據responseObject是有數據的。但是我再次調用[UserCenter getInstance]里面的 屬性發現還是nil.
查看MJExtension的mj_objectWithKeyValues面的代碼:
+ (instancetype)mj_objectWithKeyValues:(id)keyValues
{
return [self mj_objectWithKeyValues:keyValues context:nil];
}
+ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context
{
// 獲得JSON對象
keyValues = [keyValues mj_JSONObject];
MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], nil, [self class], @"keyValues參數不是一個字典");
if ([self isSubclassOfClass:[NSManagedObject class]] && context) {
return [[NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(self) inManagedObjectContext:context] mj_setKeyValues:keyValues context:context];
}
return [[[self alloc] init] mj_setKeyValues:keyValues];
}
最后發現創建對象明顯是return [[[self alloc] init] mj_setKeyValues:keyValues];。看了一下沒毛病啊。
于是我在多次在控制臺打印 [[UserCenter alloc] init];
最后發現
很明顯多次內存地址都不一樣,明顯不是同一個對象。
也就是這不是一個“純”單利。于是乎我改了下
static UserCenter *userZone = nil;
@implementation UserCenter
+ (instancetype) getInstance
{
static dispatch_once_t once ;
static id instance = nil;
dispatch_once(&once, ^{
instance = [[self alloc]init];
});
return instance;
}
+(id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
userZone = [super allocWithZone:zone];
});
return userZone;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return userZone;
}
-(id)copyWithZone:(NSZone *)zone
{
return userZone;
}
這樣即使你調了copy ,mutableCopy他還是一個單利。
因為alloc 方式內部是調allocWithZone的。上面可以直接改為
+ (instancetype) getInstance
{
static dispatch_once_t once ;
static id instance = nil;
dispatch_once(&once, ^{
instance = [[self allocWithZone:NULL]init];
});
return instance;
}
或者
+ (instancetype) getInstance
{
return [[self alloc] init];
}
+(id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
userZone = [super allocWithZone:zone];
});
return userZone;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return userZone;
}
-(id)copyWithZone:(NSZone *)zone
{
return userZone;
}
原來的也是的對的,也允許,別人去創建一個新的這樣的類的對象做其他用途吧。但是要創建單例的時候一定要這個方法去返回。
對了,上面說alloc 回調allocWithZone:。
證實下:跟進去看看
繼續跟進去
下面果然調了allocWithZone:。
好了,我們看看allocWithZone:里面怎么搞的:
allocWithZone:里面調的是class_createInstance.此方法是MRC方法,看光官方解釋:
ARC不可用。成功就返回,失敗就到badAllocHander里面。
好了,一個方法打印一下,他們創建的對象
。果然用class_createInstance創建出新對象了。