Obj-C: How to improve repetitive method implementations using method forwarding - iphone

I have an implementation which contains a set of methods which do pretty much the same:
- (NSString *) specialKey{
[self.mySpecialDictionary valueForKey:#"specialKey"];
}
- (NSString *) anotherKey{
[self.mySpecialDictionary valueForKey:#"mySpecialKey1"];
}
I can now conveniently use those getters like so:
NSString *foo = [Setting sharedInstance].specialKey;
I guess I should now be able to define my Properties dynamic and make one implementation for all those cases, since the string which I look up in my dictionary will always be the name of the getter. I am pretty sure that this should be do-able in Objective-C, but I have no Idea how to achieve this.

The answer is in your question. Try method forwarding:
- (NSMethodSignature*) methodSignatureForSelector:(SEL)selector
{
return [mySpecialDictionary methodSignatureForSelector:#selector(valueForKey:)];
}
- (void) forwardInvocation:(NSInvocation *)invocation
{
NSString* propertyName = NSStringFromSelector(invocation.selector);
[invocation setSelector:#selector(valueForKey:)];
[invocation setArgument:&propertyName atIndex:2];
[invocation invokeWithTarget:mySpecialDictionary];
}
Of course, to get rid of compiler warnings it needs to define every property explicit
#property (nonatomic, readonly) NSString* specialKey;
#property (nonatomic, readonly) NSString* anotherKey;
and provide #dynamic for them.

What about this?
- (NSString *)valueForKey:(NSString *)key
{
return [self.mySpecialDictionary valueForKey:key];
}
this will return the value for the provided key. Or this for more flexible use
- (NSString *)valueForKey:(id)key
{
if([key isKindOfClass:[NSString class]]){
return [self.mySpecialDictionary valueForKey:key];
}
}
Or even this
- (id)valueForKey:(id)key
{
if([key isKindOfClass:[NSString class]]){
return [self.mySpecialDictionary valueForKey:key];
} else if (..){
// catch more possible keys here ...
}
}

Related

How to use methods from NSManagedObject entity using NSManagedObject

I am using a NSManagedObject to save some values, but not using entity for that. Because I want to not limit it to some entities. I tried using (Oist *)managedObject.entity, not working. How to use this managedObject to get theIt's like this:
Oist.h
#class File;
#interface Oist : NSManagedObject
#property (nonatomic, retain) NSSet *files;
#end
#interface Oist (CoreDataGeneratedAccessors)
- (void)addFilesObject:(File *)value;
File.h
#class Oist;
#interface File : NSManagedObject
#property (nonatomic, retain) Oist *gist;
#end
- (void)newManagedObjectWithClassName:(NSString *)className forRecords:(NSDictionary *)records
{
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:className inManagedObjectContext:[[GPCoreDataController sharedInstance] backgroundManagedObjectContext]];
[records enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[self setValue:obj forKey:key forManagedObject:newManagedObject];
}];
}
- (void)setValue:(id)value forKey:(NSString *)key forManagedObject:(NSManagedObject *)managedObject
{
File *fileEntity = [NSEntityDescription insertNewObjectForEntityForName:#"File" inManagedObjectContext:[[GPCoreDataController sharedInstance] backgroundManagedObjectContext]];
if ([key isEqualToString:#"files"])
{
NSDictionary *files = (NSDictionary *)value;
for (NSString *key in [files allKeys]) {
NSDictionary *file = [files valueForKey:key];
NSLog(#"%#", file[#"filename"]);
[fileEntity setValue:file[#"filename"] forKey:#"filename"];
[fileEntity setValue:file[#"type"] forKey:#"type"];
}
} else if ([key isEqualToString:#"id"])
{
if ([managedObject.entity.name isKindOfClass:[Oist class]]) {
//here I want to use Oist's method - (void)addFilesObject:(File *)value; how to do that?
}
[managedObject setValue:[NSNumber numberWithInt:[value integerValue]] forKey:#"gistID"];
}
}
I may be misunderstanding your question, but it looks like you're hoping to cast a generically typed (NSManagedObject *) as a more specifically typed subclass.
The way to check if the pointer passed generically is in fact an instance of your subclass is this:
if ([managedObject isKindOfClass:[Oist self]]) {
// managedObject may safely be cast to an Oist*
}
You can do the cast without an assignment, like this:
[(Oist *)managedObject addFiles...];
Or, with proper parentheses, access a property (the dot operator has higher precedence than the cast, so you need to tell the compiler you want the cast first):
((Oist *)managedObject).entity
But I think that's less clear to the reader and potentially more keystrokes than an explicit assignment with a cast, which is what I'd recommend:
if ([managedObject isKindOfClass:[Oist self]]) {
Oist *oist = (Oist *)managedObject;
[oist addFilesObject:someFilesObject];
}

Why does implicit initialization of a variable not work consistently on iPhone?

So here is my scenario -- In the header file of a class I do:
#interface MyClass : NSObject
{
NSString *string1;
NSString *string2;
}
- (void) methodOne: (NSString *) passedString;
- (void) methodTwo: (NSString *) passedString;
#end
In the implementation file I do this:
#import MyClass.h
#implementation MyClass
- (void) methodOne: (NSString *) passedString
{
NSLog(#"%#", passedString);
string1 = passedString;
}
- (void) methodTwo: (NSString *) passedString
{
NSLog(#"%#", passedString);
string2 = passedString;
}
What I am finding is that there is some kind of inconsistency when doing this versus [NSString alloc] initWithString:].
As you can see string1 and string2 are treated exactly the same, but what is happening is that string1 is getting set, but string2 is remaining empty. I get a bad access when I reference it later.
I thought maybe I was passing an empty string to methodTwo: so I added that NSLog which proves that it is not empty, but has the expected string.
Since I have noticed this inconsistency before I decided to change to this:
- (void) methodOne: (NSString *) passedString
{
NSLog(#"%#", passedString);
string1 = passedString;
}
- (void) methodTwo: (NSString *) passedString
{
NSLog(#"%#", passedString);
string2 = [[NSString alloc] initWithString: passedString];
}
Now both strings are working as expected. My question is why is there this inconsistency?
This is not the only time this has happened to me. It has happened with all kinds of objects. The only thing that seems to work every time is alloc init. Methods like stringWithString: work most of the time, but not always.
It's because in the first example, you don't retain or copy the strings. string2 is getting deallocated at some point before you use it. It's actually pure luck that string1 is OK. You should change your code to be something like this:
- (void) methodOne: (NSString *) passedString
{
NSLog(#"%#", passedString);
NSString* oldString = string1;
string1 = [passedString copy];
[oldString release];
}
- (void) methodTwo: (NSString *) passedString
{
NSLog(#"%#", passedString);
NSString* oldString = string2;
string2 = [passedString copy];
[oldString release];
}
and release in dealloc
-(void) dealloc
{
[string1 release];
[string2 release];
// other stuff
[super dealloc];
}
I strongly recommedn you create properties for string1 and string2 to handle all that reference counting stuff:
#interface MyClass : NSObject
{
NSString *string1;
NSString *string2;
}
- (void) methodOne: (NSString *) passedString;
- (void) methodTwo: (NSString *) passedString;
#property (copy) NSString* string1;
#property (copy) NSString* string2;
#end
#imlementation MyClasss
#synthesize string1, string2;
- (void) methodOne: (NSString *) passedString
{
NSLog(#"%#", passedString);
[self setString1: passedString];
}
- (void) methodTwo: (NSString *) passedString
{
NSLog(#"%#", passedString);
[self setString2: passedString];
}
// dealloc as before
#end
You are making memory management errors. You have to retain or copy the strings when you assign them to your ivars (and release them later).
It is possible that you can still access an object even though it has been deallocated when the memory it occupied has not been overwritten yet. But you cannot rely on it.
If the passed strings are autoreleased there is no retain when they are assigned. Constant strings (#str") essentially are never released, created strings such as by stringWithFormat need to be retained.
Please show the callers.
Using #properties with retain will eliminate many retain issues. Or consider using ARC, that eliminates the need for retain/release/autorelease.
As others have stated in this thread you have some memory management issues here, perhaps you aren't quite understanding the way that NSObjects get allocated and retained, you should read up on Objective-C memory management. In the mean time there are two approaches you could take to solve your above issue.
You could have your NSString member variables (string1 & string2) kept as properties of your class, aside from some other functionality declaring these as properties would give them setter and getter accessors, that you would call instead of method1 and method2. So this would change your code to look like the following in your header file
#interface MyClass : NSObject
{
NSString *string1;
NSString *string2;
}
#property( nonatomic, retain)NSString* string1;
#property( nonatomic, retain)NSString* string1;
Then remember to add the following to your source file (typically at the top of the file after the #implementation MyClass line)
#implementation MyClass
#synthesize string1;
#synthesize string2;
Then in the class where you were calling method1 and method2 from you can change the code to look like
//Lets assume somewhere you've called an init Method for your MyClass Object, something like
MyClass* myClassObject = [[MyClass alloc] init];
//you can then call the setters to set the string like so
[myClassObject setString1:#"some string"]; //or myClassObject.string1 = #"some string";
[myClassObject setString2:#"some string"]; //or myClassObject.string2 = #some other string";
//Calling these member variables either of the above ways is valid, I prefer the former as it's a bit easier on the eye for me
//and to get the values back out of the strings you could call
NSString* output = [myClassObject string1];
//or
NSString* output2 = myClassObject.string2;
Now you may for some reason not want to use an #property for the NSString member variables, so you could modify your original source (.m) file to look like
#implementation MyClass
- (void) methodOne: (NSString *) passedString
{
NSLog(#"%#", passedString);
if( string1 != nil )
{
[string1 release];
}
string1 = [[NSString alloc] initWithString:passedString];
}
- (void) methodTwo: (NSString *) passedString
{
NSLog(#"%#", passedString);
if( string2 != nil )
{
[string2 release];
}
string2 = [[NSString alloc] initWithString:passedString];
}
That should solve the issue of why your strings aren't valid, as you wont be overwriting the memory and trying to read back garbage this way. You will have to remember to release these NSStrings in your dealloc if they are not nil, otherwise you'll get a memory leak there too.
Hope this helps.

When declare a property(retain) in objective c, what's the default implementation of set/get method

I am a newer to objective c. I have read the memory management document on apple's "Memory Management Rules". But I am still not very clear about how to manage reference for a property.
What's the default implementation of set/get access methods for a property declared with "retain" annotation?
This is my assuming, please give some comments. Thanks.
#interface SubClass : NSObject {
NSString * _name;
}
... ...
#property (nonatomic, retain) NSString * name;
... ...
#end
-(NSString *) setName {
return _name;
}
-(void) setName: (NSString *) pName{
// the correct version of default set method for retain
if( _name != pName ) {
[_name release];
_name = [pName retain];
}
}
So the dealloc method, is this ok?
- (void)dealloc {
self.name = nil; // or [_name release], _name = nil;
}
As Matteo Alessani says, you can simply synthesize the property to get the default implementations.
For reference, this is what's generated (I got this from reading Objective-C Declared Properties and piecing information together):
- (NSString *)name {
return _name;
}
- (void)setName:(NSString *)aName {
if (_name != aName) {
[_name release];
_name = [aName retain];
}
}
You can use synthesize in your implementation file:
#implementation SubClass
#synthesize name = _name;
#end
Automatically you get the default getter and setter.
As Matteo said you can synthesize accessor methods automatically.
But speaking about implementation details:
yes getter method may look like (but note the correct name):
-(NSString *) name {
return _name;
}
or to handle the case when you use name value and object that holds it gets deallocated:
-(NSString *) name {
return [[_name retain] autorelease];
}
setter method:
-(void) setName: (NSString *) pName{
NSString *temp = [pName retain];
[_name release];
_name = temp;
}
dealloc. Your implementation is ok, but as calling properties may cause some side effects relying that all fields in your object are valid (which may not be so in dealloc method) it may be better to release ivar directly.
Your setter example is wrong as you don't need to handle nil case (actually in your code you can never set name value to nil), but you need to handle the case when property is set to the same object.

#dynamic properties and its usage?

Can anyone give me clear picture about dynamic property and its usage? y not use the usual #property everywhere?
Dynamic properties are used when you don't provide an implementation at compile time, but ensure that one exists at runtime. Being a dynamic language, Objective-C can respond to messages at runtime, even if the class doesn't have an implementation at compile time.
Here's a contrived example: Let's say you have a Book class, backed by an NSMutableDictionary that contains the keys title and author. However, you want Book to respond to title and author as well, and have them as properties; title and author will grab the appropriate value from the dictionary, and setTitle: and setAuthor: will change the value stored in the dictionary. You could do so with this code:
#import <Foundation/Foundation.h>
#interface Book : NSObject
{
NSMutableDictionary *data;
}
#property (retain) NSString *title;
#property (retain) NSString *author;
#end
#implementation Book
#dynamic title, author;
- (id)init
{
if ((self = [super init])) {
data = [[NSMutableDictionary alloc] init];
[data setObject:#"Tom Sawyer" forKey:#"title"];
[data setObject:#"Mark Twain" forKey:#"author"];
}
return self;
}
- (void)dealloc
{
[data release];
[super dealloc];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSString *sel = NSStringFromSelector(selector);
if ([sel rangeOfString:#"set"].location == 0) {
return [NSMethodSignature signatureWithObjCTypes:"v#:#"];
} else {
return [NSMethodSignature signatureWithObjCTypes:"##:"];
}
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
NSString *key = NSStringFromSelector([invocation selector]);
if ([key rangeOfString:#"set"].location == 0) {
key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];
NSString *obj;
[invocation getArgument:&obj atIndex:2];
[data setObject:obj forKey:key];
} else {
NSString *obj = [data objectForKey:key];
[invocation setReturnValue:&obj];
}
}
#end
int main(int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Book *book = [[Book alloc] init];
printf("%s is written by %s\n", [book.title UTF8String], [book.author UTF8String]);
book.title = #"1984";
book.author = #"George Orwell";
printf("%s is written by %s\n", [book.title UTF8String], [book.author UTF8String]);
[book release];
[pool release];
return 0;
}
Note the the methods are "created" at runtime via forwardInvocation:; hence, title and author are dynamic properties.
(This isn't the best example, but I think it gets the point across.)
#dynamic thing; is merely a way to inform the system not to generate getters/setters for the thing, that you (or someone else) will provide them for you—As in, they'll be there at runtime.
This is in contrast to #synthesize which tells the compiler to generate the getter/setter (as appropriate) for you.
#dynamic is (in my experience) used primarily in conjunction with Core Data and subclasses of NSManagedObject. To quote Marcus Zarra's Core Data,
By declaring them
[attributes/relationships], we are
telling the compiler to ignore any
warnings associated with there
properties because we "promise" to
generate them at runtime. Naturally,
if the turn up missing at runtime,
then our application is going to
crash.

Javascript style objects in Objective-C

Background: I use a ton of NSDictionary objects in my iPhone and iPad code. I'm sick of the verbose way of getting/setting keys to these state dictionaries.
So a little bit of an experiment: I just created a class I call Remap.
Remap will take any arbitrary set[VariableName]:(NSObject *) obj selector and forward that message to a function that will insert obj into an internal NSMutableDictionary under the key [vairableName].
Remap will also take any (zero argument) arbitrary [variableName] selector and return the NSObject mapped in the NSMutableDictionary under the key [variableName].
e.g.
Remap * remap = [[Remap alloc] init];
NSNumber * testNumber = [NSNumber numberWithInt:46];
[remap setTestNumber:testNumber];
testNumber = [remap testNumber];
[remap setTestString:#"test string"];
NSString * testString = [remap testString];
NSMutableDictionary * testDict = [NSMutableDictionary dictionaryWithObject:testNumber forKey:#"testNumber"];
[remap setTestDict:testDict];
testDict = [remap testDict];
where none of the properties testNumber, testString, or testDict are actually defined in Remap.
The crazy thing? It works... My only question is how can I disable the "may not respond to " warnings for JUST accesses to Remap?
P.S. : I'll probably end up scrapping this and going with macros since message forwarding is quite inefficient... but aside from that does anyone see other problems with Remap?
Here's Remap's .m for those who are curious:
#import "Remap.h"
#interface Remap ()
#property (nonatomic, retain) NSMutableDictionary * _data;
#end
#implementation Remap
#synthesize _data;
- (void) dealloc
{
relnil(_data);
[super dealloc];
}
- (id) init
{
self = [super init];
if (self != nil) {
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
[self set_data:dict];
relnil(dict);
}
return self;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSString * selectorName = [NSString stringWithUTF8String: sel_getName([anInvocation selector])];
NSRange range = [selectorName rangeOfString:#"set"];
NSInteger numArguments = [[anInvocation methodSignature] numberOfArguments];
if (range.location == 0 && numArguments == 4)
{
//setter
[anInvocation setSelector:#selector(setData:withKey:)];
[anInvocation setArgument:&selectorName atIndex:3];
[anInvocation invokeWithTarget:self];
}
else if (numArguments == 3)
{
[anInvocation setSelector:#selector(getDataWithKey:)];
[anInvocation setArgument:&selectorName atIndex:2];
[anInvocation invokeWithTarget:self];
}
}
- (NSMethodSignature *) methodSignatureForSelector:(SEL) aSelector
{
NSString * selectorName = [NSString stringWithUTF8String: sel_getName(aSelector)];
NSMethodSignature * sig = [super methodSignatureForSelector:aSelector];
if (sig == nil)
{
NSRange range = [selectorName rangeOfString:#"set"];
if (range.location == 0)
{
sig = [self methodSignatureForSelector:#selector(setData:withKey:)];
}
else
{
sig = [self methodSignatureForSelector:#selector(getDataWithKey:)];
}
}
return sig;
}
- (NSObject *) getDataWithKey: (NSString *) key
{
NSObject * returnValue = [[self _data] objectForKey:key];
return returnValue;
}
- (void) setData: (NSObject *) data withKey:(NSString *)key
{
if (key && [key length] >= 5 && data)
{
NSRange range;
range.length = 1;
range.location = 3;
NSString * firstChar = [key substringWithRange:range];
firstChar = [firstChar lowercaseString];
range.length = [key length] - 5; // the 4 we have processed plus the training :
range.location = 4;
NSString * adjustedKey = [NSString stringWithFormat:#"%#%#", firstChar, [key substringWithRange:range]];
[[self _data] setObject:data forKey:adjustedKey];
}
else
{
//assert?
}
}
#end
Cool class. I like it.
I can't think of a way to suppress all your warnings, but we can get it down to one line per property. Add this to your Remap.h:
#define RemapProperty(PROP) \
#interface Remap(PROP) \
#property (nonatomic, retain) id PROP; \
#end \
#implementation Remap(PROP) \
#dynamic PROP; \
#end
Now you can suppress all remap warnings for a given property by putting this at the top of the file that's giving you a warning:
RemapProperty(propertyName);
This requires some extra work, but it gives you dot-syntax as a reward.
remap.propertyName = #"Foo";
With a little more work, you could define a similar macro that adds properties directly to NSDictionary, thereby making the Remap class unnecessary.
If I'm reading your code right, I think a potential problem with this approach might be that you can't have key names such as hash (or other methods from NSObject, assuming your Remap inherits from NSObject). You will end up with the Remap instance's hash value rather than letting Remap look up a key called hash within _data, because [remap hash] will not invoke forwardIncovation:, as far as I can tell.
As an alternative for making dictionaries more like general purpose objects, what I have done is make categories of NSDictionary that wrap getters and setters for specific keys. Just use very descriptive names since you are filling a global namespace.
#interface NSDictionary (MyGetters)
#property (nonatomic,readonly) id something;
-(id) something;
#end
#interface NSDictionary (MyGetters)
-(id) something { return [self objectForKey:#"something"]; }
#end
#interface NSMutableDictionary (MySetters)
-(void) setSomething:(id)inValue;
#end
#interface NSDictionary (MySetters)
-(void) setSomething:(id)inValue { return [self setObject:inValue forKey:#"something"]; }
#end
The only problem with setter properties is that you must define the getter in both categories to avoid a warning.
You still have to declare everything, but you are going to have to do that anyway to get rid of warnings for Remap.