why does not address of non-exist extern variable equal to null - objective-c

I found something very confusing about non-exists extern variable.
As the code below, obviously, compiler will generate an error cause there is not a int variable named iiii.
But, if the line code of "NSLog(#"%p", &iiii);" is deleted, no error will be generated. And the "not null" will be printed if I execute the output binary.
Why is this happen? I am hoping to get an error, no matter how I manipulate iiii.
Thanks for your help
IDE: Xcode 10.3 with clang
Target platform: iOS simulator 12.4
extern int iiii;
if(NULL != (&iiii)) {
NSLog(#"not null");
}
NSLog(#"%p", &iiii);

Building an application includes the distinct stages of compilation and linking.
It is the linking stage, which occurs after compilation, that global declarations and "extern" references to such declarations and linked together. If you look at the error you get when the NSLog() is in your code you will see it is a linker (coming from ld, the linker/loader) error.
During compilation the compiler assumes that any extern references will be resolved by the linker. So it assumes the iiii variable will exist, and as such it will have an address so it can optimise away the test.
The above assumes strong linking, when a compiler is informed that weak linking will be used then it cannot make the assumption that an external reference will be non-null. E.g. if the variable iiii was declared in a library/framework which was weakly dynamically linked to your application then the compiler could not assume the variable existed and the application would need to test somewhere that the weakly-linked dynamic library was present. However we're getting into another topic entirely... (ask a new question if you have a dynamic linking issue).
HTH

Related

Marshall object attribute generates compiler error

I'm working on a brigde class to work with a unmanaged c++ library. I've a problem with the following (reduced) example code:
ref class ManagedClass
{
private:
UnManagedClass* m_UnManaged;
String^ m_someString;
public:
UserAgent_Managed(String^ someString)
{
m_someString = someString;
// Compiler error
// Severity Code Description Project File Line Suppression State Error C2665 'msclr::interop::marshal_as': none of the 3 overloads could convert all the argument
// types
std::string unManagedString = msclr::interop::marshal_as<std::string>(m_someString);
// Following works
// std::string unManagedString = msclr::interop::marshal_as<std::string>(someString);
m_UnManaged = new UnManagedClass(unManagedString);
}
};
When I call std::string unManagedString = msclr::interop::marshal_as<std::string>(m_someString); with the object attribute m_someString, the compiler tells me that there is no matching marshal_as method signature. If I perform the same with the someStringparameter the compiler doesn't throw an error. What am I missing? Both m_someStringand someString have the type String^.
Thx
The marshal_as() function is not very friendly, it is missing an overload to allow this code to compile. You can hone down the problem by looking at the IntelliSense popup that shows which overloads are available. The one you are trying to use is the 4th:
std::string marshal_as<std::string, System::String^>(System::String^ const & _from_obj)
The devil is in &, an unmanaged reference. Yes, an unmanaged reference to a managed object reference, mind blown :) But perfectly legal in C++/CLI, at runtime this argument turns into a raw pointer to the object reference.
It would have compiled if the template offered a System::String^ % _from_obj overload. It doesn't. The distinction between % and & matters a lot in C++/CLI, % declares a managed reference. Called a "tracking reference" in the docs. One that the garbage collector knows about and can update when it compacts the GC heap. Otherwise semantically completely identical to an unmanaged reference.
That the GC cannot update a & reference is the hang-up here. The compiler outright forbids generating unmanaged pointers to members of a managed type, other than through pin_ptr<>. It is far too dangerous, the garbage collector can kick in any time, even while the marshal_as() function is executing. Triggered by, say, another thread that allocates objects. And move the ManagedClass object, invalidating any raw pointers to the object. Having the function continue to use the outdated pointer at runtime will make the function produce garbage and possibly corrupt the GC heap.
The someString object reference is very different, it is stored on the stack or a processor register and cannot change when a collection occurs. So no complaints from the compiler.
You already have a good workaround here, the constructor argument is good as-is. But usually you have to provide one explicitly and store the member value into a local variable. In other words, write something like this:
auto temp = this->m_someString; // Intentional temporary
auto str = marshal_as<std::string>(temp);

No warning when using nonnull?

With code like this:
- (nonnull NSString *)testing {
return nil;
}
Shouldn't I get a compiler warning? I get no warning all, which seems to make the entire nullability stuff seem useless?
Well, in my opinion it should produce a warning, but I couldn't figure out to get one either.
What might be helpful though, is using Product > Analyze to run the CLANG Static Analyzer. This should give the following hint:
Null is returned from a method that is expected to return a non-null value
Another thing worth mentioning is the setting CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION which is named Incorrect Uses of Nullable values in the Apple LLVM 7.1 - Warnings - All languages section of the Build Settings.
This setting will not produce a warning for incorrect return values, but it shows a warning when using the method with incorrect parameters (e.g. nil for nonnull parameters).
This answer refers to Xcode Version 7.3.1 (7D1014)
"nonnull" is mostly there to combine Objective-C and Swift. Swift will translate the type of the method to "NSString" and not "NSString?".

Xcode LLDB throws Parse error when printing out property

I'm facing big issues when trying to print out Objective C properties in many situations when I'm almost certainly sure it SHOULD work.
Consider the following setup:
The view controller's property has strictly set class (Card *), but still, LLDB outputs an parsing error where subproperty cannot be found on object of type id.
Having an object property defined on a view controller (see points 5 & 6):
stop at breakpoint inside the controller code (f.e. in -viewDidAppear: method)
try to print out the property itself with po _card (points 1 & 2)
try to print out its subproperty with po _card.offlineURL (points 3 & 4)
an LLDB parsing error occurs
Printing out via [_card offlineURL] prints out proper object description as LLDB sends a message to Card object with no class check.
Definition of object property on the controller declares non-id class, though (point 5).
I'm expecting LLDB to print out the property object's subproperty description, NSString containing URL string in this case, but this annoying LLDB error occurs instead.
This is just a single example from many. Sometimes it affects direct property printout, numbers printing etc. These issues are way more frequent since integration of Swift began, being worse with every new version of Xcode since 6.2, including the latest 7.2.
This happens in my Objective C project in many situations, though sometimes it works fine in different cases.
Do you know about any work-arounds or fixes for this issue? I've already filed a report on Apple Bug Reporter, but this will certainly take time for Apple to even notice.
The most likely problem given the info in your question is that we don't actually have debug information for _card.
The lldb command:
(lldb) image lookup -t Card
will tell you whether lldb had debug information for Card available to it. If this doesn't find anything, then maybe some part of your project isn't getting built with debug info. If the command does find some correct definition of Card, then it must be that the debug info for the _card ivar is not getting hooked up to this type correctly.
If there is a definition of Card, then the workaround:
(lldb) po ((Card *) _card).offLineURL
is available.
For future reference, there are two other bits of behavior that are probably complicating your attempt to figure out what is going on here:
1) (w.r.t. picture 3) The Xcode IDE uses its built-in indexer for auto completion in the debug window as well as the Source Code editor. But the debugger runs off debug information, since we need to be able to debug things that aren't built in Xcode. So the fact that auto-completion can find a name in an expression doesn't tell you anything about what lldb will do.
2) (w.r.t. picture 2) po force-casts the expression you are trying to "po" to an ObjC object (i.e. "id") and then calls its description method. The description method of an ObjC object that doesn't override description prints the type name and the address. So lldb didn't need to know the type of the _card ivar to get the output you saw. We only start to need types when the expression is more complex and involves accesses to ivars or properties of an ObjC object in the expression.
Note also, by default if you do:
(lldb) print _card
lldb will evaluate the expression "_card", find it resolves to a pointer of at least type id, then follow that pointer's isa pointer into the ObjC runtime to figure out what the dynamic type is. So it is likely to print Card * in this case.
But the expression parser hasn't yet been taught to resolve the dynamic type of sub-expressions within the expression parser in mid-parse. That would actually be quite a trick... So if it doesn't know the full type of _card, then:
(lldb) print _card.offlineURL
isn't going to work, since id does indeed not have a property of that name.
You can work around this by doing:
(lldb) print _card
$0 = (Card *) 0x12345678
Then:
(lldb) print $0.offlineURL
since the result variable captures the full dynamic type information, that will also be available in subsequent expressions.

