Javascript style objects in Objective-C - iphone

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.

Related

How do I print the values of all declared properties of an NSObject?

I want to override my object's description method to be able to print the values of all my declared properties. I know that I can do this by appending all the values to each other, one by one, but sometimes this is time consuming if you have a lot of properties.
I was wondering if there's an easy way to do this, by getting help from the runtime powers of Objective-C?
Did you try this solution ?
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface NSObject (PropertyListing)
// aps suffix to avoid namespace collsion
// ...for Andrew Paul Sardone
- (NSDictionary *)properties_aps;
#end
#implementation NSObject (PropertyListing)
- (NSDictionary *)properties_aps {
NSMutableDictionary *props = [NSMutableDictionary dictionary];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSString *propertyName = [[[NSString alloc] initWithCString:property_getName(property)] autorelease];
id propertyValue = [self valueForKey:(NSString *)propertyName];
if (propertyValue) [props setObject:propertyValue forKey:propertyName];
}
free(properties);
return props;
}
#end

iPhone - writign a class that can be instanciated many times, each instance being able to access a shared property

I know this can be done with many languages, but I don't see how to do it using Objective-C. I've read about singletons but as they are designed to be instanciated only once, they do not feed this need.
So this class could be called like this :
MyClass* obj1 = [[MyClass alloc] initWithKey:#"oneKey"];
NSString* lib = obj1.lib;
or
int id = [MyClass idForKey:#"anotherKey"];
I've tried this code but I'm pretty sure it's really bad, but I don't see how to achieve this :
.h file
#interface MyClass : NSObject {
NSString* key;
}
#property(nonatomic, retain) NSString* key;
#property(nonatomic, readonly) int id;
#property(nonatomic, readonly) NSString* lib;
#property(nonatomic, readonly) int value;
+ (id) classWithKey:(NSString*)theKey;
#end
.m file
#import "MyClass.h"
#interface MyClass.h (Private)
-(id)initWithKey:(NSString*)theKey;
#end
#implementation MyClass
#synthesize key;
static NSMutableDictionary* vars = nil;
-(id)init
{
if (!(self = [super init])) return nil;
self.key = nil;
[MyClass initVars];
return self;
}
-(id)initWithKey:(NSString*)theKey
{
if (!(self = [super init])) return nil;
self.key = theKey;
[MyClass initVars];
return self;
}
+ (id) classWithKey:(NSString*) theKey
{
return [[[MyClass alloc] initWithKey:theKey] autorelease];
}
+(void)initVars
{
if (vars != nil) return;
#define mNum(x) [NSNumber numberWithInt:x]
#define k0 #"id"
#define k1 #"lib"
#define k2 #"val"
vars = [NSMutableDictionary dictionary];
[vars setObject:[NSDictionary dictionaryWithObjectsAndKeys:mNum(5), k0, #"One value", k1, mNum(0), k2, nil] forKey:#"oneKey"];
[vars setObject:[NSDictionary dictionaryWithObjectsAndKeys:mNum(8), k0, #"Another value", k1, mNum(1), k2, nil] forKey:#"anotherKey"];
...
[vars retain];
}
- (int)id { return [[[vars objectForKey:self.key] objectForKey:k0] intValue]; }
- (NSString*)lib { return [[vars objectForKey:self.key] objectForKey:k1]; }
- (int)value { return [[[vars objectForKey:self.key] objectForKey:k2] intValue]; }
-(void)dealloc
{
self.key = nil;
[vars release];
[super dealloc];
}
+(int) idForKey:(NSString*)theKey
{
if (vars == nil) [self initVars];
return [[[vars objectForKey: theKey] objectForKey:k0] intValue];
}
#end
take a look at singleton class concept
there are a lot of answer for singletons, just search
here's' one:
Is this really a singleton?

Memory management of container classes

I've made a container class to store a single tweet. Its initialized by passing in a dictionary object which is a single tweet.
I then store an array of these 'tweets' which I process through to display in a table.
The project is now finished and I am reviewing everything at the moment and I was wondering is there a better way to do this in the future. Is the memory handled correctly. I declare the string member vars with 'copy' and later in the dealloc I use a 'release' rather than just setting them to 'nil'.
Is my init ok or could that be improved?
Tweet.h
#import
#interface Tweet : NSObject
{
NSString * _userName;
NSString * _tweetText;
NSString * _tweetURL;
}
#property (nonatomic, copy) NSString * userName;
#property (nonatomic, copy) NSString * tweetText;
#property (nonatomic, copy) NSString * tweetURL;
- (id) initWithDict:(NSDictionary *)productsDictionary;
#end
Tweet.m
#implementation Tweet
#synthesize userName = _userName;
#synthesize tweetText = _tweetText;
#synthesize tweetURL = _tweetURL;
- (id) initWithDict:(NSDictionary *)productsDictionary
{
NSDictionary *aDict = [productsDictionary objectForKey:#"user"];
self.userName = [aDict objectForKey:#"screen_name"];
self.tweetText = [productsDictionary objectForKey:#"text"];
NSRange match;
match = [self.tweetText rangeOfString: #"http://"];
if (match.location != NSNotFound)
{
NSString *substring = [self.tweetText substringFromIndex:match.location];
NSRange match2 = [substring rangeOfString: #" "];
if (match2.location == NSNotFound)
{
self.tweetURL = substring;
}
else
{
self.tweetURL = [substring substringToIndex:match2.location];
}
}
else
{
self.tweetURL = nil;
}
return self;
}
-(void) dealloc
{
[self.tweetText release];
[self.tweetURL release];
[self.userName release];
[super dealloc];
}
#end
Many Thanks,
Code
At first sight, I see no inherent flaws here. That looks fine. I would prefer to do:
-(void) dealloc
{
[_tweetText release];
[_tweetURL release];
[_userName release];
[super dealloc];
}
But what you do is good as well.

#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.

Simple calculator app crashes when a third number key is punched

I am a newbie to the iphone app world. So I thought I try my luck with a calculator app.
Unfortunately I am running into an issue where if I press a third key in the calculator the app crashes. Sometimes I get this error EXC_BAD_ACCESS. Here is a code in my CalculatorViewController.m file.
#import "CalculatorViewController.h"
#implementation CalculatorViewController
#synthesize screenText;
- (IBAction)buttonPressed:(id)sender {
NSString *title = [sender titleForState:UIControlStateNormal];
[self collect:title];
}
- (void)collect:(NSString *)digitz {
NSString * newText = nil;
if ([digitz isEqualToString:#"+"]) {
[self add:result];
big_digit = nil;
}
else if ([digitz isEqualToString:#"+"]) {
[self sub:result];
}
else if ([digitz isEqualToString:#"x"]) {
[self multiply:result];
}
else if ([digitz isEqualToString:#"="]) {
[self equate:result];
}
else {
if (big_digit != nil && [big_digit isEqualToString:#"0"] == FALSE)
big_digit = [big_digit stringByAppendingFormat:#"%#",digitz];
else
big_digit = (NSMutableString *) digitz;
result = (int) big_digit;
newText = [[NSString alloc] initWithFormat:
#"%#",big_digit];
}
screenText.text = newText;
[newText release];
}
- (void)add:(int)res {
NSString * newText = nil;
ans = ans + res;
newText = [[NSString alloc] initWithFormat:
#"%#",ans];
screenText.text = newText;
[newText release];
}
Can anyone spot an obvious issue here. Here is the respective header file too.
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController {
UILabel *screenText;
int number;
int result;
int ans;
//NSString *big_digit;
NSMutableString * big_digit ;
}
#property (nonatomic, retain) IBOutlet UILabel *screenText;
- (IBAction)buttonPressed:(id)sender;
- (void)collect:(NSString *)digitz;
- (void)add:(int)num;
- (void)sub:(int)num;
- (void)multiply:(int)num;
- (void)equate:(int)num;
#end
Well, you probably don't want to just cast a string to an integer (ala (int)big_digit). Instead you want to use [big_digit integerValue];
I think what is happening is that your big_digit property is not retained. In this line, you just assign a string to it that is autoreleased:
big_digit = [big_digit stringByAppendingFormat:#"%#",digitz];
On the next pass through, big_digit is != nil, but [big_digit isEqualToString:#"0"] == FALSE fails because big_digit now points to an invalid memory location.
What you want to do is make big_digit a property in your interface, like so...
#property (nonatomic, retain) NSMutableString *big_digit;
I know reading docs sucks, but looking at your code I think you would really find reading through this useful. Memory management in objective c is quite a bit different from regular old C.
http://developer.apple.com/iphone/library/documentation/cocoa/conceptual/memorymgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW1
In your format strings, you're doing this:
newText = [[NSString alloc] initWithFormat:#"%#", ans];
But according to your #interface, ans is an integer. So that line should read:
newText = [[NSString alloc] initWithFormat:#"%d", ans];
since %d is the format specifier for an integer.