On Apr 28, 7:34 am, Paulo Roberto Schwertner
See below. You're welcome to use these patches. I release them into
the public domain. Since this is a mostly abandoned project while we
all wait for v3.0 of iPhone OS, perhaps you should consider helping
getting them into the trunk. I have never used the change management
system.
Anon,
Andrew
In SQLitePersistentObject.h:
// As a performance enhancement, this method should be overridden to
cache the dictionary of properties in your class.
+ (NSDictionary *) propertiesWithEncodedTypes;
+ (NSMutableDictionary *) propertiesWithEncodedTypes_; // private.
Here is how I overrode the method in my class.
+ (NSDictionary *) propertiesWithEncodedTypes {
static NSDictionary *classProperties = nil;
if (classProperties == nil) {
classProperties = [NSDictionary dictionaryWithDictionary: [[self
class] propertiesWithEncodedTypes_]];
[classProperties retain]; // Because it is stored in a static, we
must retain it.
}
return classProperties;
} // propertiesWithEncodedTypes
In SQLitePersistentObject.m:
+(NSDictionary *) propertiesWithEncodedTypes {
// Call the standard routine unless overridden.
return [[self class] propertiesWithEncodedTypes_];
} // propertiesWithEncodedTypes
+(NSMutableDictionary *) propertiesWithEncodedTypes_
{
// Recurse up the classes, but stop at NSObject. Each class only
reports its own properties, not those inherited from its superclass
NSMutableDictionary *theProps;
if ([self superclass] != [NSObject class])
theProps = (NSMutableDictionary *)[[self superclass]
propertiesWithEncodedTypes_];
else
theProps = [NSMutableDictionary dictionary];
unsigned int outCount;
#ifndef TARGET_OS_COCOTRON
objc_property_t *propList = class_copyPropertyList([self class],
&outCount);
#else
NSArray *propList = [[self class] getPropertiesList];
outCount = [propList count];
#endif
int i;
// Loop through properties and add declarations for the create
for (i=0; i < outCount; i++)
{
#ifndef TARGET_OS_COCOTRON
objc_property_t oneProp = propList[i];
NSString *propName = [NSString stringWithUTF8String:property_getName
(oneProp)];
NSString *attrs = [NSString stringWithUTF8String:
property_getAttributes(oneProp)];
// Read only attributes are assumed to be derived or calculated
// See
http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/chapter_8_section_3.html
if ([attrs rangeOfString:@",R,"].location == NSNotFound)
{
NSArray *attrParts = [attrs componentsSeparatedByString:@","];
if (attrParts != nil)
{
if ([attrParts count] > 0)
{
NSString *propType = [[attrParts objectAtIndex:0]
substringFromIndex:1];
[theProps setObject:propType forKey:propName];
}
}
}
#else
NSArray *oneProp = [propList objectAtIndex:i];
NSString *propName = [oneProp objectAtIndex:0];
NSString *attrs = [oneProp objectAtIndex:1];
[theProps setObject:attrs forKey:propName];
#endif
}
#ifndef TARGET_OS_COCOTRON
free( propList );
#endif
return theProps;
} // propertiesWithEncodedTypes_
+ (BOOL)resolveClassMethod:(SEL)theMethod
{
@synchronized(self)
{
const char *methodName = sel_getName(theMethod);
// swap comments on the below two lines - awd
// NSString *methodBeingCalled = [[NSString alloc]
initWithUTF8String:methodName];
NSString *methodBeingCalled = [NSString
stringWithUTF8String:methodName];
if ([methodBeingCalled hasPrefix:@"findBy"])
{
NSRange theRange = NSMakeRange(6, [methodBeingCalled length] - 7);
NSString *property = [[methodBeingCalled
substringWithRange:theRange] stringByLowercasingFirstLetter];
NSDictionary *properties = [self propertiesWithEncodedTypes];
NSLog(@"Property: %@", property);
if ([[properties allKeys] containsObject:property])
{
SEL newMethodSelector = sel_registerName([methodBeingCalled
UTF8String]);
// Hardcore juju here, this is not documented anywhere in the
runtime (at least no
// anywhere easy to find for a dope like me), but if you want to
add a class method
// to a class, you have to get the metaclass object and add the
clas to that. If you
// add the method
#ifndef TARGET_OS_COCOTRON
Class selfMetaClass = objc_getMetaClass([[self className]
UTF8String]);
return (class_addMethod(selfMetaClass, newMethodSelector, (IMP)
findByMethodImp, "@@:@")) ? YES : [super
resolveClassMethod:theMethod];
#else
if(class_getClassMethod([self class], newMethodSelector) != NULL)
{
return [super resolveClassMethod:theMethod];
} else {
BOOL isNewMethod = YES;
Class selfMetaClass = objc_getMetaClass([[self className]
UTF8String]);
struct objc_method *newMethod = calloc(sizeof(struct
objc_method), 1);
struct objc_method_list *methodList = calloc(sizeof(struct
objc_method_list)+sizeof(struct objc_method), 1);
newMethod->method_name = newMethodSelector;
newMethod->method_types = "@@:@";
newMethod->method_imp = (IMP) findByMethodImp;
methodList->method_next = NULL;
methodList->method_count = 1;
memcpy(methodList->method_list, newMethod, sizeof(struct
objc_method));
free(newMethod);
class_addMethods(selfMetaClass, methodList);
assert(isNewMethod);
return YES;
}
#endif
}
else
return [super resolveClassMethod:theMethod];
}
// TODO: This is due for some heavy refactoring - too much copy &
paste going on...
else if ([methodBeingCalled rangeOfString:@"Of"].location !=
NSNotFound)
{
NSRange rangeOfOf = [methodBeingCalled rangeOfString:@"Of"];
NSString *operation = [methodBeingCalled
substringToIndex:rangeOfOf.location];
if ([operation isEqualToString:@"sum"] || [operation
isEqualToString:@"avg"]
|| [operation isEqualToString:@"average"] || [operation
isEqualToString:@"min"]
|| [operation isEqualToString:@"max"] || [operation
isEqualToString:@"count"])
{
NSRange criteriaRange = [methodBeingCalled
rangeOfString:@"WithCriteria"];
if (criteriaRange.location == NSNotFound)
{
// Do for all
NSRange theRange = NSMakeRange(rangeOfOf.location +
rangeOfOf.length, [methodBeingCalled length] - (rangeOfOf.location +
rangeOfOf.length));
NSString *property = [[methodBeingCalled
substringWithRange:theRange] stringByLowercasingFirstLetter];
NSDictionary *properties = [self propertiesWithEncodedTypes];
if ([[properties allKeys] containsObject:property])
{
SEL newMethodSelector = sel_registerName([methodBeingCalled
UTF8String]);
Class selfMetaClass = objc_getMetaClass([[self className]
UTF8String]);
return (class_addMethod(selfMetaClass, newMethodSelector, (IMP)
aggregateMethodImp, "@@:")) ? YES : [super
resolveClassMethod:theMethod];
}
}
else
{
// do with criteria
NSRange theRange = NSMakeRange(rangeOfOf.location +
rangeOfOf.length, [methodBeingCalled length] - criteriaRange.length -
(rangeOfOf.length + rangeOfOf.location) - 1);
NSString *property = [[methodBeingCalled
substringWithRange:theRange] stringByLowercasingFirstLetter];
NSDictionary *properties = [self propertiesWithEncodedTypes];
if ([[properties allKeys] containsObject:property])
{
SEL newMethodSelector = sel_registerName([methodBeingCalled
UTF8String]);
Class selfMetaClass = objc_getMetaClass([[self className]
UTF8String]);
return (class_addMethod(selfMetaClass, newMethodSelector, (IMP)
aggregateMethodWithCriteriaImp, "@@:@")) ? YES : [super
resolveClassMethod:theMethod];
}
}
}
}
return [super resolveClassMethod:theMethod];
}
return NO;
}
In NSDateSQLitePersistence.m:
In particular, I've been making the smallest changes possible. This
static could also be used by the following method.
#if 1 // Create a static date formatter. - awd
+ (id)objectWithSqlColumnRepresentation:(NSString *)columnData;
{
#ifdef TARGET_OS_COCOTRON
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc]
initWithDateFormat:@"%Y-%m-%d %H:%M:%S.%F" allowNaturalLanguage:NO]
autorelease];
NSDate *d;
BOOL cvt = [dateFormatter getObjectValue:&d forString:columnData
errorDescription:nil];
assert(cvt);
return d;
#else
static NSDateFormatter *dateFormatter = nil;
if (dateFormatter == nil) {
// We're not autoreleasing this because it lives in a static.
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSSS"];
}
return [dateFormatter dateFromString:columnData];
#endif
}
#else
+ (id)objectWithSqlColumnRepresentation:(NSString *)columnData;
{
#ifdef TARGET_OS_COCOTRON
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc]
initWithDateFormat:@"%Y-%m-%d %H:%M:%S.%F" allowNaturalLanguage:NO]
autorelease];
NSDate *d;
BOOL cvt = [dateFormatter getObjectValue:&d forString:columnData
errorDescription:nil];
assert(cvt);
return d;
#else
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init]
autorelease];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSSS"];
return [dateFormatter dateFromString:columnData];
#endif
}
#endif