It seems that the default behavior in XCode is to silently allow redefinition of local variables if they are declared in a deeper scope, but throw an error or warning otherwise. For example, XCode produces an error for "Redefinition of 'var'" if it is redefined in the exact same scope:
- (void) doStuff
{
NSString *var = #"Hello World";
NSString *var = #"Goodbye"; // Error on this line
}
Similarly, if I have an ivar called 'var', and I try to re-declare 'var' in a local method, XCode will produce a warning for "Local declaration of 'var' hides instance variable" when I try to use it:
//MyClass.h
...
#interface MyClass : NSObject
{
NSString *var;
}
...
//MyClass.m
...
- (void) doStuff
{
NSString *var = #"Hello World";
NSLog(#"%#",var); // Warning thrown on this line
}
So far this is what I would expect. However, if var is redefined in a deeper scope, such as an if block or for loop, XCode allows it, and the outer declaration is silently ignored:
NSString *var = #"Hello World";
if (TRUE)
{
int var = 0;
NSLog(#"%d",var); //prints '0', No errors or warnings
}
NSLog(#"%#",var); //prints 'Hello World'
Why is the last example silently allowed, but the other two are caught? Is there some option or flag I can toggle in XCode so that an error or warning would also be created in the last example? If XCode won't catch it for me, is there some code I could write to make sure variables are never redefined? Or is it just my responsibility to make sure I'm not re-using my variable names?
In the build settings (Xcode 5 & 6, at least) you can set a warning for Hidden Local Variables to YES.
The last example is behavior that Objective-C inherits from standard C. A variable's scope is determined by the bracing level. It's been that way since the earliest days for C. It's called variable shadowing, and it's actually pretty useful in ensuring that code keeps working even in the face of API changes in system libraries.
As far as why it's allowed, but the earlier examples aren't, that's a consequence of how Objective-C implements instance variables. The instance variables are essentially treated as local variables of each of the class's methods. So when you declare a local variable in a function that shadows an instance variable, it gets flagged as an error. Basically the first and second cases are treated as equivalent.
To get a warning for these cases, set the LLVM warning option Hidden Local variables to Yes.
Related
I am a newbie Objetive C developer and I am trying to fill up a vector which it is a variable property... But It doesn't working, I can not understand why or how to fix it
-(void) setUpLabels:(NSString *) _labels_path {
// Read the label list
self.label_strings.clear();
std::vector<std::string> new_values;
std::ifstream t;
t.open([_labels_path UTF8String]);
std::string line;
while (t) {
std::getline(t, line);
new_values.push_back(line);
}
self.label_strings.reserve(new_values.size());
for (int index = 0; index < new_values.size(); index++) {
self.label_strings.push_back(new_values[index]);
}
NSLog(#"size=%lu",self.label_strings.size());
t.close();
}
This is the .h file
#interface AIClassifier: Classifier
#property std::vector<std::string> label_strings;
- (id) init;
- (id) initWithParams:(NSString *)model:(NSString *) labels;
#end
The function setUpLabels is not in the file..
All the time is printing size = 0. I tried more simple versions with the same result.
One problem with this code is that you use #property and self.label_strings. #property creates a getter method that returns a value of type std::vector<std::string>. This is a value, not a pointer/reference, so it will be a new copy of the underlying object. It means that each time you call self.label_strings.push_back(x) or any other method, it operates on a copy of the initial object (which is always empty).
To solve this use _label_strings instead of self.label_strings, or use an instance variable for label_strings instead of a #property:
#interface AIClassifier: Classifier {
std::vector<std::string> label_strings;
}
#end
(Note: better move this to be a private instance variable in your .mm file)
If there's no particular reason to use C++ for this task, there are several Objective-C ways to read a file line by line.
If the file is small, you can read it completely into memory and then split into separate lines like here: https://stackoverflow.com/a/4466934/1009546
In Objective-C we use NSArray<NSString*>* instead of std::vector<string>.
Another likely reason you don't get anything is that either the _labels_path file path is wrong or that it doesn't exist on device.
You can set a breakpoint in this function and use Xcode step by step debugger to see what actually is going on and what you have in _labels_path and new_values at the time of execution.
Normally you shouldn't normally have to use C++ in Objective-C, especially not in interfaces. If you make your file ".m", and not ".mm", it will not allow C++ inside (only Objective-C).
Note 1: if you stick with C++, you don't need a second loop. Just assigning self.label_strings = new_values; will copy it (this is C++ magic).
Note 2: always check the file after opening it. In Objective-C you normally have NSError* returned from functions. In C++ you can check the failbit (see open docs example).
Note 3: Ideally use RAII to make sure that the file is closed. In this case t.close(); is not needed, because ifstream destructor will close it (see http://www.cplusplus.com/reference/fstream/ifstream/close/).
I forgot to initialize a local variable, and I got no warning when I used it. Since I'm using ARC, the variable was initialized to nil, so no harm was done, but I still want a warning when I used an uninitialized value. If I disable ARC, I get the warning I expect.
NSString *foo;
NSString *baz;
if (bar) {
foo = #"fizz";
} else {
foo = #"buzz";
}
NSLog(#"foo: %#", foo); // foo: (fizz|buzz)
NSLog(#"baz: %#", baz); // baz: (null)
Without ARC:
/blah/blah/blah/Blah.m:14:18: note: initialize the variable 'foo' to silence this warning
NSString *foo;
^
--EDIT--
I've figured out how to make uninitialized values impossible using local blocks. This obviates the need for the warning.
With ARC, pointers to Objective C objects are automatically initialized to nil, so there is no "uninitialized value" which the compiler can warn about.
Clang has an option -Wuninitialized that looks like it should do what you want, but as pointed out in another answer, variables are guaranteed to be initialized to 0/nil under ARC.
Martin R is correct:
With ARC, pointers to Objective C objects are automatically
initialized to nil, so there is no "uninitialized value" which the
compiler can warn about.
However, I've avoided this problem altogether by using local blocks to initialize variables. The block guarantees that all paths end in a return, which means that my variable is guaranteed to be initialized.
The example would be written thusly:
NSString *foo = ^{
if (bar) {
return #"fizz";
} else {
return #"buzz";
}
}();
NSLog(#"foo: %#", foo); // foo: (fizz|buzz)
The block is stack-allocated, so it doesn't incur any overhead beyond a normal function call.
I'm just getting started on Objective-C and I came across this example on creating a singleton:
+ (BNRItemStore *) sharedStore
{
static BNRItemStore *sharedStore = nil;
if (!sharedStore)
sharedStore = [[super allocWithZone:nil] init];
return sharedStore;
}
I understand what's he's trying to do - which is to return the same instance if it's existing and create a new one if it's not. What bothers me is this line:
static BNRItemStore *sharedStore = nil;
Won't this line reset the sharedStore to a nil value everytime the method is called? I don't see how the method will be able to return the previously existing instance if this line always sets it to nil.
Thanks in advance.
This is an element which Objective-C inherits from standard C. Any variable with static storage duration (which the static type specifier explicitly declares) is only initialized once, and the c standard says that this happens before the program starts.
6.2.4 3) An object whose identifier is declared with external or internal linkage, or with the storage-class specifier static has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
Note that it also mentions that if the variable with static storage duration is of 'pointer type', then it is automatically set to a NULL pointer (which is what nil is), so if you want, you can omit the = nil part of the declaration if you think it improves the readability of your function.
Won't this line reset the sharedStore to a nil value everytime the method is called?
Because sharedStore is static, it will be initialized (the = nil bit) the first time it is called. Subsequent calls will skip these instructions.
I don't see how the method will be able to return the previously existing instance if this line always sets it to nil.
Because it is static the variable and its value will remain in memory after the method exits.
Basically, you can think of this as a global variable, but it is accessible only to +sharedStore.
I'm not sure how much use this question is but it seems interesting to me...
I thought that using property/synthesize statements was equivalent to me creating the getter/setter. Therefore
// .h
#property (nonatomic) BOOL on;
// .m
#synthesize on = _on;
// In my mind synthesizes the following methods
// - (BOOL)on;
// - (void)setOn:(BOOL)on;
However if I change the declarations to the following:
v
#property (nonatomic, getter=isOn) BOOL on;
#synthesize on = _on;
// In my mind synthesizes the following
// - (BOOL)isOn;
// - (void)setOn:(BOOL)on;
Then given the above I override the getter so I know when it is called:
- (BOOL)isOn;
{
NSLog(#"I was called");
return _on;
}
Now calling the following on an instance (myClass) results in:
NSLog(#"%d", [myClass isOn]);
//=> 2012-02-09 22:18:04.818 Untitled[1569:707] I was called
//=> 2012-02-09 22:18:04.820 Untitled[1569:707] 1
NSLog(#"%d", myClass.isOn);
//=> 2012-02-09 22:18:24.859 Untitled[1599:707] I was called
//=> 2012-02-09 22:18:24.861 Untitled[1599:707] 1
NSLog(#"%d", myClass.on); // This is the one I didn't expect to work
//=> 2012-02-09 22:18:55.568 Untitled[1629:707] I was called
//=> 2012-02-09 22:18:55.570 Untitled[1629:707] 1
I had always assumed that if I was using a property in this sense it was perfectly valid to use the getter/setter with dot syntax in the form
myClass.isOn;
myClass.on = on;
From another question it was suggested that when using dot syntax I should use the property name like this:
myClass.on // Correct
myClass.isOn // Incorrect
Although this works it seem slightly less logical because I know there is no underlying method - (BOOL)on it is instead mapped to - (BOOL)isOn
My questions are (using the latter example)
Is this a bug or should myClass.on really be silently changed to call - (BOOL)isOn
Semantically speaking I am accessing state not invoking behaviour so is my current use of dot syntax correct? (e.g. myClass.isOn)
Update
Although no one has explicitly said it I have reasoned that using .isOn is bad form because regardless of the fact that under the hood the same method is called, semantically isOn is asking a question, which is more behaviour rather than state.
However I am still unclear on where the "magic" wiring goes on that turns calls to myClass.on into [myClass isOn]
Update 2
After looking around the docs some more I found this section on Declared Properties. Using the following code I can inspect a class' properties:
id MyClass = objc_getClass("MyClass");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(MyClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSLog(#"Name: %s, attributes: %s\n", property_getName(property), property_getAttributes(property));
}
//=> 2012-02-10 07:10:28.333 Untitled[934:707] Name: on, attributes: Tc,GisOn,V_on
So we have the following attributes:
name = on
type = char (Tc)
getter = isOn (GisOn)
variable = _on (V_on)
With all of this information available at runtime it kind of leaves the question is this lookup done at runtime or compile time like some answers suggest?
However I am still unclear on where the "magic" wiring goes on that turns calls to myClass.on into [myClass isOn]
The logic surely goes as follows, when compiling an obj.name in a getting context:
if(there is an accessible #property for name in scope)
{
if(there is a custom getter specified)
compile "[obj customGetter]"
else
compile "[obj name]"
}
else if (there is an an accessible instance method name in scope)
compile "[obj name]"
else
{
compile "[obj name]"
warn obj may not respond to name
}
There are other ways a language/execution environment can handle custom getter names, but given that Obj-C puts the declaration in the header (which is public) the above is a good guess as to where the custom getter logic is performed - when compiling the call site.
From your experiment we can infer that dot syntax is interpreted as follows:
is there a property with this name? If so, does it have a specified getter / setter name? if so, let's call that method.
otherwise, make up an appropriate method name (direct if we're getting, setXX if we're setting) and throw that at the receiver.
You can, for example, try to use .count against an NSArray instance. Before the abomination police kick in your doors, you may have time to see that it works.
To actually answer your question, in my mind dot notation should only be used to access properties, and in that case you should use the property name as declared in the interface. So .on for a UISwitch. I don't know why the getter name isn't given in the synthesize statement instead of the property declaration, it seems to belong in implementation rather than interface to me.
Well concerning dot notation, let me cite Aaron Hillegass (Cocoa Programming for Mac OSX, 3rd. Ed):
"Overall, I think that this is a rather silly addition to the language since we already had a syntax for sending messages."
When you have a member variable on, and your getter for this variable is called isOn then .on and .isOn are two very different kind of things. By using the getter (and probably a setter, too) you will adhere to the "information hiding" promise, whereas by using direct access to the member variables you won't. Cocoa won't enforce those things as it is relying on conventions. It's up to you to decide which way is right for you. Considering convention, you would have to stick to setters and getters - no matter what names you give them, though.
Property declarations are merely shorthand for regular method declarations. E.g.:
#property int color;
#property (getter=isOn) BOOL on;
becomes these method declarations:
- (int)color;
- (void)setColor:(int)value;
- (BOOL)isOn;
- (void)setOn:(BOOL)on;
You can call these methods just like any other method:
[foo color];
[foo isOn];
Likewise, dot notation is merely informal shorthand for calling plain old methods. For example:
x = #"Hello".length;
x = foo.on;
x = foo.isOn;
becomes
x = [#"Hello" length];
x = [foo isOn];
x = [foo isOn];
Note that #"Hello".length works even though NSString does not actually declare a property named "length". By default, foo.bar always expands to [foo bar] unless bar has been declared a property with a custom getter. If bar happens to be the name of a valid method then it will work without error.
Likewise, in your example foo.isOn works even though you don't actually declare a property named "isOn". Rather "isOn" is the name of a method that just happens to be the getter method for your "on" property.
So, while foo.isOn may work, it's considered bad form because isOn is not actually the name of the property.
What you cannot do is this:
x = [foo on]; // Error
because you never declare an on method.
Just when I think I'm getting comfortable with Objective-c the mentioned symbols totally throw me down a rabbit hole...
** a double pointer??
& what sort of things can I do with &reference, is a #property? a full on object? weird pointer razzledazzle?
± I see both a + or a - before method declarations; I've seen Java annotate some datatype declarations by manually typing the + and the magic of compiling in Eclipse would change them to a -
I'm likely asking repetitious questions and/or way outta the ballpark on my guesses; thanks for answers/edits.
You're getting into the C portion that objective-c is built on top of.
** is a pointer to a pointer. Since functions in C take arguments by value, it means you can't change the value of the argument in that function. But, by providing a level of indirection and passing a pointer to the pointer, you can change the value.
& means it's a reference. If an argument takes a ** and you have a * variable, pass a reference to it.
Foo *foo;
[self changeFoo: &foo];
- (BOOL)changeFoo: (Foo **)foo
{
// dereference the double pointer and assign a val = alloc init returns a *
*foo = [[Foo alloc] init];
return YES;
}
A common usage in objective-c / cocoa is NSError. It's essentially an out argument.
NSError *err;
BOOL success = [self doSomething:#"Foo" error:&err];
- (BOOL)doSomething:(NSString*)withData error:(NSError**)error
{
}
As you might know, a pointer points to the address of an object and is the way you reference an object. A double pointer is sometimes used in Objective-C, mainly for returning NSErrors, where you want to get back the address, i.e. a pointer, to an error object (NSError) if an error occurred, thus you pass in a pointer assigned to null and the caller can change that pointer so that it points to the address of another pointer which in turn points to an NSError object.
The ampersand (&) is mostly used by the lower level C APIs, e.g. Core Graphics. They are used to reference things, like the current context. As long as most of your code uses square brackets around its method calls you won't see these very often.
Using a + or a - before a method declarations is used to differentiate between class (+) and instance (-) methods. A class methods is called on the class itself (such as alloc), while a instance method is called on an instance of that object (such as init).
- and + before a method declaration designate an instance method and a static class method. To use an instance method you have to create an object of your class before you can call its method, a static method can be called directly from a class type