I have created an NSObject subclass without any implementation and named it "WebView".
In my code I need to be able to dynamicly instantiate objects of this class using NSClassFromString.
While it seams like a trivial work, I am having problems with this specific class name "WebView".
It seams like NSClassFromString() is failing for this specific class name, but with lldb I can print both class1 and class2 descriptions and for both it says "WebView".
Anyone else experiencing same issue? Is there some list of forbidden class name like it is for instance variables etc?
#interface WebView : NSObject
#end
#implementation WebView
#end
#implementation ViewController
- viewDidLoad {
Class class1 = [WebView class];
Class class2 = NSClassFromString(NSStringFromClass(class1));
NSAssert(class1 == class2, #"Classes not equal"); // FAIL
}
#end
And another example:
[class1 new]; // OK
[class2 new]; // EXC_BAD_ACCESS (code=2, address=0x0)
When I try to allocate the WebView. It looks into apple's internal framework as shown in image.
You should always use a three letter prefix for your classes to avoid conflicts like this. Apple reserves all two letter prefixes for itself.
When creating your project in Xcode, it will even ask you what prefix you would like to use and it will create new classes using that prefix for you.
WebView is the instance apple sdk class. When i used UIWebView i was seen webView class how ancestor UIWebView. So, do not use class with name WebView, this name use the apple.
Related
I am trying to create a global function or method in objectiveC. This is so that I can call it from any of my view controllers.
I already checked out this post: How to create global functions in Objective-C
So what I did was in Xcode 6 I created new file and choose a category drop down. that created these two *.h & *.m files for me. I added my method called SaveWeatherDataNow in it
//NSObject+SaveWeatherData.h
#import <Foundation/Foundation.h>
#interface NSObject (SaveWeatherData)
+(void)SaveWeatherDataNow:(NSString *)XML;
#end
//NSObject+SaveWeatherData.m
#import "NSObject+SaveWeatherData.h"
#implementation NSObject (SaveWeatherData)
+(void)SaveWeatherDataNow:(NSString *)XML
{
NSLog(#"SaveWeatherDataNow XML lenght: %ld", [XML length]);
}
#end
Now in my another view controller if I call this method then complier doesn't find it at all. What did I miss?
// v1CitiesAdd.m
#import "NSObject+SaveWeatherData.h"
#implementation v1CitiesAdd
...
...
-(IBAction) doneAction
{
...
[SaveWeatherData SaveWeatherDataNow:XML]; // Complier Error: Use of undeclared identifier SaveWeatherData
Change the + to a - (in front of (void)):
-(void)SaveWeatherDataNow:(NSString *)XML;
That way, you can refer to the method within your SaveWeatherData object.
Don't forget to change it in both the header (.h) file as well as the class (.m) file.
You could also call
[NSObject SaveWeatherDataNow:XML];
by leaving the +.
===
When you use a +, it's a class method. It works globally, and doesn't know or care about individual objects.
When you use a -, it's an object method. It works within a specific object.
So, if there is a Brick class,
+(void) smash;
would change/update every brick to be smashed.
-(void) shatter;
would shatter a specific brick you send that message to.
[myBrick1 shatter]; would only shatter the one brick.
[Brick smash]; would change something globally for all bricks.
I am writing a different answer, because I think the best answer to this question would be Don't do it!.
To create a category for NSObject to save data is a very ugly thing to do.
WHY
It would mean that when you import it to a class EVERY class that inherits from NSObject (almost all of them) would suddenly have that extra method.
For example, code like this would work:
NSNumber *myInt = #(3)
NSString *myXmlAsString = [NSString stringWithFormat:#"<rating>%#</rating>",myInt.integerValue];
If you keep using the static method (with +) you could do:
[NSObject SaveWeatherDataNow:myXmlAsString];
or
[NSNumber SaveWeatherDataNow:myXmlAsString];
or even
[UIImage SaveWeatherDataNow:myXmlAsString];
If you change it to an instance method (with -), you could do:
[myXmlAsString SaveWeatherDataNow:myXmlAsString];
or
[myInt SaveWeatherDataNow:myXmlAsString];
or use most of the existing variables in that scope, which would seem very strange to say the least.
WHAT YOU SHOUD DO INSTEAD
I would use one of following 2 options:
Create a class that manages data saving/loading for you. The usage should then look like this:
[DataManager saveWeatherDataNow:myXmlAsString];
If you don't need that much, only that function, you could create a Utility class and add that static method (in case you do not have one already).
[Utility saveWeatherDataNow:myXmlAsString];
PS: by convention, you usually start method names with lowercase.
[SaveWeatherData SaveWeatherDataNow:XML]; // Complier Error: Use of undeclared identifier SaveWeatherData
The root of the problem here is that you haven't defined a class named SaveWeatherData. Instead, you've created a category on NSObject, so that you can call +SaveWeatherDataNow: on any class that's derived from NSObject. The straightforward solution is to create a class instead of a category. Change these lines:
#interface NSObject (SaveWeatherData)
#implementation NSObject (SaveWeatherData)
to these:
#interface SaveWeatherData
#implementation SaveWeatherData
and that should allow a call like this:
[SaveWeatherData SaveWeatherDataNow:XML];
to work correctly.
Imagine I have define a class MyClass as follows:
The class interface file:
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
#property (nonatomic) NSString *myProperty;
- (void)myPublicMethod;
#end
The class implementation file using categories:
#import "MyClass.h"
#interface MyClass (MyCategory)
- (void)myPrivateMethod;
#end
#implementation MyClass
- (void)myPublicMethod {
NSLog(#"myPublicMethod was called!");
[self myPrivateMethod];
}
- (void)myPrivateMethod {
NSLog(#"myPrivateMethod was called!");
}
#end
An alternative class implementation file NOT using categories:
#import "MyClass.h"
# implementation MyClass
- (void)myPublicMethod {
NSLog(#"myPublicMethod was called!");
[self myPrivateMethod];
}
- (void)myPrivateMethod {
NSLog(#"myPrivateMethod was called!");
}
#end
Was hoping someone could explain the difference between the two implementation file approaches.
Is it the case that using categories means the "private" methods are inherited by any subclasses of MyClass and not using categories means the "private" methods are not inherited by any subclasses?
All methods that exist on a class are always inherited and are callable by anyone regardless of how you declare them. The main difference is whether anybody knows about them. There was also a historic need to declare things before use which leads to internal forward declarations in older and old-style code.
A category is used to add methods to an existing class. A common use is to extend the functionality of one of the existing classes. For example you might implement:
#interface NSURL (HTTPQueryParameters)
- (NSDictionary *)httpQueryParameters;
#end
So from then on you've given NSURL itself the knowledge required to parse HTTP protocol query parameters. It's often the correct factoring to add functionality directly to classes you don't have the source for.
Objective-C used to follow the C rule that methods had knowledge only of those methods that had preceded them within the compilation unit. So to be able to call a method that appeared later in the source file you'd need a forward declaration. If you didn't want to publish that method for the world to see you could achieve that with a category or a class extension (which for this purpose is just an unnamed category).
Nowadays Objective-C methods can call any method that is defined anywhere within the compilation unit, including subsequently in the same source file. It's therefore now normal not to collect up your unpublished methods into a category or an extension just for the benefit of the compiler.
That leaves categories for:
adding functionality to existing classes; and
segmenting your classes if they become very large;
Class extensions are now primarily for:
declaring #propertys without publishing them.
In Objective-C any method call can be sent to any object — objects are dynamically typed. So there's a mapping table in memory at runtime for every class from method name to implementation. The lookup process is to see whether the method is implemented in the class dispatched to. If not then dispatch to the superclass. An exception will be raised if the runtime runs out of superclasses.
The declaration of the method in a category #interface only serves to expose the method to users of the class, including -- as you mentioned in your comment -- subclasses.
(It would be much more usual to use a class extension (sometimes called an "anonymous category") declare a method that you're defining in the main implementation block. Actually, I'm not 100% sure what the interaction is between your category declaration and the main block definition -- I wouldn't have been surprised if it didn't compile, but it does.)
Thus, the only difference between your two examples is that the declaration allows you to create a private header in a situation where you want your own subclasses to access this method, but have framework users who you want to restrict.
I am looking at the Ray Wenderlich AFNetworking tutorial, and I have come across a way of subclassing NSDictionary that I have not seen before:
first:
#implementation NSDictionary (weather)
then in another file:
#implementation NSDictionary (weather_package)
This seems to just "work" in another file where it's included. What's most puzzling is that the NSDictionary defined in a class that uses these seems to match the "weather_package" version and within that container the "weather" version.
Can someone point me to the name of what's going on here? I am not entirely clear on how this works from reviewing the code, particularly how the compiler decides what to apply where since no explicit reference is made to these files apart from including them in the header.
Thank you for any help.
This is not subclassing. This is extending the class using categories. This is used to bring new functionality into existing classes without subclassing them.
A category can be declared for any class, even if you don’t have the original implementation source code (such as for standard Cocoa or Cocoa Touch classes). Any methods that you declare in a category will be available to all instances of the original class, as well as any subclasses of the original class. At runtime, there’s no difference between a method added by a category and one that is implemented by the original class.
A category can be defined like
#interface ClassName (CategoryName)
#end
#implementation ClassName (CategoryName)
#end
For more information read https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW1
It's not subclassing, it's more extending existing implementation with new methods, so called categories:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html
I'm new to iOS development and a bit stucked with such problem.
In my iphone app I'm using this awesome dropdown view controller https://github.com/nmattisson/DropdownMenu via Cocoapods.
I'm extending DropdownMenuController in my own MyDropdownMenuController:
MyDropdownMenuController.h
#import "DropdownMenuController.h"
#interface MyDropdownMenuController : DropdownMenuController
#end
I would like to override this drawOpenLayer (https://github.com/nmattisson/DropdownMenu/blob/master/DropdownMenu/DropdownMenuController.m#L126) method inside my controller instance, but unfortunately compiler says it's not possible:
MyDropdownMenuController.m
- (void)drawOpenLayer {
// compiler says
// "No visible #interface for "DropdownMenuController" declares the selector "drawOpenLayer"
[super drawOpenLayer];
}
Is it possible to override this method without actually updating external interface etc.?
You can create a category that defines the method
#interface DropdownMenuController (MichaelHacksIt)
- (void)drawOpenLayer;
#end
(no need to define the #implementation for this, because it's already implemented.)
Then you may just call that method.
Disclaimer:
Btw, that's the way to go if you want to call undocumented methods and don't care about Apples approval. In this case, there is nothing wrong with it, because you're not hacking Apple code, and Apple doesn't care if you hack some CocoaPods program. However, you are depending on internals of a third-party package, so there may be problems when you update that package next time...
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
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.
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.
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.
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.