Why do I need to cast before a method of an item of a NSArray can be called?

I am fairly new to Objective-C. Currently porting my own library from C#/Java to objective C.
I now run into a very strange problem for me.
I have a NSArray with several Note objects. I want to transpose on of these notes:
//Note.h
- (Note *) transpose: (int) semitones;
//Main
NSArray *notes = [get it from somewhere];
Note *transposedNote = [[notes objectAtIndex:0]transpose:1]; //Doesn't compile
Note *transposedNote = [(Note*)[notes objectAtIndex:0]transpose:1]//Does compile
Is this happening because there is already a transpose method available in the general libraries?
I thought due to the dynamic nature of objective-C at runtime it would be checked which class objectAtIndex returns and then sends the message to it?
It is my understanding that there is no runtime type checking for the assignment operator in Objective C. Since an array can contain a mixture of types, there is no way for the system to know what objectAtIndex returns.
How about
Note *transposedNote = [notes objectAtIndex:0]; // first line
[transposedNote transpose:1]; // second line
? Notice in the reference that objectAtIndex: returns an id, you will see it is pretty obvious:
In the code above, because id can fit into any object, the first line doesn't need to cast it into Note. In the second line I'm just calling a method on a Note so the compiler is happy.
In your code you are calling methods on the returned id object, so the compiler doesn't understand what you are trying to do. Just assign it to a Note reference and it will be fine.
Yes, the error is because there's already a transpose: method in AppKit. And you're also right that it normally doesn't cause an error when you have two unrelated classes implementing methods with the same name. The reason you get an error is because the two methods either return incompatible types or take incompatible types as arguments. In your particular case, you're seeing both problems:
-[NSResponder transpose:] takes an id and returns void
-[Note transpose:] takes an int and returns an id
These are totally incompatible types, and the compiler does need to know the types involved even if it doesn't know what exact method is going to be called.
It does compile unless you have -Werror set to treat warnings as errors.
It might produce a warning if the compiler doesn't already know about the selector or if the selector is declared in more than one class. In the former case, it should be necessary only to import the interface containing the selector. In the latter case, you'll need to do the cast to suppress the error.

