Search code examples
iosobjective-cxcodeobjective-c-category

No warning in Xcode on header file including itself


I included in my iOS project a widely used UIImage category to handle PDF, and got some compilation errors. This raised several questions.

Q1: Why would Xcode (or actually the LLVM compiler) not give any warning on a header file that is including itself ? I don't know if this has a special meaning in C or Objective-C that would prevent it from being seen as a typo ? In my example, I believe that a mistake was made and Foundation.h was probably the intended include:

In the category file NSString+MD5.h I found:

#import "NSString+MD5.h"

@interface NSString(MD5)
- (NSString *)MD5; 
@end

The corresponding .m does not include the .h. (Link is "Here")

Q2: I got the compilation error because I started from an Apple project that doesn't NOT include the Foundation framework (AVCam 3.1 for iOS) ! So NSString was undefined. I'm a bit puzzled by this but linking with the foundation framework in xcode is like including all its headers ? I would expect again some warning at least ?

Thanks for any explanation on this probably very basic C topic.


Solution

  • First of all, in Objective-C, we #import, which is different from #include. While #include will attempt to include any file you list, #import will never double import any file. I imagine that this explains why there's no problem with a .h file trying to #import itself. It's not correct for the file to import itself, but because it's an #import, it won't actually cause any issues.


    As for the .m file not importing it's .h, in this case, it doesn't actually have to. It's a good idea, out of habit, to always import the .h file, and any files generated by Xcode will do this automatically, however it's not always necessary. It's only necessary when the .h file has declared something that the .m has to know about. For example, a @property that you intend to use. You've declared it in the .h so it'd have public access, but if you intend to use it in the .m, the .m has to know the @property has been defined. Additionally, if you've #imported some files in the .h that the .m needs and you don't want to import them again in the .m, you'd need to import the .h (although it's generally going to be better to just import them in the .m.

    Here, the .h simply defines the exists of a method that returns an NSString * and is called md5 and takes no arguments. This means that anything importing this .h file can call this method and Xcode won't complain about the md5 method not existing. The corresponding .m file implements a method that coincidentally matches the one the .h defined. If you deleted the .m file, your program would almost certainly still compile just fine... but you'd hit an unrecognized selector exception when you go to the point that md5 was being called--despite Xcode not complaining about it. With the .m in the project, this exception isn't hit. The program figures out at run time which method to execute.


    And finally, as for not including Foundation.h, every iOS project created by Xcode has #import Foundation.h and #import UIKit.h in the precompiled header file. Any other file in your project with these imports is just redundantly importing the file, as it's already been imported by the .pch, but because of the magic of #import (vs #include) it's not actually double imported.

    If you're working with an iOS project that doesn't have these imports in your .pch, your best bet is to just stick them in the .pch.

    If you're opposed to this for some reason, the best way to fix this md5 file is with this simple line:

    @import Foundation.NSString;