Objective-C Pointers > pointing to properties - iphone

I have an NSInteger property of a custom class called 'estimatedTime', now, in my UITableView class I'm trying to pass this property as a pointer to a UITableViewCell. I can't seem to get it to work! I've tried the following:
NSInteger *pointer = sharedManager.tempTask.&estimatedTime;
NSInteger *pointer = &sharedManager.tempTask.estimatedTime;
I get the errors: lvalue required as unary '&' operand
and: expected identifier before '&' token
Can you not pass a pointer to a property? Is the property not just it self pointing to the ivar in my custom class? I need it as a pointer type so I can edit the value when a UITextField is changed inside the UITableViewCell.
Thanks and hope it makes sense!

Properties aren't variables; they are just syntactic sugar for get/set-style methods. Consequently, you can't take the address of a property.

As Marcelo said, you can't do this using the property itself.
You would either have to:
Add a method to tempTask that returns a pointer to the estimatedTime iVar (NSInteger *pointer = sharedManager.tempTask.estimatedTimePointer)
Use a temporary NSInteger, taking its address for whatever calls you need, then copy the result into estimatedTime
Option 1 is probably a really bad idea, because it breaks object encapsulation.

For using numbers in pointers I would suggest using a NSNumber* rather than a NSInteger* (an NSInteger is really an int). For example, if sharedManager.tempTask.estimatedTime is an NSInteger, you could do:
NSNumber *pointer = [NSNumber numberWithInt:sharedManager.tempTask.estimatedTime];
Now, when you want to use the int value for the number do:
NSInteger i = [n intValue];
The NSNumber * is a obj-c object so the usual retain/release/autorelease mechanisms apply.

Actually when you say Object1.Propertyxy.Property1
It is actually called as a FUNCTION rather than a VARIABLE/VALUE at some memory.
In your case "tempTask" will act as a
function and "estimatedTime" as a
argument and the result would be the
RETURN of the function.
I know and completely agree that pointers are very favorable for increasing speed but in this case it is just useless as it would require storing that PROPERTY somewhere and thereafter referring to i, just a waste of time and memory, go for it only if you have to use that very specefic property a 100 times per run :D
Hope this helped, if it didn't just let me know, I'll be glad to help.

Related

CoreData Object typing won't work

