最近写接口。因为比较懒。我就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:。
证实下:跟进去看看
继续跟进去
B34DB59E-B685-48A0-BADD-428F59AEA24D.png下面果然调了allocWithZone:。
好了,我们看看allocWithZone:里面怎么搞的:
BE2BF48B-563D-4B45-A76A-B02F0B321D68.pngallocWithZone:里面调的是class_createInstance.此方法是MRC方法,看光官方解释:
2CA5FDBD-E96F-49C9-B31D-80826A23259A.pngARC不可用。成功就返回,失败就到badAllocHander里面。
好了,一个方法打印一下,他们创建的对象
02354372-4373-483E-8B56-C7880A15C722.png。果然用class_createInstance创建出新对象了。
网友评论