I want to add a KVO observation that removes itself after it fires once. I have seen lots of folks on StackOverflow doing stuff like this:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"myKeyPath"])
{
NSLog(#"Do stuff...");
[object removeObserver:self forKeyPath:#"isFinished"];
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
This seems plausible, but I'm aware that calling -removeObserver:forKeyPath: from within -observeValueForKeyPath:... can be lead to non-deterministic crashes that are hard to debug. I also want to be sure this observation only gets called once (or not at all if the notification is never sent). What's a good way to do this?
I'm answering my own question here because I've seen the pattern in the question all over the place, but haven't had a reference to a good example of a better way. I've lost days, if not weeks, of my life to debugging problems ultimately found to be caused by adding and removing observers during the delivery of KVO notifications. Without warranty, I present the following implementation of a one-shot KVO notification that should avoid the problems that come from calling -addObserver:... and -removeObserver:... from inside -observeValueForKeyPath:.... The code:
NSObject+KVOOneShot.h:
typedef void (^KVOOneShotObserverBlock)(NSString* keyPath, id object, NSDictionary* change, void* context);
#interface NSObject (KVOOneShot)
- (void)addKVOOneShotObserverForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context block: (KVOOneShotObserverBlock)block;
#end
NSObject+KVOOneShot.m: (Compile with -fno-objc-arc so we can be explicit about retain/releases)
#import "NSObject+KVOOneShot.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
#interface KVOOneShotObserver : NSObject
- (instancetype)initWithBlock: (KVOOneShotObserverBlock)block;
#end
#implementation NSObject (KVOOneShot)
- (void)addKVOOneShotObserverForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context block: (KVOOneShotObserverBlock)block
{
if (!block || !keyPath)
return;
KVOOneShotObserver* observer = nil;
#try
{
observer = [[KVOOneShotObserver alloc] initWithBlock: block];
// Tie the observer's lifetime to the object it's observing...
objc_setAssociatedObject(self, observer, observer, OBJC_ASSOCIATION_RETAIN);
// Add the observation...
[self addObserver: observer forKeyPath: keyPath options: options context: context];
}
#finally
{
// Make sure we release our hold on the observer, even if something goes wrong above. Probably paranoid of me.
[observer release];
}
}
#end
#implementation KVOOneShotObserver
{
void * volatile _block;
}
- (instancetype)initWithBlock: (KVOOneShotObserverBlock)block
{
if (self = [super init])
{
_block = [block copy];
}
return self;
}
- (void)dealloc
{
[(id)_block release];
[super dealloc];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
KVOOneShotObserverBlock block = (KVOOneShotObserverBlock)_block;
// Get the block atomically, so it can only ever be executed once.
if (block && OSAtomicCompareAndSwapPtrBarrier(block, NULL, &self->_block))
{
// Do it.
#try
{
block(keyPath, object, change, context);
}
#finally
{
// Release it.
[block release];
// Remove the observation whenever...
// Note: This can potentially extend the lifetime of the observer until the observation is removed.
dispatch_async(dispatch_get_main_queue(), ^{
[object removeObserver: self forKeyPath: keyPath context: context];
});
// Don't keep us alive any longer than necessary...
objc_setAssociatedObject(object, self, nil, OBJC_ASSOCIATION_RETAIN);
}
}
}
#end
The only potential hitch here is that the dispatch_async deferred removal may marginally extend the lifetime of the observed object by one pass of the main run loop. This shouldn't be a big deal in the common case, but it's worth mentioning. My initial thought was to remove the observation in dealloc, but my understanding is that we don't have a strong guarantee that the observed object will still be alive when the -dealloc of KVOOneShotObserver is called. Logically, that should be the case, since the observed object will have the only "seen" retain, but since we pass this object into API whose implementation we can't see, we can't be completely sure. Given that, this feels like the safest way.
Related
i dont know what is wrong with code below
#import "ViewController.h"
#interface ViewController ()
{
NSMutableArray * buttons;
NSMutableArray * centers;
int counter;
int index;
}
#end
#implementation ViewController
-(void)viewDidLoad
{
[super viewDidLoad];
buttons = [NSMutableArray new];
centers = [NSMutableArray new];//error here expected identifier or '('
}
//other methods
#end
i am getting two arrays, xcode works normally for buttons but it gives error for centers when creating. What may be the problem?
note: i tried deleting derived data.
Okay, I know this isn't the question you asked, but I would like to suggest an alternative to instantiating variables in viewDidLoad. It's something called "lazy instantiation", and it looks like this:
- (NSMutableArray *)buttons
{
if (!_buttons) _buttons = [NSMutableArray new];
return _buttons;
}
When you need to reset the array, just set it to nil. It won't reallocate the memory until you call it again.
P.S. Whatever you do, don't call self.buttons within that method, or you will create an infinite loop.
Generally speaking... in an NSManagedObject class, inside one of the setters for a given float dynamic property, is it OK to use the dot-syntax getters for other float dynamic properties of the same NSManagedObject within that setter? Or do I need to use KVC-compliant CoreData accessors any time I access a value, even if it's from a different method than the actual getter for the value being accessed? I would assume that calling the dot-syntax in this way would cause my other custom accessor to fire, which is OK with me, since inside that there are the proper KVC primitive accessors. But I've seemed to run into weird issues where the dot-syntax either simply fails, or seems to have unpredictable results, and I'm not sure if it's because of the fact I'm using the dot-syntax in an unsafe way, or if there's some other bug I haven't figured out yet.
Here's a code sample of something I'm talking about:
- (void)illTakeYouToTheWoodshed {
float h = self.SSx.floatValue/self.yourMomsCurrentWeightInTons.floatValue;
[self willChangeValueForKey:#"SSy"];
[self setPrimitiveValue:#(h) forKey:#"SSy"];
[self didChangeValueForKey:#"SSy"];
[self diagonal]; //makes sure nd gets set
}
- (void)setSSx:(NSNumber *)value{
[self willChangeValueForKey:#"SSx"];
[self setPrimitiveValue:value forKey:#"SSx"];
[self didChangeValueForKey:#"SSx"];
if(self.WorH==syanara || self.WorH == dude_wtf) {
if(self.SSy.floatValue != 0.0) {
[self doThatFunkyDance];
[self diagonal];
} else if (self.youBetcha.floatValue != 0.0) {
[self whatTheFrakDoesThisEvenDo];
}
} else if (self.WorH==fooBarTastic) {
if(self.yourMomsCurrentWeightInTons.floatValue != 0.0) {
[self illTakeYouToTheWoodshed];
}
} else {
NSLog(#"Escaped with salad not having been tossed.");
}
}
I'm using in-memory NSPersistentStores to hold transient objects. Once the store is unused - which doesn't necessarily mean empty - I'd like to remove it from the coordinator to free up the memory it uses.
My first attempt was to have each controller create a store in its init, and remove it in its dealloc. This didn't work, because background threads were still using NSManagedObjects in that store; it was being removed while it was still being used, and things broke.
My second attempt was to wrap the stores in an object that could remove the real store once the wrapped object's retain count hit zero. That way, the background threads could retain the wrapped store, and it would only be removed once nothing was using it any more. I used message forwarding to create a proxy object, like so:
#interface MyStoreWrapper : NSObject
#property (nonatomic, retain) NSPersistentStore *persistentStore;
+(MyStoreWrapper *) wrappedInMemoryStore;
+(MyStoreWrapper *) wrapStore:(NSPersistentStore *)aStore;
-(id) initWithStore:(NSPersistentStore *)aStore;
#end
#implementation MyStoreWrapper
#synthesize persistentStore;
+(MyStoreWrapper *)wrappedInMemoryStore
{
NSError *error = nil;
NSPersistentStore *store = [[[MyAppDelegate sharedDelegate] persistentStoreCoordinator] addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error];
if (error)
{
[NSException raise:#"Core data error" format:#"Could not add temporary store: %#, %#", error, [error userInfo]];
}
return [self wrapStore:store];
}
+(MyStoreWrapper *)wrapStore:(NSPersistentStore *)aStore
{
return [[[self alloc] initWithStore:aStore] autorelease];
}
-(id)initWithStore:(NSPersistentStore *)aStore
{
self = [super init];
if (self)
{
self.persistentStore = aStore;
}
return self;
}
-(void)dealloc
{
NSError *error = nil;
[[[MyAppDelegate sharedDelegate] persistentStoreCoordinator] removePersistentStore:self.persistentStore error:&error];
if (error)
{
[NSException raise:#"Core data error" format:#"Could not remove temporary store: %#, %#", error, [error userInfo]];
}
[super dealloc];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([self.persistentStore respondsToSelector:[anInvocation selector]])
{
[anInvocation invokeWithTarget:self.persistentStore];
}
else
{
[super forwardInvocation:anInvocation];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([super respondsToSelector:aSelector])
{
return YES;
}
else
{
return [self.persistentStore respondsToSelector:aSelector];
}
}
- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
if (!signature)
{
signature = [self.persistentStore methodSignatureForSelector:aSelector];
}
return signature;
}
+(BOOL)instancesRespondToSelector:(SEL)aSelector
{
if ([super instancesRespondToSelector:aSelector])
{
return YES;
}
else
{
return [NSPersistentStore instancesRespondToSelector:aSelector];
}
}
+(NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature* signature = [super instanceMethodSignatureForSelector:aSelector];
if (!signature)
{
signature = [NSPersistentStore instanceMethodSignatureForSelector:aSelector];
}
return signature;
}
#end
...but this wrapped object doesn't seem to be a valid substitute in e.g. NSFetchRequests.
Have I made some mistake in my wrapper class? Is there some other way to remove an NSPersistentStore from my coordinator once it is unused?
You can't have a question that says both Once the store is unused and then because background threads were still using NSManagedObjects in that store.
By definition, if background threads are still using it, it's not unused ;)
Your method is correct but you are deallocing too early. Your background threads should be retaining the store if they are interested in it's contents. That way, as soon as all your background threads are finished, they will call release and the store will dealloc itself safely.
I have the following code but can't compile it because I have a "Type name requires a specifier or qualifier" error" for (self).
How to fix this error? I have compared it with the original code and there are no differences, so I don't know what's going on.
#import "CurrentTimeViewController.h"
#implementation CurrentTimeViewController
{
// Call the superclass's designated initializer
self = [super initWithNibName:#"CurrentTimeViewController"
bundle:nil];
if (self) {
// Get the tab bar item
UITabBarItem *tbi = [self tabBarItem];
// Give it a label
[tbi setTitle:#"Time"];
}
return self;
}
Here is the code from the mirror file HynososViewController.h, and which I cut, pasted and modified:
#import "HypnososViewController.h"
#implementation HypnososViewController
- (id) init
{
// Call the superclass's designated initializer
self = [super initWithNibName:nil
bundle:nil];
if (self) {
// Get the tab bar item
UITabBarItem *tbi = [self tabBarItem];
// Give it a label
[tbi setTitle:#"Hypnosis"];
}
return self;
}
- (id) initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
{
// Disregard parameters - nib name is an implementation detail
return [self init];
}
// This method gets called automatically when the view is created
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Loaded the view for HypnosisViewController");
// Set the background color of the view so we can see it
[[self view] setBackgroundColor:[UIColor orangeColor]];
}
#end
Here is the complete code for CurrentTimeViewController.h:
#import "CurrentTimeViewController.h"
#implementation CurrentTimeViewController
{
// Call the superclass's designated initializer
self = [super initWithNibName:#"CurrentTimeViewController"
bundle:nil];
if (self) {
// Get the tab bar item
UITabBarItem *tbi = [self tabBarItem];
// Give it a label
[tbi setTitle:#"Time"];
}
return self;
}
- (id) initWithNibName:(NSString *)nibName bundle:(NSBundle *)Bundle
{
// Disregard parameters - nib name is an implementation detail
return [self init];
}
// This method gets called automatically when the view is created
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Loaded the view for CurrentTimeViewController");
// Set the background color of the view so we can see it
[[self view] setBackgroundColor:[UIColor greenColor]];
}
#end
Is the code above a cut-and-paste of EXACTLY what you are trying to compile? If so, I think you are missing something very important that would make that code block a method implementation:
-(id)init // This, or something like it, is missing???
{
...
}
Check your code, here, and in your project. :-)
From wikipedia explanation about thread-safety,thread safe codes can be run in multithreads.
For iOS 3.x, UIKit is not thread safety, since 4.0, UIKIt is thread safety.
In our implementations, we can use synchronized to build thread safety codes. My questions about thread safety are:
1). How to detect thread safety coding issue with instruments tools or other ways ?
2). Any good practices to write thread safety codes for iOS development ?
since 4.0, UIKIt is thread safety.
Basically, UIKit is not thread-safe. Only drawing to a graphics context in UIKit is thread-safe since 4.0.
1) Hmm, I also want to know about that :-)
2) How about Concurrency Programming Guide?
To make a non thread-safe object thread safe, consider using a proxy (see the code below). I use it for example for NSDateFormatter, which is not a thread safe class, when parsing data in a background thread.
/**
#brief
Proxy that delegates all messages to the specified object
*/
#interface BMProxy : NSProxy {
NSObject *object;
BOOL threadSafe;
}
#property(atomic, assign) BOOL threadSafe;
- (id)initWithObject:(NSObject *)theObject;
- (id)initWithObject:(NSObject *)theObject threadSafe:(BOOL)threadSafe;
#end
#implementation BMProxy
#synthesize threadSafe;
- (id)initWithObject:(NSObject *)theObject {
object = [theObject retain];
return self;
}
- (id)initWithObject:(NSObject *)theObject threadSafe:(BOOL)b {
if ((self = [self initWithObject:theObject])) {
self.threadSafe = b;
}
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [object methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if (self.threadSafe) {
#synchronized(object) {
[anInvocation setTarget:object];
[anInvocation invoke];
}
} else {
[anInvocation setTarget:object];
[anInvocation invoke];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector {
BOOL responds = [super respondsToSelector:aSelector];
if (!responds) {
responds = [object respondsToSelector:aSelector];
}
return responds;
}
- (void)dealloc {
[object release];
[super dealloc];
}
#end