Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSObject's category is available for every NSObject subclass even without any import of this category's .h file anywhere

Background.

Please consider the following steps:

1) In Xcode create a new "Single View Application".

2) Create a category NSObject+Extension.h and .m files:

// .h
@interface NSObject (Extension)
- (void)someMethod;
@end

// .m
@implementation NSObject (Extension)
- (void)someMethod {
    NSLog(@"someMethod was called");
}
@end

3) Ensure that NSObject+Extension.m file is included into a main target.

4) Add the following line to AppDelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[NSString new] performSelector:@selector(someMethod)];

    return YES;
}

5) Ensure that #import "NSObject+Extension.h line does not exists anywhere in the app!

6) Run Application.

The output is

2013-08-27 04:12:53.642 Experimental[32263:c07] someMethod was called

Questions

  1. I wonder if there is no any #import of this category anywhere in the app, how is it even possible that NSString does still have NSObject+Extension available? This behavior makes me feeling very bad about every Objective-C category I declare because I want the categories I declare to be available only in the scopes they are declared within. For example, I want NSObject to be extended by Extension only in some class but not in the whole app because its globalspace becomes "polluted" otherwise.

  2. Is there a way to avoid this behavior? I do want my categories to work only when I explicitly import them, not when I just have them linked to a target I use to run.

like image 598
Stanislav Pankevich Avatar asked Aug 27 '13 01:08

Stanislav Pankevich


People also ask

How to subclass NSObject?

Create a new file and choose Cocoa Touch Class. Click Next and name the class “Person”, type “NSObject” for "Subclass of", then click Next and Create to create the file. NSObject is what's called a universal base class for all Cocoa Touch classes.

How do I create an NSObject in Objective-C?

Creating a Custom Class Go ahead an choose “Objective-C class” and hit Next. For the class name, enter “Person.” Make it a subclass of NSObject. NSObject is the base class for all objects in Apple's developer environment. It provides basic properties and functions for memory management and allocation.

What is NSObject in Swift?

The root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.

Should Swift classes inherit from NSObject?

While Swift's base classes don't require inheritance from NSObject , it is still available as an option when creating a class. To create a Swift-y subclass of NSObject named Ponso , we would begin the class's definition like this: class Ponso: NSObject {...}


2 Answers

I wonder if there is no any #import of this category anywhere in the app, how is it even possible that NSString does still have NSObject+Extension available? This behavior makes me feeling very bad about every Objective-C category I declare because I want the categories I declare to be available only in the scopes they are declared within. For example, I want NSObject to be extended by Extension only in some class but not in the whole app because its globalspace becomes "polluted" otherwise.

There are no namespaces on Objective-C objects. If you declare that a class has a method (whether via a category or on the primary @interface) then every instance of that class will have that method.

The way that Objective-C deals with "private" methods is by choosing not to tell other people about the methods in question (which is accomplished by not #import-ing the file that declares those methods). This, coupled with -Wundeclared-selector (warn if you use a selector that the compiler doesn't know about) is about as good of a guard as you're going to get.

But regardless, if you compile the .m file into your final binary, the method will exist, even if no one else "knows" about it.

Are there way to avoid this behavior? I do want my categories to work only when I explicitly import them, not just when I have them linked to a target I use to run.

Yeah, use -Wundeclared-selector, and Xcode will warn you.

like image 174
Dave DeLong Avatar answered Oct 13 '22 01:10

Dave DeLong


Including the header just makes it so the compiler knows about it. It compiles it regardless because xCode compiles every file included in a target. At runtime, the method will be there, so even if you didn't include it for compile time checking, the object will still respond to that category method.

like image 25
atomkirk Avatar answered Oct 13 '22 00:10

atomkirk