Self destructing singleton design pattern iOS - objective-c

I recently came across an issue in which I only wanted one instance of a particular object to exist, and exist for only the brief period of time it needed to perform a specific operation. Its operation was asynchronous so ARC would dealloc it at the end of the run loop if I didn't hold a reference to it. If I did hang onto it I would need delegate callbacks or notifications to know when it was done to release it.
The object needed to download several images and other data and cache it to disk. I didn't want it to waste memory when it wasn't caching items since the cache limit was around 24 hours. I also didn't need feedback of any kind from it; I wanted it to perform it's task and be done with itself.
I came up with a design pattern I liked quite nicely. I've used it in a few other projects since then, and was curios if it was a well known and analyzed pattern that I'm just not aware of (self-destructing singleton???). I'd like to know so I can be made aware of any potential pitfalls I'm not currently seeing.
I'm also very interested to hear any input you guys might have about why this is a bad design.
The Design Goes Like This (this is ARC, but non-arc can work too if you release the singleton through a class method):
A global static object (not really a singleton because it doesn't live the entire time)
static MySelfDestructingClass* singleton;
A single public class method
+ (void)downloadAndCacheDataIfNeeded
{
//Force synchronized access
#synchronized(singleton){
//We are already doing something, return
if(singleton){
return;
}
NSDate* lastCacheDate = [[NSUserDefaults standardDefaults] objectForKey:kKeyForLastUpdate];
if([[NSDate date] timeIntervalSinceDate:lastCacheDate] > kCacheLimit){
//Our cache is out of date, we need to update
singleton = [[self alloc] init];
[singleton downloadAndCache];
}
}
}
Now our instance methods, we need our object alive so the request can come back:
- (void)downloadAndCache
{
//This would probably be a delegate, but for simplicity of this example it's a notification
[[NSNotificationCenter defaultCenter] addObserver:self forNotificationWithName:NotificationSomeRequestDidSucceed selector:#selector(someCustomRequestDidSucceed:withData:) object:nil];
[SomeCustomRequest makeRequestWithURL:#"http://www.someURL.com"];
}
- (void)someCustomRequestDidSucceed:(SomeCustomRequest *)request withData:(NSDictionary *)dictionary
{
//Do whatever we need to in order to save our data, or fire off image download requests etc...
....
//Set our lastUpdated time in NSUserDefaults
[[NSUserDefaults standardDefaults] setObject:[NSDate date] forKey:kKeyForLastUpdate];
//Remove our observer
[NSNotificationCenter defaultCenter] removeObserver:self name:NotificationSomeRequestDidSucceed object:nil];
//Release ourselves (ok not really, but tell arc we can be released)
singleton = nil;
}
This way all I have to do anywhere else in the application is:
[MySelfDestructingClass downloadAndCacheDataIfNeeded];
Now this object will download things if it needs to and release itself when it's done, or not create itself at all. It also won't go and start downloading the data twice.
I'm aware this design has limitations with extendibility and functionality, but for an instance like this, and the other ones I've used it for, I've found it quite useful.

This pretty common using blocks. Consider something similar (though I would probably handle multiple invocations differently...)
void ExecuteWithMySingleSelfDestructingObject(void(^block)(MySelfDestructingClass *object)) {
static MySelfDestructingClass* singleton;
#synchronized(singleton) {
if (singleton) {
// To get past the synchronization primitive, this must be a recursive call.
}
// Whatever other conditions you want to have (like your date check)
singleton = [[MySelfDestructingClass] alloc] init];
#try { block(singleton); }
#finally { singleton = nil; }
}
}
Note double exception handling (try/finally plus what #synchronized does - may want to change that...
Then do whatever you want with the block...
ExecuteWithMySingleSelfDestructingObject(^(MySelfDestructingClass *object){
// Do whatever I want with the singleton instance that has
// been given to me as <object>
});
Of course, it could be a class method...
+ (void)performBlock:(void(^)(MySelfDestructingClass *object))block {
static MySelfDestructingClass* singleton;
#synchronized(singleton) {
if (singleton) {
// To get past the synchronization primitive, this must be a recursive call.
}
// Whatever other conditions you want to have (like your date check)
singleton = [[self] alloc] init];
#try { block(singleton); }
#finally { singleton = nil; }
}
}
[MySelfDestructingClass performBlock:^(MySelfDestructingClass *object){
// Do whatever I want with the singleton instance that has
// been given to me as <object>
}];
I hope that makes sense (I typed it free-hand, so syntax may vary, but you should get the idea).

