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.
Related
I am trying to adopt a SDK written in objective-c in a swift project. The objective-c way to initialize the SDK is as follows:
#implementation ViewController
nokeSDK *nokelock;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//INITIALIZE NOKE SDK
nokelock = [[nokeSDK alloc] init];
nokelock->cmd = [[nokecommand alloc] init];
I don't believe there is an equivalent to the arrow operator in swift, so is it possible to still initialize? I can't seem to find any info about his particular subject.
In Objective-C, a property is merely syntactic sugar for accessor methods; if, as is usually the case, these are a front for an instance variable, you can, with the right privacy access, bypass the accessor methods (or not have them in the first place) and get/set the instance variable directly. That's what your code is doing.
But Swift doesn't draw that distinction. A variable declared at the top level of a type declaration, in Swift, is a property; behind the scenes, a stored property has accessor methods, and you are passing through those automatically when you get/set the property. There is no separate instance variable to get/set.
To see what I mean, make a hybrid Swift / Objective-C app with class Thing whose interface looks like this:
#interface Thing : NSObject {
#public
NSString* s;
}
Now create the generated interface. You will see that, in Swift, s is nowhere to be seen.
Presumably, therefore, to rewrite your code, you'd need to turn cmd into a property, or at least provide a setter method. Otherwise, Swift will never be able to set the Objective-C cmd instance variable. Of course, if it is also up to you to rewrite nokeSDK in Swift, your problems are over, as you can now do whatever you like.
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.
I run into a fairly common scenario in Objective-C where I pass in a variable to an init method and then want to assign it to an instance variable of the same name. However I have not found a way to scope the variables to clarify which is the value from the message argument and which is the instance variable.
Say I have some class such as the following:
#interface MyObject
{
NSString *value;
}
- (id)initWithValue:(NSString *)value;
#end
In my implementation I want my init method to look something like this:
- (id)initWithValue:(NSString *)value
{
self = [super init];
if(self) {
self.value = value; // This will not work with instance variables
}
}
I know of three solutions:
Create a property, which allows calling self.value
Rename my instance variable, such as _value
Rename my init argument variable, such as initValue or argValue
I am not pleased with any of these solutions. Adding a property either makes the property publicly available on the interface or, if I use an extension, hides it from inheritors. I also do not like having different names for the variables or using an underscore, which perhaps comes from developing in other languages such as Java and C#.
Is there a way to disambiguate instance variables from message arguments? If not, is there a coding guideline in Cocoa for how to solve this problem? I like following style guidelines when appropriate.
Update
After thinking about how to do this in C, I came up with the solution of self->value. This works, but it produces a compiler warning that the Local declaration of 'value' hides instance variable. So this is not a satisfactory solution either since I have a zero-warning goal.
For setters (and by extension initializers), I believe the convention is to prefix the parameter name with new:
- (void)setCrunk:(Crunk *)newCrunk;
- (id)initWithCrunk:(Crunk *)newCrunk;
In general, I think the most common form I've seen is to call the parameter theCrunk, but Apple seems to recommend aCrunk.
And changing the name to "inValue" is not a good idea? What you have here - your 'solution' is complex, especially with the accessors, etc of Obj-C 2. Since self.value and inValue are different things, they need different names.
Note that you can use
-(void)method1:(NSString*)value;
in the header
and
-(void)method1:(NSString*)inValue;
in the .m file.
If you use only 1 the compiler will give you a warning.
You can combine 1 & 2 by using :
#synthesize value = _value;
If you want to hide your variable from the inheritors you can declare a empty named category and declare your property there.
For 3 you can use aValue for your argument.
I have a console message that appears to be triggered by apparently unrelated events.
The message states:
snarfed from ivar layout: [propertyName] = [constantString]
Where [propertyName] is the name of a property to which I set the value of a string constant [constantString].
What causes this message and what does it means?
I also ran into this issue recently. I was able to fix my specific issue, but I don't think that is exactly what the questioners are running into, since my issue was only being exposed in VoiceOver mode. As such, I'll offer thoughts on what I think is generally occurring and then I'll speak to my specific issue.
As for the general issue, I think that the Apple Framework is deciding to look through all of the ivars of a particular class in order to extract some information that it wants, but that is not provided by other parts of the UI element. This seems a little bizarre to me, but that is what I encountered.
So, to continue with the general case, and in answer to the initial question. If you're like me, then your property name is probably the same as your ivar. Try explicitly defining a getter method for that property. Then, set a breakpoint within that getter if you will be returning a non-nil value. Look at the stacktrace and that should tell you which piece of the apple frameworks is deciding to loop through your ivar layout in order to get the information it wants. (If you're not using the the same name for your property and ivar, then just define a property and getter with the ivar name and do the same thing with the breakpoint.)
My specific case was for a Custom Table Cell (like one of the commenters). In that cell,I had a property that was the same name as its ivar. I also had an explicitly defined getter for that property. I also referenced that custom table cell from the Nib file. So, it looked something like this:
class CustomTableViewCell:UITableViewCell
{
NSString *s ;
}
#property(nonatomic,retain) NSString *s ;
and in the implementation:
#synthesize s ;
-(NSString *)s
{
if( !s )
return self.reuseIdentifer ;
return s ;
}
I put a breakpoint in the return self.reuseIdentifier line,and that showed me a stacktrace from the Accessibility functions. The stacktrace showed my method being called by an Apple internal method that was looping through all of my ivars looking for something to use as the accessibilityLabel for my table cell. The name of the selector is '_accessibilityRetrieveTableViewIvarsText'.
To make matter worse, in my case, this was not just a debugger issue, it was messing up my Accessibility interface by using the wrong thing as the accessibilityLabel.
I came up with 3 fixes for my specific problem:
1) I added a value for the accessibilityLabel for the table cell inside the Nib. This satisfied the Apple framework to the point where it did not go searching through my ivars. This was not the solution I went with, however, because I did not want a static accessibility label.
2) I subclassed my CustomTableViewCell with an empty implementation and interface, and I used that as my Table cell class inside the Nib. This solved the problem because the Apple Framework looped through that class's ivars, of which there were none, and there weren't any values to 'snarf'. I did not use that solution either, but it might be the best one because it keeps Apple's frameworks from inspecting my ivars.
3) The solution I decided on was to make my ivar private and to define the property with a different name. That seems to be the standard way that a lot of folks use properties. This is what it looks like:
class CustomTableViewCell:UITableViewCell
{
#private
NSString *_s ;
}
#property(nonatomic,retain) NSString *s ;
and in the implementation:
#synthesize s = _s ;
-(NSString *)s
{
if( !_s )
return self.reuseIdentifer ;
return _s ;
}
This fixed the problem because nil is returned when Apple inspects the ivar, and, thus, nothing is 'snarfed'. I'm still not sure whether this or #2 is more appropriate.
"snarfed from ivar" basically autofills your accessibilityLabel. If you do that yourself, the message goes away, as there is no more need for sneeking into your UITableViewCell.
For future reference. The message is logged by the accessibility framework, which apparently looks through an UIView ivars for strings.
If you have a custom subclass you can define the custom attributes as specified in the following link:
Accessibility Programming Guide
Alternatively you can make the view subclass not accessible:
- (BOOL)isAccessibilityElement
{
return NO;
}
However, note:
If your application contains a custom individual view with which users need to interact, you must make the view accessible.
What's the difference between referring to an instance variable in an objective-c class as this:
self.myIvar
and
myIvar
if it's been declared as a property in the header and synthesized?
If you refer to self.myVar, it will use the #property declared in your header file.
For example
#property(nonatomic, retain) Class *myClass;
If you have
myClass = [ [ Class alloc ] init .... ];
Retain Count will be 1
But if you use
self.myClass = [ [ Class alloc ] init .... ];
Retain Count will be 2 because of the retain property.
It's the same if you set setter || getter method in the #property.
What's the difference between referring to an instance variable in an objective-c class as this:
self.myIvar
and
myIvar
if it's been declared as a property in the header …
Simple: The former does not refer to an instance variable.
It refers to a property named myIvar. Likewise, the latter refers to an instance variable and not a property.
The property is, of course, misnamed, because a property and an instance variable do not necessarily have anything to do with each other, and indeed a property does not even need to be backed by an instance variable.
Attempting to access self.myIvar is exactly the same as sending self a getter message for the property. That is, these two statements:
foo = self.myIvar;
foo = [self myIvar];
are exactly the same.
Likewise, attempting to assign to self.myIvar is exactly the same as sending self a setter message. These two statements:
self.myIvar = foo;
[self setMyIvar:foo];
are exactly the same.
By comparison, referring to the instance variable myIvar (no self.):
foo = myIvar;
myIvar = foo;
is exactly that: accessing a variable; nothing more.
This means a lot.
The accessors, particularly the setter, tend to have side effects. For example, if the property is declared as retain, a synthesized setter for it will release the old value of the property and retain the new value. Likewise, if the property is declared as copy, a synthesized setter will release the old value and make a copy of the new one.
Since an assignment to a property:
self.myProperty = foo;
is an accessor message, that “assignment” will cause the old value to be released and the new value to be retained/copied.
An assignment to a variable:
myIvar = foo;
being nothing more than an assignment to a variable, will not do that. If you owned the old value of myIvar, you just leaked it, and if you don't already own the new value, you still don't own it, which means it will probably die while you're still holding onto it, leading to a crash later. (See the Memory Management Programming Guide.)
Despite the two looking similar, they are very, very different.
As a general rule, you should use your properties everywhere except init methods and the dealloc method, and directly access the instance variables (where you have instance variables) in those methods. (Again, accessors may have side effects; you're likely to not want those side effects in a half-initialized or half-deallocated object.)
… and synthesized?
That doesn't matter. #synthesize is just one of three ways of telling the compiler how the property's accessors are implemented:
#synthesize: Compiler, you implement them.
#dynamic: Don't worry about it, compiler; my superclass will dynamically supply the accessors at run time. (Most common in subclasses of NSManagedObject.)
- (Foo *) myProperty { … } / - (void) setMyProperty:(Foo *) newFoo { … }: Here are my implementations of the accessors.
Failing to do one or more of those things for a property will get you a warning from the compiler and probably some run-time exceptions, because you never actually stated an implementation for the accessors that (by declaring a #property) you declared the instances would have.
The difference is that ivar is just a variable pointing to a location in memory, whereas self.ivar calls the setter (in the case of self.ivar = x) and getter (for x = self.ivar) methods. IE, under the hood, the self.ivar in these statements gets translated into [self setIvar:value] and [self getIvar] respectively . These methods can then handle things like retain/release and any class-specific behaviour on your behalf, and in fact do so by referencing ivar directly. The #synthesize keyword automatically generates these getter and setter methods for you to cut down on boilerplate code.
So, ivar is a location in memory where your object can store something, and self.ivar wraps class methods around that location in memory to manage access to it. Note that when initializing an object it is usually preferable to set the ivars directly to avoid possible strange behaviour with not-quite-fully-formed objects.
Without the self. part you'll be accessing/assigning the actual data member of the class, without going through the getter/setter generated by #synthesize (or you can write your own getter/setter if you need something more fancy than the default behavior).
Note that in those custom accessors you'd pretty much have to omit the self. part to avoid endless recursion, e.g. if you have a string property called s, a setter could be (this is similar to what is generated when you do #synthesize, by the way):
-(void)setS:(NSString *)newVal
{
if(newVal == s) return;
[s release];
s = [newVal retain]; //if you use self.s here, setS will be called again
}
self.ivar
calls a property method that you can later change or add to, and that might do some memory management as well. For instance, you could make setting self.ivar also change ivar2, increment ivar3, bounds check ivar4, send a message to object5, release object6, play sound7, etc.
ivar
just reads or writes some number of bits in memory.