I've watched some of the WWDC videos on Core Data and I'm planning on maintaining a canonicalized text property.
Let's say I have the following data:
originalString normalizedString (+lowercase?)
Ønsker onsker
onsker onsker
Onsker onsker
When I query my model, I want to sort it by 'normalizedString' so that it ignores the case and the Ø (or other characters). I also want to be able to run a query like "starts with 'o'" and have it return the 3 words above.
I was trying to avoid to do something like:
[NSPredicate predicateWithFormat:#"(originalString like[cd] %#)"...
for querying the model.
I was also trying to use the 'originalString' for my sorting.
I've tried two different approaches with no success, my normalized string is still saved as the originalString (I override the setter in a category I created):
Calling decomposedStringWithCanonicalMapping:
// ...
[normalizedString decomposedStringWithCanonicalMapping];
// ...
Followed this example:
// ...
CFStringNormalize((CFMutableStringRef)normalizedString, kCFStringNormalizationFormD);
CFStringFold((CFMutableStringRef)normalizedString, kCFCompareCaseInsensitive | kCFCompareDiacriticInsensitive | kCFCompareWidthInsensitive, NULL);
Any ideas on how I can accomplish my goal?
Edit:
Here's my overridden setter, which I know it gets called:
- (void) setNormalizedName:(NSString *)newNormalizedName
{
NSMutableString *normalizedString;
if (![self.lastName length] == 0) {
normalizedString = [NSMutableString stringWithString:self.lastName];
} else {
normalizedString = [NSMutableString stringWithString:self.firstName];
}
// CFStringNormalize((CFMutableStringRef)normalizedString, kCFStringNormalizationFormD);
// CFStringFold((CFMutableStringRef)normalizedString, kCFCompareCaseInsensitive | kCFCompareDiacriticInsensitive | kCFCompareWidthInsensitive, NULL);
[normalizedString decomposedStringWithCanonicalMapping];
[self willChangeValueForKey:#"normalizedName"];
[self setPrimitiveValue:normalizedString forKey:#"normalizedName"];
[self didChangeValueForKey:#"normalizedName"];
}
You should override the setters for the "primary" properties (e.g. firstName, lastName), and not the setter for the "derived" property.
Also note that decomposedStringWithCanonicalMapping returns a new string, it does not
modify the receiver.
The code could roughly look like this (not compiler checked):
- (void) setFirstName:(NSString *)firstName
{
[self willChangeValueForKey:#"firstName"];
[self setPrimitiveValue:firstName forKey:#"firstName"];
[self didChangeValueForKey:#"firstName"];
[self updateNormalizedName];
}
- (void) setLastName:(NSString *)lastName
{
[self willChangeValueForKey:#"lastName"];
[self setPrimitiveValue:lastName forKey:#"lastName"];
[self didChangeValueForKey:#"lastName"];
[self updateNormalizedName];
}
- (void) updateNormalizedName
{
NSString *normalizedString;
if ([self.lastName length] > 0) {
normalizedString = [self.lastName decomposedStringWithCanonicalMapping];
} else {
normalizedString = [self.firstName decomposedStringWithCanonicalMapping];
}
self.normalizedString = normalizedString;
}
I am developing an Objective-C application, and what I want to do, is something like the following:
+-----------------+ +---------------+
| Some Object | <---------- | Synchronize |
|(Not Thread Safe)| | Proxy |
+-----------------+ / +---------------+
/
/ Intercepts [someobject getCount]
/ #synchronize (someObject)
/
[someObject getCount] /
+----------------------+
| Some Calling Object |
+----------------------+
What I've asking is, how can I create an object in objective-c, that intercepts messages sent to another object, in order to perform code before the message is sent to that object.
Some things that I think will not work:
Categories (I need this to only happen for certain instances of a class)
Rewriting the object (I don't have access to the source of the object)
Method swizzling (once again, this need to only happen for certain instances of a class)
You would implement an NSProxy that forwards messages to your non-thread-safe object.
Here is a nice writeup of message forwarding in Objective-C, and here is Apple's documentation.
To handle thread safety, it depends on what you need. If your non-thread-safe object must run on a specific thread then you can use a NSRunLoop on said thread to serialize messages to that object.
Here is an example of using NSInvocation in conjunction with NSRunLoop. In that example they're using performSelector:withObject:afterDelay: but to use it with performSelector:onThread:withObject:waitUntilDone: would be very similar.
Otherwise, just use a single NSRecursiveLock in your proxy.
If you know exactly what instances should have the behavior you are trying to achieve you can go with method swizzling and call the base implementation if the instance is not the one you are looking for.
You can have a global shared object that lists the "interesting" instances and use it in the swizzling implementation whether you have to call the base one or your custom one.
So, I bit the bullet, and decided to make my own proxy class. To subclass, you simply override the 'forwardInvocation:' message, and you call any code you need there, before calling [super forwardInvocation:]. Please not this will NOT work with vardic methods, as NSInvocation doesn't work with vardic methods.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/objc.h>
#import <objc/message.h>
#interface RJProxy : NSObject {
#private
NSObject *target;
}
#property(readwrite, retain) NSObject *target;
-(NSObject *) getTarget;
#end
#implementation RJProxy
#synthesize target;
-(NSMethodSignature *) methodSignatureForSelector:(SEL)aSelector
{
if (objc_getAssociatedObject(self, "isProxy"))
{
IMP NSObjectImp = [NSObject instanceMethodForSelector:#selector(methodSignatureForSelector:)];
NSMethodSignature *methodSignature = (NSMethodSignature *) NSObjectImp(self, #selector(methodSignatureForSelector:), aSelector);
if (methodSignature)
return methodSignature;
return [target methodSignatureForSelector:aSelector];
}
else
{
Class subClass = self->isa;
#try {
self->isa = objc_getAssociatedObject(self, "realSuperclass");
return [super methodSignatureForSelector:aSelector];
}
#finally {
self->isa = subClass;
}
}
}
-(void) forwardInvocation:(NSInvocation *)anInvocation
{
if (objc_getAssociatedObject(self, "isProxy"))
{
Class subClass = target->isa;
target->isa = objc_getAssociatedObject(self, "realSuperclass");
[anInvocation invokeWithTarget:target];
target->isa = subClass;
}
else
{
Class realSuperclass = objc_getAssociatedObject(self, "realSuperclass");
Class subclass = self->isa;
self->isa = realSuperclass;
if ([self respondsToSelector:[anInvocation selector]])
{
[anInvocation invokeWithTarget:self];
}
else
{
[self doesNotRecognizeSelector:[anInvocation selector]];
}
self->isa = subclass;
}
}
-(NSObject *) getTarget
{
if (objc_getAssociatedObject(self, "isProxy"))
{
return target;
}
return self;
}
#end
BOOL object_setProxy(NSObject *object, RJProxy *proxy);
BOOL object_setProxy(NSObject *object, RJProxy *proxy)
{
proxy.target = object;
Class objectClass = object_getClass(object);
Class objectSub = objc_allocateClassPair(objectClass, [[NSString stringWithFormat:#"%s_sub%i", class_getName(objectClass), objc_getAssociatedObject(objectClass, "subclassTimes")] UTF8String], 0);
objc_setAssociatedObject(objectClass, "subclassTimes", (id) ((int) objc_getAssociatedObject(objectClass, "subclassTimes") + 1), OBJC_ASSOCIATION_ASSIGN);
objc_registerClassPair(objectSub);
Class proxyClass = object_getClass(proxy);
Class proxySub = objc_allocateClassPair(proxyClass, [[NSString stringWithFormat:#"%s_sub%i", class_getName(proxyClass), objc_getAssociatedObject(proxyClass, "subclassTimes")] UTF8String], 0);
objc_setAssociatedObject(proxyClass, "subclassTimes", (id) ((int) objc_getAssociatedObject(proxyClass, "subclassTimes") + 1), OBJC_ASSOCIATION_ASSIGN);
objc_registerClassPair(proxySub);
object_setClass(object, proxySub);
object_setClass(proxy, proxySub);
objc_setAssociatedObject(object, "isProxy", (id) NO, OBJC_ASSOCIATION_ASSIGN);
objc_setAssociatedObject(proxy, "isProxy", (id) YES, OBJC_ASSOCIATION_ASSIGN);
objc_setAssociatedObject(object, "realSuperclass", objectClass, OBJC_ASSOCIATION_ASSIGN);
objc_setAssociatedObject(proxy, "realSuperclass", proxyClass, OBJC_ASSOCIATION_ASSIGN);
return NO;
}
#interface SynchronizeProxy : RJProxy
#end
#implementation SynchronizeProxy
-(void) forwardInvocation:(NSInvocation *)anInvocation {
#synchronized ([self getTarget])
{
[super forwardInvocation:anInvocation];
}
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSArray *arrayToSynchronize = [NSArray arrayWithObjects:#"This, is, a, test!", nil];
SynchronizeProxy *myProxy = [SynchronizeProxy new];
object_setProxy(arrayToSynchronize, myProxy);
// now all calls will be synchronized!
NSLog(#"Array at address 0x%X with count of %lu, and Objects %# ", (unsigned) arrayToSynchronize, [arrayToSynchronize count], arrayToSynchronize);
[myProxy release];
[arrayToSynchronize release];
}
return 0;
}
I'm having a problem with NSValueTransformer, instead of transforming the value (NSString) it's putting NULL into the database where the attributes are transformable. I'm using the apple example, which is the same are everyone else's example and I just can't get it to work on a simple level. I'm also putting breakpoints at transformedValue and reverseTransformedValue, but they never get hit.
My custom class
+ (void)registerValueTransformer {
[NSValueTransformer setValueTransformer:[[[self alloc] init] autorelease] forName:#"myTransformerTest"];
}
#pragma mark -
#pragma mark NSValueTransformer implementation
+ (BOOL)allowsReverseTransformation {
return YES;
}
+ (Class)transformedValueClass {
return [NSString class];
}
- (id)transformedValue:(id)value {
return #"Test in";
}
- (id)reverseTransformedValue:(id)value {
return #"Test Out";
}
And this in my app delegate
+(void) initialize
{
[super initialize];
[transformerTest registerValueTransformer];
}
And then in Core data I've made my attribute transformable with a name "myTransformerTest". And according to the apple documentation that should be it.
is there any difference between the two methods?
```
// MACRO FUNCTION
#define SAFELY_RELEASE(__POINTER) { [__POINTER release]; __POINTER = nil; }
// C FUNCTION
void SAFELY_RELEASE(id __POINTER) {
[__POINTER release]; __POINTER = nil;
}
```
Yes. The function won't do what you expect it to, because the pointer will have been passed into it by value, rather than by reference.
Imagine this:
- (void)method {
id object = [[NSObject alloc] init];
SAFELY_RELEASE( object );
}
SAFELY_RELEASE gets object. It can send it messages, but setting it to nil will not change its value in method.
An equivalent function would be:
void SAFELY_RELEASE(id *__POINTER) {
[*__POINTER release]; *__POINTER = nil;
}
Then you'd use it by using:
SAFELY_RELEASE( &object );
The macro has another downside, though: Xcode's refactoring tools will probably not be able to change the parameter inside. For instance:
#interface Foo {
NSObject *var;
}
#implementation Foo
- (id)init {
if (( self = [super init] )) {
var = [[NSObject alloc] init];
}
return self;
}
- (void)dealloc {
SAFELY_RELEASE(var);
[super dealloc];
}
If you try to rename var using the refactor tool, you'll probably find that it won't be able to rename the var in dealloc.
Really, unless you have a really good reason to do this you should be using ARC.
void SAFELY_RELEASE(id __POINTER) is guaranted to __POINTER be an releasable object.
Both of them is wrong, because __POINTER = nil; will have no effects. You should nil it in object (controller)
I was using one of these macros, then I cam across this post.
Makes sense that you don't want to set to nil while in development as you want it to crash on a dealloc reference and not sending it to nil.
I have an object that i intalize it's propertis with call to an init function, it works fine,
when i tried to add another object and intalize it the first object didn't get the properites, how do i initalize it to more then one object with diffrent or the same properties?
- (void)viewDidLoad {
pic1 = [[peopleAccel alloc] init];
}
Class peopleAccel:
- (id) init
{
self = [super init];
if (self != nil) {
position = CGPointMake(100.0, 100.0);
velocity = CGPointMake(4.0, 4.0);
radius = 40.0;
bounce = -0.1f;
gravity = 0.5f;
dragging = NO;
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
acceleratedGravity = CGPointMake(0.0, gravity);
}
return self;
}
I see a problem with setting the delegate of sharedAccelerometer. Only one object can be a delegate of another object at a time. So you should create only one peopleAccel object.
EDIT:
If you need to send accelerometer events to more than one object, you can create a specific delegate object in charge of receiving accelerometer events and broadcasting them to your several peopleAccel objects via notifications. See this question for some hints: NSNotificationCenter vs delegation?
Create a proxy so multiple objects can receive accelerometer events.
Whether you should do this or use NSNotificationCenter is debatable and there are two camps, but personally I would use this approach. NSNotificationCenter has to check string names to recognise event type; this kind of approach could be ever so slightly faster especially with a bit more optimisation. A bit more typing but I would say also easier for someone else to follow.
Something like this...
/* PLUMBING */
/* in headers */
#protocol MyAccelSubDelegate
-(void)accelerometer:(UIAccelerometer*)accelerometer
didAccelerate:(UIAcceleration*)acceleration;
#end
#interface MyAccelSubDelegateProxy : NSObject <UIAccelerometerDelegate> {
NSMutableArray subDelegates;
}
-(id)init;
-dealloc;
-(void)addSubDelegate:(id<MyAccelSubDelegate>)subDelegate;
-(void)removeSubDelegate:(id<MyAccelSubDelegate>)subDelegate;
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:
(UIAcceleration *)acceleration;
#end
/* in .m file */
#implementation MyAccelSubDelegateProxy
-(id)init { self = [super init];
if (self!=nil) subDelegates = [[NSMutableArray alloc] init]; return self; }
-dealloc { [subDelegates release]; }
-(void)addSubDelegate:(id<MyAccelSubDelegate>)subDelegate {
[subDelegates insertObject:subDelegate atIndex:subDelegates.count]; }
-(void)removeSubDelegate:(id<MyAccelSubDelegate>)subDelegate {
for (int c=0; c < subDelegates.count; c++) {
id<MyAccelSubDelegate> item = [subDelegates objectAtIndex:c];
if (item==subDelegate) { [subDelegates removeObjectAtIndex:c];
c--; continue; }
}
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:
(UIAcceleration *)acceleration {
for (int c=0; c < subDelegates.count; c++)
[((id<MyAccelSubDelegate>)[subDelegates objectAtIndex:c])
accelerometer:accelerometer didAccelerate:acceleration];
}
#end
/* SOMEWHERE IN MAIN APPLICATION FLOW STARTUP */
accelProxy = [[MyAccelSubDelegateProxy alloc] init];
[UIAccelerometer sharedAcclerometer].delegate = accelProxy;
[UIAccelerometer sharedAcclerometer].updateInterval = 0.100; // for example
/* TO ADD A SUBDELEGATE */
[accelProxy addSubDelegate:obj];
/* TO REMOVE A SUBDELEGATE */
[accelProxy removeSubDelegate:obj];
/* SOMEWHERE IN MAIN APPLICATION SHUTDOWN */
[UIAccelerometer sharedAcclerometer].delegate = nil;
[accelProxy release];