Related

Objective-C: Methods called by a swizzled method should call the original implementation

I'm fixing bugs in someone else's closed-source app.
In macOS, scrollbars can be set in System Preferences to display "always" (NSScrollerStyleLegacy), "when scrolling" (NSScrollerStyleOverlay), or "automatically based on mouse or trackpad" (NSScrollerStyleOverlay if a trackpad is connected, otherwise NSScrollerStyleLegacy). To check which style is in use, apps are supposed to do something like:
if ([NSScroller preferredScrollerStyle] == NSScrollerStyleLegacy)
addPaddingForLegacyScrollbars();
Unfortunately, for some reason, this app is reading the value from NSUserDefaults instead (confirmed using a decompiler).
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([[defaults objectForKey:#"AppleShowScrollBars"] isEqual: #"Always"])
addPaddingForLegacyScrollbars();
This code incorrectly assumes any value of AppleShowScrollBars other than "Always" is equivalent to NSScrollerStyleOverlay. This will be wrong if the default is set to "Automatic" and no Trackpad is connected.
To fix this, I used the ZKSwizzle library to swizzle the NSUserDefaults objectForKey: method:
- (id)objectForKey:(NSString *)defaultName {
if ([defaultName isEqual: #"AppleShowScrollBars"]) {
if ([NSScroller preferredScrollerStyle] == NSScrollerStyleLegacy) {
return #"Always";
} else {
return #"WhenScrolling";
}
}
return ZKOrig(id, defaultName);
}
Unfortunately, this led to a stack overflow, because [NSScroller preferredScrollerStyle] will itself initially call [NSUserDefaults objectForKey:#"AppleShowScrollBars"] to check the user's preference. After some searching, I came across this answer on how to obtain the class name of a caller, and wrote:
- (id)objectForKey:(NSString *)defaultName {
if ([defaultName isEqual: #"AppleShowScrollBars"]) {
NSString *caller = [[[NSThread callStackSymbols] objectAtIndex:1] substringWithRange:NSMakeRange(4, 6)];
if (![caller isEqualToString:#"AppKit"]) {
if ([NSScroller preferredScrollerStyle] == NSScrollerStyleLegacy) {
return #"Always";
} else {
return #"WhenScrolling";
}
}
}
return ZKOrig(id, defaultName);
}
This works perfectly! However, obtaining the caller uses the backtrace_symbols API intended for debugging, and comments on the aforementioned answer suggest this is a very bad idea. And, in general, returning different values depending on the caller feels yucky.
Obviously, if this was my own code, I would rewrite it to use preferredScrollerStyle instead of NSUserDefaults in the first place, but it's not, so I can only make changes at method boundaries.
What I fundamentally want is for this method to be swizzled only when it's called above me in the stack. Any calls further down the stack should use the original implementation.
Is there a way to do this, or is my current solution reasonable?
This approach is probably ok (within the context of "I've already decided to swizzle"), but it does feel a bit fragile as you note, and callStackSymbols can be very slow, and what information is available depends on whether debug symbols are available (which probably won't ever break this particular use case, but if it does, the bug will be very confusing).
I think you can make this more robust and much faster by short-circuiting recursion with a static variable.
- (id)objectForKey:(NSString *)defaultName {
static BOOL isRunning = false;
if (!isRunning && [defaultName isEqual: #"AppleShowScrollBars"]) {
isRunning = true;
NSScrollerStyle scrollerStyle = [NSScroller preferredScrollerStyle];
isRunning = false;
if (scrollerStyle == NSScrollerStyleLegacy) {
return #"Always";
} else {
return #"WhenScrolling";
}
}
return ZKOrig(id, defaultName);
}
static variables within a function retain their value between calls, so you can use this to detect that recursion is happening. (This is not thread-safe, but that shouldn't be a problem in this use case. Also note that all instances of this class share the same static variable. That shouldn't matter here since you're swizzling a specific object.)
If this function is reentered, then it'll just skip down to the original implementation.
Rob's answer is good, but if I've understood your requirements correctly, there may be an alternative solution that may simplify things a bit. You can avoid re-entrancy (and avoid having NSUserDefaults be swizzled in all contexts) by swizzling the app method, using that as an entry point to know when the app is about to read from NSUserDefaults, and temporarily swizzle NSUserDefaults for the duration of that call:
// This can be an ivar, static variable, etc.
// You can initialize this with `dispatch_once` if the method only reads the scroller
// style only once (e.g. on initialization), or leave this mutable if you want to check
// every time the app method is called.
static NSScrollerStyle effectiveScrollerStyle = NSScrollerStyleLegacy;
// Replace this dummy method with whatever the actual interface is for the app method
// in question.
- (void)whateverTheInterfaceIsForTheAppMethod:(id)whatever {
// Call this _prior_ to swizzling `NSUserDefaults`.
effectiveScrollerStyle = [NSScrollerStyle preferredScrollerStyle];
/* swizzle NSUserDefalts with the implementation below */
ZKOrid(void, whatever);
/* restore NSUserDefaults */
}
// --------------------------------------------------- //
- (id)objectForKey:(NSString *)defaultName {
if ([defaultName isEqual:#"AppleShowScrollBars"]) {
if (effectiveScrollerStyle == NSScrollerStyleLegacy) {
return #"Always";
} else {
return #"WhenScrolling";
}
}
return ZKOrig(id, defaultName);
}
I'm not familiar with ZKSwizzle so I'm not sure of the exact syntax you use to swizzle NSUserDefaults, but hopefully the concept is clear, and does what you want.

awakeFromInsert called twice with nested contexts

This project uses Mogenerator and Magical Record. I have tracked down a bug to the fact that awakeFromInsert is getting called twice. Once for each of my contexts I presume. This is an issue because I need to listen for NSNotifications on this NSManagedObject like this:
- (void)awakeFromInsert
{
// Listen for a return from background mode
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enteringForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
But awakeFromInsert get's called twice which is rather annoying. I want to call a method ONCE when my NSManagedObject is first created.
After searching this solution seems to make a lot of sense. However I can't see how I can add a category onto NSManagedObject when using Mogenerator and MagicalRecord. Without some complex overriding.
In MagicalRecord MR_createEntity calls
if ([self respondsToSelector:#selector(insertInManagedObjectContext:)])
{
id entity = [self performSelector:#selector(insertInManagedObjectContext:) withObject:context];
return entity;
}
Is there a neater solution to this issue?
Ok well this feels very hacky but appears to work. I created the following class methods on my human readable NSManagedObject class:
+ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ {
JWBoard *newobject = [super insertInManagedObjectContext:moc_];
[JWBoard awakeFromCreate:newobject];
return newobject;
}
+ (void)awakeFromCreate:(JWBoard *)board
{
// do setup stuff & add observers
}
Open to much better solutions!
Open to much better solutions!
I wish! Would have been easy enough for apple to not invoke awakeFromInsert, or to at least provide a flag that's true in the context of a "parentProcessSaveRequest." If you look at the call stack for the not-first calls to awakeFromInsert, the stack always contains parentProcessSaveRequest.
Here's some terrible code proving as much:
- (void) awakeFromInsert
{
[super awakeFromInsert];
NSArray* stackArray = [NSThread callStackSymbols];
for (NSString* method in stackArray)
{
if ([method rangeOfString:#"_parentProcessSaveRequest"].location != NSNotFound)
{
NSLog(#"Parent insert %#",self.objectID);
return;
}
}
NSLog(#"First insert %#",self.objectID);
// Initialize here
}
And the log output -- the objectId stays the same:
2014-05-19 20:53:52.964 myApp[1891:a01f] First insert 0x6000000326c0 <x-coredata:///MyEntity/t496E9B17-E170-4A7C-B7D4-7D8B92433E1C2>
2014-05-19 20:53:53.531 myApp[1891:303] Parent insert 0xdca8000eb <x-coredata://7274869F-4BF3-4B8A-9270-A64E54476AAD/MyEntity/p14122>
2014-05-19 20:53:53.537 myApp[1891:303] Parent insert 0xdca8000eb <x-coredata://7274869F-4BF3-4B8A-9270-A64E54476AAD/MyEntity/p14122>
Seems to work for saving to any of the nested contexts that I have, ugly as it is.
Unfortunately I cant figure any reasonable way to determine whether awakeFromInsert is being called in the context of a parentProcessSaveRequest. Come on, Apple! Give us a flag here.
here is the simplest one:
when parentContext is null, means when this context is saved you can do you custom logic, for example incrementing table number
- (void)awakeFromInsert
{
if (!self.managedObjectContext.parentContext) {
//setting tableNumber
[self willChangeValueForKey:#"number"];
[self setPrimitiveNumber:tableNumber];
[self didChangeValueForKey:#"number"];
}
}

Granularity status of an NSBlockOperation

I have extended NSOperationQueue to allow adding NSBlockOperation with a specific NSString as identifier.
The identifier value is held in a NSMutableArray serving as a registry. This is how I implement the registry.
-(void)addOperation:(NSOperation *)operation withID:(NSString*)operationID
{
#synchronized(self.queueReference)
{
[self.queueReference addObject:operationID]; // <-- just a mutable array
}
[operation setCompletionBlock:^(){
#synchronized(self.queueReference) {
[self.queueReference removeObject:operationID];
}
}];
[self addOperation:operation];
}
Basically I am adding a completion block which is cleaning the registry when that particular operation has finished.
However, while this works, I am in need to add more granularity to the queue.
I only use the queue with block operation, and during the execution of the block I may send different NSNotification to the listener depending how the execution went.
What I was trying to achieve:
A caller try to add a particular NSBlockOperation with identifier to queue. If queue already has such identifier just don't add block, and the calling class set itself as listener.
What is missing ? Checking for the identifier is not enough, there may be case when the NSBlockOperation already dispatched the NSNotification but the completion block has not yet being called.
So the caller class ask the queue, which is saying the identifier exists in registry, and caller wrongly set itself for listening to a notification that will never arrive because it's already being sent.
The scenario would be instead: caller ask the queue, which is saying 'identifier is in registry' but NSNotification is sent. And the caller put NSBlockOperation to queue.
The check of registry is made by means of a simple method:
-(BOOL)hasOperationWithID:(NSString*)operationID
{
#synchronized(self.queueReference)
{
return [self.queueReference containsObject:operationID];
}
}
but at this point I have not much idea on how to extend such method. The code I am working on is kind of 'academic', it does not serve any particular purpose, it is just me trying to experiment. Therefore I have great flexibility within the code. But this is quite new subject to me, so please be as much specific as possible of any downside of suggested implementation.
It looks like your current system has three fundamental events:
Operation is added to the queue
Operation sends notification while executing
Operation completion block is called
Unless the queue itself explicitly listens for any NSNotifications that might be sent by the blocks, it has no way of knowing whether they have happened yet. But even if it does listen, the ordering in which observers of NSNotifications are called is non-deterministic. In other words, even if the queue listens for the notification and interlocks its callback with enqueue/dequeue operations, it could (and eventually would) still be too late for another client to start listening for that NSNotification, and you would falsely reject an operation.
Consider this alternative: Instead of using the completion block to manage the identifier list, use the notification itself -- have the queue handle sending the notifications. Put differently, let's get rid of the third event and have the notification sending do double duty for identifier list maintenance. The simplest way I came up with to do this looked like:
Header:
//
// SONotifyingOperationQueue.h
// NotifyingOpQueue
//
typedef void (^SOSendNotificationBlock)(NSDictionary* userInfo);
typedef void (^SONotifyingBlock)(SOSendNotificationBlock sendNotificationBlock);
#interface SONotifyingOperationQueue : NSOperationQueue
- (BOOL)addOperationForBlock:(SONotifyingBlock)block withNotificationName:(NSString*)notificationName;
#end
Implementation
//
// SONotifyingOperationQueue.m
// NotifyingOpQueue
//
#import "SONotifyingOperationQueue.h"
#implementation SONotifyingOperationQueue
{
NSMutableSet* _names;
}
- (BOOL)addOperationForBlock: (SONotifyingBlock)block withNotificationName: (NSString*)notificationName
{
notificationName = [[notificationName copy] autorelease];
BOOL shouldAdd = NO;
#synchronized(self)
{
_names = _names ? : [[NSMutableSet alloc] init];
if (![_names containsObject: notificationName])
{
[_names addObject: notificationName];
shouldAdd = YES;
}
}
if (shouldAdd)
{
NSBlockOperation* blockOp = [[[NSBlockOperation alloc] init] autorelease];
__block SONotifyingOperationQueue* blockSelf = self;
SOSendNotificationBlock notificationBlock = ^(NSDictionary* userInfo){
#synchronized(blockSelf)
{
[blockSelf->_names removeObject: notificationName];
// Sending the notification from inside the #synchronized makes it atomic
// with respect to enqueue operations, meaning there can never be a missed
// notification that could have been received.
[[NSNotificationCenter defaultCenter] postNotificationName: notificationName object: blockSelf userInfo: userInfo];
}
};
dispatch_block_t executionBlock = ^{
block(notificationBlock);
};
[blockOp addExecutionBlock: executionBlock];
[self addOperation: blockOp];
}
return shouldAdd;
}
- (void)dealloc
{
[_names release];
[super dealloc];
}
#end
This approach makes several changes to your original approach. First, the API here adds blocks and not NSOperations. You could do the same thing with an NSOperation subclass, but it would be more code, and wouldn't change the overall pattern. It also merges the notion of the identifier and the notification name. If an operation could send multiple, different NSNotifications, this won't work without modification, but again, the overall pattern would be the same. The important feature of this pattern is that your id/name check is now interlocked with the notification sending itself, providing a strong guarantee that if someone goes to add a new block/operation to the queue, and another operation with the same id/name hasn't fired its notification yet, the new operation won't be added, but if the notification has been fired, then it will be added, even if the preceding block hasn't yet completed.
If having the NSOperation object was somehow important here, you could also have the method here return the operation it creates for the supplied block.
HTH.

blocks and async callback, dealloc object - need to nil the block

There is a similar question here, which doesn't explain exactly what I want: Objective C Blocks as Async-callbacks & BAD ACCESS
I have a view controller, which calls a service with an async callback. The callback is done using a block, which references variables on the view controller to populate them.
It looks like so:
- (void) loadData {
__block MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
me.data = result;
}
}];
}
However, if I dealloc the view controller, 'me' is then badly accessed by the callback.
What is the simplest way of making 'me' NULL? If i put it as an iVar, it then brings back the circular reference... i think?
I think I'm missing something obvious....
Thanks
Are you targeting iOS 5.0 or later (or Mac OS X 10.7 or later)? If so, you can use ARC and a __weak variable (instead of a __block one). This will automatically zero out when the referenced object is deallocated. Your code would look like
- (void)loadData {
__weak MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
MyViewController *strongMe = me; // load __weak var into strong
if (strongMe) {
strongMe.data = result;
}
}
}];
}
If you need support for an older OS then you need to find a different solution. One solution is to just go ahead and let the block retain self. If the service is guaranteed to execute the completion block (and then release it), this will only produce a temporary cycle that will break automatically when the completion block is run. Alternatively if you have some way to cancel the service (in a way that guarantees the block cannot be called after the cancellation), you can stick with the __block and just be sure to cancel the service in your -dealloc. There's other alternatives too but they're more complicated.
I did a combination of things above from the suggestions. Including nilling the blocks. Although, my objects are still not getting released immediately. i.e. I'd put a breakpoint on dealloc of MyViewController, and without the __block variable it would get called at a much later point in time (probably due to the async connection) and sometimes not at all.
The code is fairly complex - so I imagine there are other things going on for it to not work as suggested above.
What I have also done, is used Mike Ash's MAZeroingWeakRef, which i guess is the same as using __weak - which #KevinBallard suggested.
Below is how I've implemented it, and it appears to be working. Dealloc is called immediately on disposal of the view controller, which i want. And I can't get it to crash... and with the log comment that i've put in, I can already see that I'm dodging bullets.
- (void) loadData {
__block MAZeroingWeakRef *zeroWeakRef = [[MAZeroingWeakRef alloc] initWithTarget:self];
[zeroWeakRef setCleanupBlock: ^(id target) {
[zeroWeakRef autorelease];
}];
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
MyViewController *me = [zeroWeakRef target];
if (!me) {
DULog(#"dodged a bullet");
}
if (!error) {
me.data = result;
}
}];
}
Is there a real retain cycle problem that you're trying to avoid? Is there a reason that self should not simply be retained until -executeWithCompletion: completes? Is there any real chance that it won't complete?
So long as it really will eventually complete (even with failure) and so long as it releases the block after invoking it (perhaps by setting a property to nil), then the retain cycle will eventually be broken and all will be well.

Reusing NSObjects by Overriding release in Obj-C

I am implementing an object reuse scheme using a singleton class.
What I do basically is:
MyClass* obj = [[MyClassBank sharedBank] getReusableItem];
The bank is just an NSMutableSet tweaked for optimum reusability. When I was happily implementing this Singleton, I had in mind that I will just do the following when I am done with "obj":
[[MyClassBank sharedBank] doneWithItem:obj];
Currently, My code would work if I where to use it this way, but I later realized that I sometimes add "obj" to an "NSCollection", and sometimes I call:
[theCollection removeAllObjects];
At first I thought about making my own class that is composed of a collection, then I would iterate the objects within the collection and call:
[[MyClassBank sharedBank] doneWithItem:obj];
But, that's too much of a hassle, isn't?
A neat idea (I think) popped into my mind, which is to override: -(oneway void)release;, so, I immediately jumped to Apple's documentation, but got stuck with the following:
You would only implement this method to define your own reference-counting scheme. Such implementations should not invoke the inherited method; that is, they should not include a release message to super.
Ao, I was reluctant to do that idea .. basically:
-(oneway void)release{
if ([self retainCount] == 1) {
//This will increment retain count by adding self to the collection.
[[MyClassBank sharedBank] doneWithItem:self];
}
[super release];
}
Is it safe to do that?
PS: Sorry for the long post, I want the whole idea to be clear..
EDIT:
How about overriding alloc alltogther and adding [[MyClassBank sharedBank] getReusableItem]; there?
Suggested method:
You're playing with the reference counting system. 99.9999999999999999% of the time this is a bad idea. I would highly recommend going with a different mechanism. Perhaps these objects could implement their own reference count that's independent of the retainCount? Then you could use that referenceCount to actually control when an object is ready to be re-used or not.
Not suggested method:
If, for some weird reason, you can't do that, then you could do the following thing that is still a bad idea and that i don't recommend you actually use:
You can override dealloc:
- (void)dealloc {
[ivar release], ivar = nil;
[anotherIvar release], anotherIvar = nil;
somePrimitive = 0;
// do not call [super dealloc]
}
- (void)_reallyDealloc {
[self dealloc]; // clean up any ivars declared at this level
[super dealloc]; // then continue on up the chain
}
Basically, the dealloc method would be the point at which the object is ready for re-use. When you're totally done with the object and finally want it to go away, you can use the _reallyDealloc method to continue on up the chain, eventually resulting in the object getting freed.
PLEASE don't do this. With things like Automatic Reference Counting, this is going to introduce you into a world of hurt and really bizarre debugging scenarios. A lot of the tools and classes and stuff depend on the reference counting mechanism to be working without alteration, so screwing around with it is usually not a Good Idea™.
For ppl who find this approach interesting/useful, Here is a cleaner way than calling [super dealloc]; directly (which is definitely bad)
//BAD!
//-(void)dealloc{
// for some reason, the retainCount at this point == 1
// if (![[BankStep sharedBank] purgeFlag]) {
// [self resetObject];
// [[BankStep sharedBank] doneWithItem:self];
// } else {
// [children release];
// [super dealloc];
// }
//}
by calling [[Bank sharedBank] purgeBank]; , set the flag to true, then remove all objects from the NSSet.
Adapted solution:
#Joe Osborn idea of using categories to implement a returnToBank Method!