Objective C: Initializing static variable with static method call

The Compiler claims an error saying: "initializer element is not constant", when I try to initialize a static variable inside a method with a call to a static method (with + in its definition).
Anyway I can tell him that this method always returns the same value. I know this is not the same as static method, but there seems to be no constant methods in Objective-C (other than macros which won't work here because I am calling UI_USER_INTERFACE_IDIOM() from inside the method).
There's actually another solution in addition to Yuji's. You can create a function and prefix it with a GCC attribute (also works in Clang and LLVM) that will cause it to be executed before main() is. I've used this approach several times, and it looks something like this:
static NSString *foo;
__attribute__((constructor)) initializeFoo() {
foo = ...;
}
When you actually use foo, it will already be initialized. This mean you don't have to check whether it's nil each time. (This is certainly a minor performance benefit, though multiplied by the number of times you use it, but it can also simplify one or more other regions of code. For example, if you reference the static variable in N different places, you might have to check for nil in all N or risk a crash. Often, people call a function or use a #define to handle initialization, and if that code is only actually used once, it can be a penalty worth removing.
You cannot do that in Objective-C.
There are two solutions:
Switch to Objective-C++. Change the file extension from .m to .mm.
Initialize it with nil, and check it when you first use it, as in:
static NSString*foo=nil;
if(!foo){
foo=[ ... ] ;
}