I'm sure this is something simple but I've been staring at it for a bit now and I think all I need is a fresh pair of eyes to look at it, and since my cat doesn't have a whole lot of experience with iPhone programming I've turned to you.
I am passing a variable of type float from a class (UIView) to another class (UIViewController) right before the variable is passed it's value is correct but when it reaches the function it loses it's value, it's shown to be 0 or -2 and I'm not sure why. Like I said I'm sure it's something simple but I just need a fresh pair of eyes to look at it
Code is below
//inside UIView
-(void)UpdateFloat
{
myFloat = myFloat + 0.01;
}
-(void)RemoveView
{
//Function Call
[viewController myFunction:myFloat];
}
//Function
-(void)myFunction:(float)myFloat
{
[myView removeFromSuperview];
[self.view addSubview:myOtherView];
[myOtherView anotherFunction:myFloat];
}
the float gets updated by a timer and when the UIView makes the function call the value of the float is correct (usually about 15.67)
any help would be appreciated
Thank you in advance,
BWC
If your float really is a float, I bet there is something else is going on somewhere else that's manipulating that value, it's not the message send. Search for every occurrence of that variable name.
You'll see stuff like this happen when you accidentally do integer math on floats (which is really easy to do). Check and make sure your floats are floats and all the math is being done in floats and no int math is happening.
It's also possible that it is a scope related thing and you are instead getting a different myFloat, hard to tell without all the source available.
Well, I don't understand you, because you say you pass from UIView to UIViewController. But in your code, you first call an function in the UIViewController, which have to be [self myFunction:myFloat] by the way, and in that function you call your UIView.
Ok Guys,
I think I've figured out why the float isn't retaining it's value but the reason also baffles me.
when I'm calling the ViewControllers function in the UIView Xcode is showing the warning that there is no method and that methods without a matching signature will assume to return(id) and pass '...' as arguments
I understand what this warning means but what I don't understand is that I have the method declared in the header of my viewController
-(void)myFunction:(float)myFloat;
I also have another view that calls functions to the view controller and it's not displaying that warning...I'm pretty sure this is the reason the float isn't retaining it's value but I don't know why it can't see the method signature
Related
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.
OK, I have a custom object (an NSManagedObject subclass, if it matters) and I want to pass a pointer to one of its iVars to a function that I've set up to modify such values. With a normal pointer you'd just prefix it with an ampersand (&) as in the classic NSError &error example, but that can't be done with dot notation. I can't just pass &object.iVar as I'd hoped. Can anyone suggest a simple and elegant way to obtain the pointer of iVar so that I can pass it? I am loath to pass the entire object for reasons of code structure and neatness.
-Ash
Argh, as is almost always the case, I ask a question after an hour of frustrating puzzling then ten minutes later answer it myself. I don't know, maybe asking questions is some kind of therapeutic trigger for answers... shame this isn't a psychology website.
Anyway, my solution was to add a new 'pseudo-getter' method to the object I'm trying to access the pointer from that looks a bit like this:
- (Pointer **)getIVarPointer
{
return &iVar;
}
It's a bit cludgy, but since I only have that one iVar whose pointer I need to obtain it's not too bad. On ther other hand if there is a simpler, more 'official' way of doing this, I'd love to know it!
I have a custom class Custom.mm in which I am trying to set a float value using a setter in my controller class MainController. The Custom instance is typed as an id because it is an Obj-C++ file and pointing to a proper class at compile time has worked well for me. Everything works fine, the instance is verified. If I set up the amount variable as type int and pass ints it works fine. Same with any other value or object--except floats. For some reason floats (float, CGFloat and the like) are set to 0 in the Custom.mm class. This is not an issue with NSLog or anything--I've checked the amount variable with a breakpoint and everything works but floats.
//Custom.h
#interface Custom : UIView
{
CGFloat amount;
}
#property CGFloat amount;
#end
//Custom.mm
#implementation Custom
#synthesize amount;
- (id) initWithCoder:(NSCoder*)coder
{
if ((self = [super initWithCoder:coder]))
{
//set initial value to 1
self.amount = 1.0; //verified as 1.0
}
return self;
}
//MainController.h
#interface MainController : UIViewController
{
IBOutlet id customInstance; //IB points to the CustomView class
}
//MainController.m
#implementation MainController
-(void)viewDidLoad
{
[super viewDidLoad];
//Checking this value in Custom.mm via the debugger shows it as being 0,
//when before it was 1.0.
[customInstance setAmount:2.0];
}
#end
I was able to reproduce this on my own; you've run into an interesting phenomenon.
Your problem is that the compiler can't see a definition for the setAmount: method. As a result, it doesn't know the correct type of the arguments that method is expecting. In such cases, the compiler assumes that all parameters are of type '...', and return values are of type 'id'. But you're passing a CGFloat, not a '...'.
Objective-C is a dynamic language, so even when the compiler doesn't know whether the target method exists, it will happily package up the parameters and try to call it anyway. However, on most architectures the method for passing parameters is dependent on the type of the parameters. Integers and pointer parameters are usually passed in one set of registers, while floating point parameters are passed in another, and structs are usually passed directly on the stack. (Precise details depend on which architecture you're running on.) Since the compiler can't see the definition for the setAmount: method, it assumes that the parameters are of type .... Depending on the architecture, those may be passed in a different set of registers, or even on the stack.
When the setAmount: method runs, however, it is expecting the passed-in parameters to be in a certain set of registers. Those weren't populated by the caller, of course, and are thus still set to 0. The caller put the new value in one location, but the receiver looked in another. No wonder things are going wrong.
The solution to this is simple: Add #import "Custom.h" at the top of MainController.m. Then, the compiler will be able to see the definition of setAmount: when it's compiling MainController, and will thus know to put the new values in the place that the receiver is expecting them.
Incidentally, I'll bet that when you were compiling, you were getting a warning; something like
warning: no '-setAmount:' method found
warning: (Messages without a matching method signature
warning: will be assumed to return 'id' and accept
warning: '...' as arguments.)
At least, that's the warning I'm getting. That was the compiler telling you that it didn't know where to put the parameters when making that call, so it just picked something and hoped it worked. In this case, it didn't.
As for ints and other types working correctly, the compiler's guess at the parameter passing style just happened to match what the receiver was expecting. It was a simple matter of luck.
It looks like you haven't assigned a Custom object to the MainController's customInstance variable, so it's nil. Ask nil for anything and it will give you 0.
I want to populate a scrollView with quite a few different UI elements.
Therefore I thought I would write a method that remembers the current Position in the scrollView and just adds the element to the scrollView at the current Position.
Something like:
- (void)addUIElement:(id)element withWidth:(CGFloat)width andHeight:(CGFloat)height andYGap:(CGFloat)YGap {
element.frame = CGRectMake(currentScrollPos.x, (currentScrollPos.y + YGap), width, height);
[scrolly addSubview:element];
//And then set the current scroll position here
}
Unfortunately when I try to do access element.frame = ..., I get request for member in something not a structure or union. When I try to do [element frame] = ... Lvalue required as left operand of assignment.
Now, first of all I am not sure what's the best way to dynamically add objects to a scrollview. Maybe anyone has a better or easier approach.
Then on the other hand, I don't get why the above does not work?! Would I have to cast my element to the actual class? I thought I would not have to do so... Also then my method would not make that much sense anymore. Or at least would require some more steps...
This should work I think:
[element setFrame:...];
However if you work with different UI elements in your method may be you can make your elements parameter UIView* instead of id? This way your code will work for all UIView subclasses (which is what you actually need I suppose)
The difference is that "id" doesn't have any kind of reference to a frame. It could be anything. You want to instead do (UIView *)element in the method declaration, or alternatively in the call to element.frame, you would do ((UIView *)element).frame.
(And yeah, all things that you put on the screen are inheriting from UIView -- UIButton, UIImageView, etc.)
I have a view controller that is created by the app delegate - it's the first one shown in the app.
In its interface I declare
float lengthOfTime;
I also set it as a property:
#property (nonatomic) float lengthOfTime;
And in it's implemetation:
#synthesize lengthOfTime;
In the class viewDidLoad method, I set the value:
self.lengthOfTime = 3.0f;
However, after this, the value is always zero.
No errors, no compile warnings, nothing. Just zero.
The class is instantiated, it is showing in the view, so I'm pretty sure it's not a nil reference.
I've searched all over Google and can't figure it out.
What's going on?!?
:(
The property should be atomic I think. Just declare it as:
#property float lengthOfTime;
Updating primitives is an atomic operation.
I'm not sure if that will solve your problem or not.
The "non-atomic" keyword is for protection from multi-threading issues where one thread gets interrupted in the middle of changing it. You can only interrupt an operation if it takes more than one instruction to perform. Updating a single floating-point value is a one-instruction operation, and therefore cannot be interrupted.
It is possible that your view is reading the value before -viewDidLoad is called. I would try setting the variable in a method that is called before the view is initialized such as -awakeFromNib.
I fixed it and don't know how I did it. That's the worst. Now I don't know how to solve it if it happens again.
I thought it wasn't being initialized, but I removed the line and it still works.
I think I'm even more frustrated than before.