Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 227 lines (195 sloc) 7.042 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
#import "LPManagedObjectContext.h"
#import "LPManagedObject.h"
#import <objc/runtime.h>
#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#endif
#import "LPManagedObjectObservationInfo.h"
#import "LPManagedObject_ObservationInfo.h"

#ifndef DEBUG_OBSERVING
#define DEBUG_OBSERVING 0
#endif

// this method is needed to properly restart observing in deleted objects after undo
// in 10.6 and iPhone OS a different solution is possible!
@interface NSManagedObjectContext (HiddenMethods)
- (void)_undoDeletions:(id)object;
@end

@interface LPManagedObjectContext (PrivateMethods)
-(void) processEntity:(NSEntityDescription*) entityDescription;
-(void) addObservationInfo:(LPManagedObjectObservationInfo*) newObservationInfo;
-(void) addObservationInfosFromArray:(NSArray*) observationInfos;
@end

#pragma mark -
@implementation LPManagedObjectContext
@synthesize dependendPropertiesObservationInfo;
@synthesize observingsActive;
@synthesize isMergingChanges;

- (id) init
{
self = [super init];
if (self != nil)
{
observingsActive = YES;
self.dependendPropertiesObservationInfo = nil;
        [super setRetainsRegisteredObjects:YES];
}
return self;
}

- (void) dealloc
{
    // stop all observings
    [self stopObserving];
        
// cleanup dictionary as last step after all managedObjects died
// workaround necessary here?
// NSMutableDictionary * temporaryRetainedDictionary = [self.dependendPropertiesObservationInfo retain];
    [dependendPropertiesObservationInfo release], dependendPropertiesObservationInfo = nil;
[super dealloc];
// [temporaryRetainedDictionary release];
// temporaryRetainedDictionary = nil;
}

#pragma mark -

- (void)setRetainsRegisteredObjects:(BOOL)flag
{
    NSAssert(flag == NO, @"LPManagedObjectContext must retain its registered objects!");
    [super setRetainsRegisteredObjects:flag];
}

#pragma mark Dependend properties specific code

// uses the ManagedObjectModel of the current ManagedObjectContext to scan all classes for relevant methods indicating dependencies with keyPathsForValuesAffectingDerived<Key>
// creates LPManagedObjectObservationInfo objects for each dependency found
// does basic validation of given information
-(void) prepareDependentProperties
{
NSAssert(self.dependendPropertiesObservationInfo == nil, @"preparedDependendProperties must not be called twice");
NSAssert([self persistentStoreCoordinator] != nil, @"Error missing persistentStoreCoordinator");
NSAssert([[self persistentStoreCoordinator] managedObjectModel] != nil, @"Error missing managedObjectModel");

self.dependendPropertiesObservationInfo = [NSMutableDictionary dictionary];

NSManagedObjectModel *managedObjectModel = [[self persistentStoreCoordinator] managedObjectModel];

// check each entity in object model for dependend properties
for (NSString* entityName in [[managedObjectModel entitiesByName] allKeys])
{
NSEntityDescription* entity = [[managedObjectModel entitiesByName] objectForKey:entityName];
NSString *className = [entity managedObjectClassName];
Class entityClass = NSClassFromString(className);

// only process classes inherited from LPManagedObject
if ([entityClass isInheritedFromClass:[LPManagedObject class]])
{
if (DEBUG_OBSERVING) NSLog(@"willAnalyze: %@", entityClass);
[self processEntity:entity];
}
}
}

- (void)startObserving
{
    self.observingsActive = YES;
    for (LPManagedObject *object in self.registeredObjects)
    {
        if ([object isKindOfClass:[LPManagedObject class]] && ![object isFault])
        {
            [object startObserving];
        }
    }
    if (DEBUG_OBSERVING) NSLog(@"all observings started");
}

- (void)stopObserving
{
    self.observingsActive = NO;
    for (LPManagedObject *object in self.registeredObjects)
    {
        if ([object isKindOfClass:[LPManagedObject class]] && object.observingsActive)
        {
            [object stopObserving];
        }
    }

    if (DEBUG_OBSERVING) NSLog(@"all observings stopped");
}


@end

#pragma mark -
@implementation LPManagedObjectContext (PrivateMethods)

-(void) processEntity:(NSEntityDescription*) entityDescription
{
// get class of entity
Class managedObjectClass = NSClassFromString([entityDescription managedObjectClassName]);

//process class if inherited from LPManagedObject
if ([managedObjectClass isInheritedFromClass: [LPManagedObject class]])
{
NSArray* propertiesWithDependencyInformation = [managedObjectClass propertiesWithDependencyInformation];
for (NSString* propertyName in propertiesWithDependencyInformation)
{
NSArray* observerInformationForProperty = [managedObjectClass observerInformationForProperty:propertyName withEntityDescription: entityDescription];
[self addObservationInfosFromArray:observerInformationForProperty];
}
}
}

-(void) addObservationInfosFromArray:(NSArray*) observationInfos
{
for (LPManagedObjectObservationInfo* newObservationInfo in observationInfos)
{
[self addObservationInfo:newObservationInfo];
}
}

// adds observation info to the internal observation info dictionary
-(void) addObservationInfo:(LPManagedObjectObservationInfo*) newObservationInfo
{
    if (DEBUG_OBSERVING)
    {
        NSLog(@"add observer information: %@", newObservationInfo);
    }
NSMutableArray* observationInfosForClass = [self.dependendPropertiesObservationInfo objectForKey:newObservationInfo.observerClassName];
if (observationInfosForClass == nil)
{
observationInfosForClass = [NSMutableArray array];
[self.dependendPropertiesObservationInfo setObject:observationInfosForClass forKey:newObservationInfo.observerClassName];
}
[observationInfosForClass addObject:newObservationInfo];
}


// needed for 10.5 backwards compatibility
// in 10.6 use
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
#if (__MAC_OS_X_VERSION_MIN_REQUIRED < 1060)

#error Make sure you really want to use this code!
- (void)_undoDeletions:(id)deletions
{
[super _undoDeletions:deletions];
@try
{
for (id deletion in deletions)
        {
            if([deletion isKindOfClass:[NSManagedObject class]])
            {
                [deletion awakeFromFetch]; // treating this as a fetch works for my purposes
            }
            else if ([deletion isKindOfClass:[NSArray class]])
            {
                for(id object in deletion)
                {
                    if([object isKindOfClass:[NSManagedObject class]])
                        [object awakeFromFetch]; // treating this as a fetch works for my purposes
                }
            }
        }
}
@catch (NSException *exception)
{

}
}
#endif
#endif

- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification
{
    @try
    {
        self.isMergingChanges = YES;
        [self stopObserving];
        [super mergeChangesFromContextDidSaveNotification:notification];
    }
    @catch (NSException *exception)
    {
        [exception raise];
    }
    @finally
    {
        self.isMergingChanges = NO;
        [self startObserving];
    }
}
@end

Something went wrong with that request. Please try again.