Reputation: 8609
I created an Objective-C Singleton class. I have a few properties on the class, including a BOOL
property. For some strange reason the BOOL
variable that I declared "resets" itself to YES outside of the scope that is set in.
In the Singleton class's header, the BOOL
is declared using @property
with the following parameters:
@property (nonatomic, assign, readonly) BOOL shouldCryptData;
In the Singleton class's @interface
in the implementation, I redefine the same property as readwrite
(because I need it to be read only to outside classes, but read / write to my own).
@interface SingletonClassName ()
@property (nonatomic, assign, readwrite) BOOL shouldCryptData;
@end
The BOOL
property gets set during initialization of the singleton class. I am setting it like this inside one of the init methods. There are multiple init methods which specify whether or not data should be crypted or not - this is just one example of where it would be set. Only one of my init methods call super, all the others make a call to the main init.
- (id)init {
self = [super init];
if (self) {
// Brief setup code
[self setShouldCryptData:NO]; // Have also tried using dot-notation and without *self*
// I can confirm that the shouldCryptData property is NO (within the current scope) right after setting it in this method
}
return self;
}
Now, the odd part is that when I try to access shouldCryptData
from any other method, it always returns YES. Why would it return YES after explicitly setting it to NO?
I'm not accessing it in any strange way, just like this:
if (self.shouldCryptData == YES) // Outside of the init method, this is ALWAYS true
I know I'm doing something wrong, but I can't figure it out. I feel like it has something to so with the singleton, but I'm not sure. It seems like neither Google, nor StackOverflow have any answers for this. Any ideas?
EDIT
Singleton implementation:
//-------- Header ---------------------//
@interface SingletonClassName : NSObject
+ (SingletonClassName *)sharedManager;
@end
//-------- Implementation ------------//
@implementation
+ (SingletonClassName *)sharedManager {
static SingletonClassName *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (sharedManager == nil) sharedManager = [[super allocWithZone:NULL] init];
});
return sharedManager;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedManager];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
Upvotes: 1
Views: 2670
Reputation: 8609
Figured out what was happening using a combination of suggestions from the answers and comments here. The best suggestion, by @Paul.s, was to place a breakpoint on the property declaration:
Are you sure that nothing else is setting it to YES? Place a breakpoint on the property declaration and it should break every time the property is read or written to
@Andy's comment was also helpful in figuring out what the issue was:
You could also try setting a watch point on the variable, which will pause anytime the variable is changed. You can do this firstly setting a breakpoint in the init method of the singleton, and then by typing watchpoint set variable shouldCryptData in the debugger once you reach the breakpoint.
I found that the init
method was getting called multiple times, and each time with a different setting for the BOOL property, shouldCryptData
.
Another suggestion from @noa helped clear up which instance the BOOL property was getting set on. I originally set it from the shared manager, but later set it in the init
method because the sharedManager
was on a different instance than the rest of the methods. Here's the relevant excerpt from the answer:
Try doing NSLog(@"%p", self) in all the methods where you set and access the property and make sure they're all the same.
So, it turns out that there were multiple issues:
init
)Upvotes: 1
Reputation: 17218
The simplest explanation is that you don't quite have a singleton, and you're setting that property on one instance, and reading it on another.
Try doing NSLog(@"%p", self)
in all the methods where you set and access the property and make sure they're all the same.
Added:
This is my usual singleton logic:
+ (instancetype) sharedInstance {
static MyClass *singleton;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[self alloc] init];
});
return singleton;
}
Invoke [MyClass sharedInstance]
when you need it.
Upvotes: 8
Reputation: 6952
KVO is one of ways. I will give an example.
TestObject.h
@interface TestObject : NSObject
@property (nonatomic, assign) BOOL isIt ;
@end
TestObject.m
@implementation TestObject
- (id)init
{
self = [super init] ;
if (self) {
[self addObserver:self forKeyPath:@"isIt" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil] ;
}
return self ;
}
- (void)dealloc
{
[self removeObserver:self forKeyPath:@"isIt"] ;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"isIt"]) {
NSLog(@"%@", change) ; // add a breakpoint here, you will know where change the value
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context] ;
}
}
@end
You will get notification only if the property is set by dot-notation or setIsIt method.
Upvotes: -1