Can someone explain to me why this doesn't work:
CoreDataClass *classObject = (CoreDataClass *)[some method that returns a dictionary with exact KVC pairs that match CoreDataClass];
NSString *myString = classObject.stringProperty;
But this does:
CoreDataClass *classObject = (CoreDataClass *)[some method that returns a dictionary with exact KVC pairs that match CoreDataClass];
NSString *myString = [classObject valueForKey:#"stringProperty"];
EDIT:
What's the easiest way to cast the dictionary as my NSManagedObjectClass CoreDataClass so I can access properties directly?
It doesn't work since KVC compliance is not at all what defines classes or makes them castable - the class hierarchy exists for a reason, and just ensuring adherence to certain methods doesn't magically make something an instance of a completely different class. Keep in mind that the dot-accessor syntax is just sugar for a method send, so these two are equivalent:
classObject.stringProperty
[classObject stringProperty]
...and the latter obviously isn't valid for instances of NSDictionary (i.e. [[NSDictionary class] instancesRespondToSelector:#selector(stringProperty)] is NO).
Your latter example works because of the very premise of your question: if something is KVC-compliant for the key stringProperty, and you ask it for a value for that key, then obviously you get something back. Furthermore, both NSDictionary and CoreDataClass respond to the selector -valueForKey:, so the message send actually works at runtime.
The best way to get the two across isn't a "cast" at all - it's a complete conversion, at the property level, of the data involved. You might consider creating a custom -initWith... method on CoreDataClass that lets you instantiate its properties from a dictionary, or finding a way to get your method to return an actual instance of CoreDataClass instead of an NSDictionary.
Note that this solution may differ from the "easiest" way to get the data across, which is effectively to keep doing what you're doing and use -valueForKey: (though preferably without the cast, which is misleading).
Casting objects only appears to work (in the sense that you won't get type-checking errors) because it's a hint to the compiler, but it doesn't actually change anything about what the pointer points to, so you are still pointing to an NSDictionary. This is because, at the end of the day, you are essentially casting a pointer to a pointer, but telling Xcode that you are allowed to send a different set of selectors to it.
For NSManagedObjects, creation from a dictionary depends on a few things, but the recommended way is to make a class method on your custom class which will use NSEntityDescription and you NSManagedObjectContext, and sets the properties from the dictionary to the object:
+(CoreDataClass *) coreDataObjectWithDictionary:(NSDictionary *) spec {
CoreDataClass *myInstance = [NSEntityDescription insertNewObjectForEntityForName: #"CoreDataClass" inManagedObjectContext: [myMOCProvider sharedMOC];
myInstance.someProp = [spec valueForKey:#"someProp"];
}

passing arguments by a button's tag in objective-c

I saw someone passing arguments by a button's tag as follow.
how could this work? and is it safe to do like this ?
NSCustomClass* cls = [[NSCustomClass alloc] init:#"",#"",#""];
[button setTag:(int) cls];
- (void)OnClick:(id)sender
{
NSCustomClass* cls = (NSCustomClass*)[sender tag];
// to do something with the "cls".
[cls release];
}
In fact,I didn't get weird results.
It works fine by passing arguments like 'NSString','NSArray' in this way.
Can you tell me why it is a bad idea?
Casting a custom class to an int is a very bad idea and you'll get weird results.
The tag is an integer property on all UI elements. It is declared as such on UIView:
#property(nonatomic) NSInteger tag;
You can assign any integer value to it, including any predefined constants:
#define Button1Constant 1
#define PushButtonConstant 2
// …Code
[button setTag:PushButtonConstant];
// …More code
if (sender.tag == PushButtonContent)
// Handle
In general you never want to abuse the frameworks. The tag is intended to store an integer and is used mainly to access a view with viewWithTag:, which can be useful in some cases if your view was built in Interface Builder but a referencing IBOutlet is inappropriate. Stuffing a pointer into an int can give unpredictable results, as others have indicated.
On top of that there's a memory management issue. Even if the pointer survives the cast you aren't retaining the object being pointed to. This in effect is a weak pointer but without any management by the run-time. Attempts to use the pointer will likely lead to the dreaded EXC_BAD_ACCESS. And who knows what ARC might do with this mess - blow up, I would expect.
To attach data to a button (which in and of itself sounds like a possible design flaw) you should either subclass or leverage the power of the run-time with objc_setAssociatedObject() and friends.
In general, casting from a pointer type to an integer type usually indicates a design flaw; there are very few legitimate reasons to do this, and I would strongly discourage doing it, especially in this case. It may work, but it's not guaranteed to work, simply because the tag property is not documented to be used this way. The more “hacks” that you put in your code, the harder it is to maintain.
Don't learn from the code where you saw this, instead stick to reputable tutorials and books.
As a side note, it is conventional in Objective-C to name all methods starting with a lowercase letter, so the method should be onClick: and not OnClick:. The exception to this rule is when the method starts with an acronym, for example -[NSString UTF8String].
You could subclass from UIButton and define a property by yourself, instead of using its tag property.

performSelector:withObject:, but not with an object

I want to do performSelector:withObject: but where the object is a CGFloat. So it's not actually an object. How can I do this?
the object I am performing the selector on is not mine, I can't modify it.
eg
[xyz performSelector:#selector(setOffset:) withObject:2];
(after posting I changed what I need to slightly to this:
[xyz performSelector:#selector(setOffset:) withObject:CGSizeMake(2,0)];
If you're trying to invoke an arbitrary selector against an object you don't have control over, you could use an NSInvocation to set up the selector, target, and arguments, and obtain the return value after the method has been executed.
Generally, though, there are simpler solutions.
try use IMP (A pointer to the start of a method implementation.)
SEL setOffsetSEL = #selector(setOffset:);
IMP setOffsetIMP = [XYZClass instanceMethodForSelector:setOffsetSEL];
setOffsetIMP(xyz, setOffsetSEL, 2);
You need an object to message. When I've needed to do something like this, I'll create a simple container class, shove the data in an instance and then perform a selector (often #selector(doIt:)) when needed.
If you can target 4.x, you can use blocks for this, too, typically.
(Without knowing more about what exactly you are trying to do, hard to get any more specific.)
object passed is a CGFloat. So it's not actually an object.
As you wrote immediately after, if you're passed an object, it can't be a CGFloat, as CGFloat is a typedef'ed primitive (float or double).
If you've been passed a number value as an object, likely you were passed an NSNumber somehow.
With zero context to your question, there's no way to be sure.
You can use:
[NSNumber numberWithFloat:(float)value]

NSNumber Warning when using setter but code seems to work OK - Passing argument 1 makes integer from pointer without a cast

I have an object Foo that has an NSNumber property which is declared like this:
#property (retain) NSNumber *siteID;
and #synthesized.
When I I do something like this:
Foo *myFoo = [[Foo alloc] init];
NSNumber *newNumber = [[NSNumber alloc] initWithInt:42];
myFoo.siteID = newNumber;
[newNumber release];
The assignment on line 3 that goes through the synthesized setter appears to work just fine- myFoo.siteID has the value I'm expecting I'm able to go about my business. However, I get a warning:
Passing argument 1 of 'setSiteID: makes integer from pointer without a cast'
I'm concerned I'm doing something wrong and approaching this assignment incorrectly even though everything appears to be functioning OK.
I understand that NSNumbers are immutable and I've read some other questions about not being able to assign the value. Apologies if there is an existing topic that covers this case or if I'm just missing something basic with the property declaration.
Thanks for any tips.
Really appreciate all the suggestions and followup questions. I feel like an idiot I had a typo like this where I was declaring with a different class than I was allocing with. Those classes were very similar.
oldFoo *myFoo = [[Foo alloc] init];
Typo was harder to spot with the actual very long class name - the perils of autocomplete not posting with the exact code in question and keeping old code around.
The old version of the class also had a siteID property. Things were were getting alloced with the new object and declared with the old. Again - feeling silly this morning but truly appreciate everybody's help as I tried to track this down. At least I have the good instinct to always heed warnings :).
Maybe the member that this property refers to has no '*' in the declaration?
EDIT:
Do you have setSiteID: method in ".m" file?
The error message tells you that the compiler thinks the parameter to the method -setSiteId: is an integer, not an NSNumber*. That probably means that it cannot see the declaration of the property at the point where it is using it. This, in turn, probably means you have omitted to import the header file for the Foo class.

How do I generically use KVC to update fields?

Say I have the following Objective-C class:
#interface Foo {
int someNumber;
NSString *someString;
}
and for reasons I won't get into here, I want to use KVC to update, in a generic fashion, the values for those variables:
[f setValue:object forKey:#"someNumber"];
or
[f setValue:object forKey:#"someString"];`
If object is a string and I'm updating the someNumber variable, it seems that I need to know to use an NSNumberFormatter to get an NSNumber and then Cocoa automatically converts that to an int inside setValue:forKey:.
Is there any way to avoid this custom code and have Cocoa infer the conversion to an int from a string, or do I need to catch this situation each time and handle it myself?
The following code should handle the conversion automatically, as long as the object parameter is a member of a class that implements integerValue.
Note that both NSNumber and NSString implement this method.
- (void)setValue:(id)object forKey:(NSString *)key
{
if ([key isEqual:#"someNumber"])
{
someNumber = [object integerValue];
}
//...
}
edit: (side note):
You can avoid writing the method yourself by using objective-c properties. The default implementation of setValue:forKey: will do the work for you, as long as you create properties for someNumber and someString.
You should do the conversion yourself with a number formatter, it gives you finer control than anything that the framework might consider to be appropriate. It is also, probably, not a good idea to use a single instance of an object to update the values for your ivars. More appropriately, you could perform your update based on the class of the object (providing you are not storing in an id by querying the runtime as to the class of the object by means of object_getClassName. More information is available in the Objective-C 2.0 Runtime Reference. But in general, you will likely find bugs in your code as a result of doing things that way.
I'm with wisequark on this one. I think of setValue:forKey: as equivalent to calling the setter directly. If you had a method:
- (void)setSomeValue:(NSInteger)aValue
You wouldn't expect to be able to pass in an NSString and hope the method can figure it out.
Put another way, a key value gives you access to a property, and that property has a type. When using KVC, numbers just get wrapped in an NSNumber object so that there's only one setValue:forKey: method. Therefore, I say it's always the calling code's responsibility to package up the string in an NSNumber object.
It's times like these I lament that Cocoa Touch doesn't support bindings. Bindings would allow you to add a value transformer that could handle the transformation from string to number automatically for you. Maybe in the next version!