I am looking at this page about C++ differences from Objective C and it states this:
The dynamic nature of Objective C allows existing classes to be extended at runtime. Objective C allows you to define categories, related sets of extensions to objects you've already created. For example, in converting a text-based app into a graphics app, the code your objects needed to draw themselves could be compiled as a category and loaded at run-time only when needed. This saves memory and allows you to leave your original objects unmodified.
Now I am familiar with Categories and have used them, but I do not see how they lead to dynamic loading. If you import a Category file, is it not compiled along with the class it extends, taking up memory whenever you use that class, whether you use the Category methods or not?
You can load a bundle/plugin/framework at runtime. This is the dynamic nature of Objective-c that the quote references. It is not specific to Categories.
However, if the (compiled) code you load includes a Category on an existing Class, the extensions will work just as if they had been there all along. Ie a Class is not 'Frozen' at compile time, and loading a bundle/plugin/framework is one way to add new methods to an existing class at runtime.
This makes it relatively easy to implement a plugin architecture, or load code only when needed to make app startup time faster/keep memory footprint down, compared to some other C based compiled languages.
If you link with a static library containing a category, the linker will copy all of the category code into your executable file. If you link with a shared library, the shared library's entire code segment gets mapped into your process's address space, but it's paged in lazily, so you might not actually read all of the category code off of the disk unless you use it all.
But I think that's not really what the page is talking about.
Link-time libraries
First, let's talk about libraries that you tell the linker to link your app with.
Consider NSString. The NSString class is defined in the Foundation framework, which is a framework full of general-purpose classes useful in programs that have GUIs and in programs that don't have GUIs. So the NSString class as defined in Foundation doesn't include any code for drawing a string into a graphics context, because that code would (usually) be useless in a non-GUI app.
The AppKit framework (on OS X) manages a GUI. It's useful in a GUI to be able to draw strings to a graphics context, so AppKit contains a category on NSString that adds methods for drawing a string, like drawAtPoint:withAttributes:. UIKit (on iOS) does the same thing (but the methods are a little bit different).
So if you write a program on the OS X and use Foundation but don't use AppKit, your process won't load the AppKit NSString category and you won't pay the price for all of those graphics methods on NSString.
For a shared library like AppKit, the price is pretty trivial on modern hardware.
Now, you could do the same thing with your own libraries, which you might make static. Let's say you make a “TwitterModel” library for talking to Twitter. It's full of classes that model the things you find on Twitter, like accounts and tweets. But you don't include code for managing a GUI to display tweets.
Instead, you make another library, “TwitterGUI”, that (in addition to defining yet more classes) uses categories to add methods to the model classes in your “TwitterModel” library.
If you write a program that links to both TwitterGUI and TwitterModel, the executable file will contain all of the Objective-C code from both libraries. But if you write a command-line only program (no GUI) and only link it with TwitterModel, that program won't contain any of the GUI-related code. Oh, the savings!
Run-time libraries
Now let's consider shared libraries that you don't tell the linker to link your app with.
You can dynamically load new code into your process at runtime, using an API like dlopen or -[NSBundle load]. If the library contains categories, those categories will be added to the classes in your running program.
So, you could make your app optionally use a shared library if it exists on the user's system when he runs your app, by trying to load the library programmatically. If you succeed, you can call any category methods that you know the library defines. (And of course you can use the classes that the library provides, if any.) If you fail to load the library, you carefully avoid calling any of those category methods from the library.
Typically, though, we use a dynamic loading API to load a plugin, and the plugin provides some class that subclasses a base class, or conforms to a protocol, that we've defined specifically for plugins to implement. We just need to get the name of that class, and then we create an instance of it and send it the messages that we defined in our base class or protocol.
Related
I'm working on few plugins for Quartz Composer, that all link to the same custom static library copied for each of them in the bundles frameworks folder. The plugins could be used separately, so I have to distribute the library in each plugin.
Everything goes well, apart from the isMemberOfClass and isKindOfClass methods. I read here that importing twice the same classes could be the origin of the problem.
I have no error at compilation.
Let's say that I have 2 plugins (NSBundles) that contains the lib XCode project and compile it before linking to it.
They both copy the lib in their resources folder.
Then, they both instantiate a custom hOzPolygon2D class from that library.
The first plugin return true to the test of the hOzPolygon2D object with isMemberOfClass method.
The second return false.
isKindOfCLass method returns the same "error".
I can't imagine a solution in my case. I'm really not a compilation professional and would really appreciate some help.
You should distribute the static library separately (possibly as its own framework). From the question title I assume you're seeing duplicate symbol errors from the linker. If you statically link the same static library into multiple other libraries and then try to link an application to more than one of those libraries you're bound to see these duplicate symbol issues. I haven't actually tried this with frameworks, but I know of this issue from linking iOS apps against interdependent static libraries.
You shouldn't worry about the fact that the modules can be used separately. Just make sure your users can also get the base library. This is a normal situation. For example AppKit and UIKit depend on Foundation, but neither of them actually contains a copy of Foundation.
I have two frameworks in my Xcode project that both define a class with the same name (B.framework and C.framework both have a class named MyClass), resulting in a couple warnings like so:
Duplicate symbol _OBJC_METACLASS_$_MyClass originally in B.framework/B(MyClass.o) now lazily loaded from C.framework/C(MyClass.o)
Duplicate symbol _OBJC_CLASS_$_MyClass originally in B.framework/B(MyClass.o) now lazily loaded from C.framework/C(MyClass.o)
Then at run time only one of the implementations is loaded, and trying to use the other one will result in a "unrecognized selector sent to instance" because they are totally different classes (even though they have the same name).
I use one of the MyClass implementations directly in my code, but the other framework only uses its MyClass internally and I have no idea why its even exported (its not even mentioned in the frameworks header files, i used nm to view the symbols).
How can I make both frameworks work?
There's no such thing as "exported" classes in Obj-C. Or rather, there's no such thing as "non-exported" classes. This problem is precisely why the use of 2- or 3-letter prefixes on classes is strongly recommended for all Obj-C code. Your only solution (besides not using these frameworks) is to edit one (or both) of the frameworks to change the class name, or if you don't have access to the source, then you need to contact the vendor and ask them to make that change.
This guy came up with a pretty neat tool to generate a class dependency graph - however, it relies on parsing your source code and looking for #import directives.
http://seriot.ch/blog.php?article=20110124
https://github.com/nst/objc_dep/blob/master/objc-dep.py
This is neat, but I have a number of problems with this. Not least of which is it doesn't take into account imports of imports nor prefix headers nor whether-or-not the class(es) in the file referenced by the import are actually being used.
I'd like to do something more akin to class-dump and examine the Objective-C metadata stored in the Mach-O file to generate an in-memory representation of the class dependencies.
I'd rather not do this from scratch, so I'm wondering:
Has it already been done?
Is there an open-source library which would provide me with the foundational tools I need to extract this information (a library which examines the Mach-O file and creates a façade of the Objective-C information contained within - such that I could iterate over all of the classes, their methods, properties, ivars, etc and scan for references to other classes) I figure class-dump's source would be a good place to start.
If you have experience in this sort of thing, is what I'm trying to accomplish feasible?
What roadblocks will I need to overcome?
Has it already been done?
Not that I know of.
Is there an open-source library which would provide me with the
foundational tools I need to extract this information?
At the core of class-dump is libMachObjC which does exatly what you want, i.e. parse all classes/methods/ivars and more. The API is very clean, it should be very easy to use.
If you have experience in this sort of thing, is what I'm trying to
accomplish feasible?
Unfortunately, no because some classes don't declare the real class but use id instead. For example, here is the information that can be extracted from a class-dump of UIKit:
#interface UITableView : UIScrollView <NSCoding>
{
int _style;
id <UITableViewDataSource> _dataSource;
id _rowData;
...
The _rowData ivar type information is id but if you check at runtime you will see that _rowData is an instance of the UITableViewRowData class. This information is not present in the Mach-O binary so you have no way to find the relation between UITableView and UITableViewRowData. The same applies for method parameters.
Here's a solution that relies on information in mach.o files, and generates graph dependency based on that information: https://github.com/PaulTaykalo/objc-dependency-visualizer
Has it already been done?
yes - but i can't recommend a good public implementation
Is there an open-source library which would provide me with the foundational tools I need to extract this information (a library which examines the Mach-O file and creates a façade of the Objective-C information contained within - such that I could iterate over all of the classes, their methods, properties, ivars, etc and scan for references to other classes) I figure class-dump's source would be a good place to start.
most use cases would benefit by using the objc runtime facilities objc/... rather than examining the binary.
If you have experience in this sort of thing, is what I'm trying to accomplish feasible?
yes. i've done something similar using the objc runtime.
What roadblocks will I need to overcome?
that depends largely on the level of detail you want... implementation time if you find no such implementation, but i figure you will find a few options if you google the more esoteric functions in the objc runtime; perhaps you would find one in an (open) language binding or bridge?
if you do end up writing one yourself, you can get registered objc classes using objc_getClassList, then access the properties/information you want from there.
Is there a way to load classes from a mac app, and use them in a different mac app?
I'd like to make an Automator action that accesses some of the classes in my mac app, and this seems like the sort of way I'd ideally do it (means you have to have bought my app to use the Automator action, etc.)
Depending on what you want to do (I'm not quite clear), a Service might do the trick for you. You make a helper app which can pass data back and forth with your app, using a shared pasteboard. You can get a fairly wide range of action, because you can pass any object that conforms to the NSPasteboardWriting and NSPasteboardReading protocols; as it says there in the docs, NSString, NSAttributedString, NSURL, NSColor, NSSound, and NSImage are already available for you, and of course you can write a custom class that suits your needs exactly.
Have you tried creating a stand-alone Automator plugin project, or tried adding an Automator bundle target to your application's project?
I'm assuming that you want to create Automator actions for your main app, but are unclear how you get these actions to interact with your application (or with the classes present in your application).
There are 3 basic types of Automator actions: AppleScript-based, shell-script based, and Objective-C based. You'll most likely want to make yours Objective-C-based, which will allow you to easily incorporate other Objective-C code from your main application into the action itself (see Implementing an Objective-C Action). (Note that by default, when you add a new target for an automator bundle, it's an AMAppleScriptAction type).
To see how an Objective-C automator action is set up compared to an AppleScript-based action, you might want to try creating a separate standalone project.
Let's say your app is document-based, and uses the KWDocument class, which exposes a method named -duplicateObjects:(NSArray *)objects toDocument:(KWDocument *)destDocument;. You also have a KWRegistrationManager that knows whether your app is registered or not. And let's say you want to create an automator action that's called "Duplicate Objects to Document". The action will be implemented in KWDuplicateObjectsToDocument, which is as a subclass of AMBundleAction. In the Info.plist for Duplicate Objects to Document.action, the NSPrincipalClass will be KWDuplicateObjectsToDocument.
KWDuplicateObjectsToDocument.h will look something like:
#import <Cocoa/Cocoa.h>
#import <Automator/AMBundleAction.h>
#interface KWDuplicateObjectsToDocument : AMBundleAction {
}
- (id)runWithInput:(id)input fromAction:(AMAction *)anAction
error:(NSDictionary **)errorInfo;
#end
And your KWDuplicateObjectsToDocument.m will look something like this:
#import "KWDuplicateObjectsToDocument.h"
#import "KWDocument.h"
#import "KWRegistrationManager.h"
#implementation KWDuplicateObjectsToDocument
- (id)runWithInput:(id)input fromAction:(AMAction *)anAction
error:(NSDictionary **)errorInfo {
if (![[KWRegistrationManager defaultManager] isRegistered]) {
return nil;
}
// eventually you'll call
// duplicateObjects:toDocument:
return input;
}
#end
You'll need to make sure that the necessary classes you use (such as KWRegistrationManager, KWDocument, etc.) are compiled and included as part of the build process for this bundle.
Basically, no: you cannot link with an executable.
An application binary is in a specific format.
And that format is different from the static or shared library format.
It means you won't be able to load any code parts from an application binary, as you would with a library.
Take a look at distributed objects. Your application could vend one or more objects that your Automator action could use. I've never tried it with Automator, but it's a very elegant system that hasn't gotten a lot of attention in recent years. I think it's definitely worth a look.
One cool aspect of distributed objects is that the application could be running on the same computer if you wish, but it could just as easily be running on a different computer, perhaps even one that's very far away.
You could make certain behavior from you app accessible via Applescript, but accessing the the actual classes is not possible in the way I think you mean. I get the impression that you mean accessing the classes loaded into the memory of your running app. This is not possible on OS X (or any UNIX-like system). Applications run at the user level. Processess at the user level are not able to read memory from other processes. The components of the OS that need to do this sort of thing run at kernel level.
If you are just trying to reuse the code, you could build the parts you want to share into a static library, and others could link against it and share your code.
EDIT:
From NSGod's answer it seems that you can use the same approach that makes it accessible via Applescript and make it accessible via Obj-C. That looks pretty cool.
This is a problem which has been bugging me for a while now. I'm still pretty new with some of these patterns so you'll have to forgive me (and correct me) if I use any of the terms incorrectly.
My Methodology
I've created a game engine. All of the objects in my game engine use inversion of control to get dependencies. These dependencies all implement protocols and are never accessed directly in the project, other than during the bootstrapping phase. In order to get these objects, I have the concept of a service locator. The service locator's job is to locate an object which conforms to a specific protocol and return it. It's a lot like a factory, but it should handle the dependencies as well.
In order to provide the services to the service locator, I have what I call service specifiers. The service locator knows about all of the service specifiers in the project, and when an object is requested, attempts to get an instance of an object conforming to the provided protocol from each of them. This object is then returned to the caller. What's cool about this set up is the service specifier also knows about a service locator, so if it has any dependencies, it just asks the service locator for those specific dependencies.
To give an example, I have an object called HighScoreManager. HighScoreManager implements the PHighScoreManager protocol. At any time if an instance of PHighScoreManager is required, it can be retrieved by calling:
id<PHighScoreManager> highScoreManager = [ServiceLocator resolve: #protocol(PHighScoreManager)];
Thus, inversion of control. However, most of the time it isn't even necessary to do this, because most classes are located in a service specifier, if one required PHighScoreManager as a dependency, then it is retrieved through the service locator. Thus, I have a nice flat approach to inversion of control.
My Problem
Because I want the code from my game engine to be shared, I have it compiled as a static library. This works awesome for everything else, but seems to get a little tricky with the service locator. The problem is some services change on a game to game basis. In my above example, a score in one game might be a time and in another it might be points. Thus, HighScoreManager depends on an instance of PHighScoreCreator, which tells it how to create a PScore objecct.
In order to provide PHighScoreCreator to HighScoreManager, I need to have a service specifier for my game. The only way I could think of to accomplish this was to use the Cocoa version of reflections. After digging around, I found out classes were discoverable through NSBundle, but it seems there's no way to get the current bundle. Thus, if I want to be able to search out my service specifiers, I would have to compile my game logic into its own bundle, and then have the engine search out this bundle and load it. In order to do this I'd have to create a third project to house both the engine code and the game logic bundle, when in reality I'd like to just have a game project which used the engine static library.
My Real Question
So after all of that, my question is
Is there a better way to do what I'm trying to accomplish in Cocoa Touch, or
Is there a way to discover classes which conform to my service specifier protocol from the main bundle?
Thanks for the help and taking the time to read the question.
-helixed
Have a look at:
+[NSBundle mainBundle];
+[NSBundle bundleForClass:];
+[NSBundle bundleWithIdentifier:];
+[NSBundle allBundles];
+[NSBundle allFrameworks];
These allow you to interact programmatically with the various bundles at runtime. Once you have a bundle to work with there are a number of strategies you could employ to find the specific class(es) you are looking for. For example:
Retrieve the bundle identifier — this will be an NSString like #"com.example.GameEngineClient".
Transform it into a legal Objective-C class name by stripping everything before the last dot, or replacing all the dots with underscores, or whatever, and then appending a predefined protocol name. Your protocol from above, for instance, might result in a string like #"GameEngineClient_PHighScoreManager".
Get the bundle's designated class for your protocol using NSClassFromString().
Now you can create an instance of the class provided by the bundle author, that implements whatever protocol you have specified.
The Objective-C runtime is a beautiful thing!
Sounds like you need to use the functions of the Objective-C runtime. First you can get a list of all available classes via objc_getClassList. Then you can iterate over all the classes and check if they conform to your protocol with class_conformsToProtocol. You shouldn’t use +conformsToProtocol: messages here, since there are classes in the runtime that don’t support this selector.