Plug It In, Plug It In
Pages: 1, 2, 3
Build Phases
In the Build Phases portion of the Targets tab we can specify what header and source files to include in the build, what frameworks and libraries to link to, and more. We've already met the Headers and Sources build phases previously when we moved the IAGrayscaleFilter class from ImageApp to the plug-in bundle. What we're interested in doing now is adding a Copy Files build phase to ImageApp.
By default there is no Copy Files build phase, so we have to add one. This is easily done by selecting from the Project menu New Build Phase > New Copy Files Build Phase. This will add the Copy Files build phase to your list of build phases. To add files to the copy phase, just drag them from the Groups & Files list onto the desired build phase. In our case we will drag Grayscale Filter.plug-in (or Grayscale Filter.bundle) into the Copy Files phase. If you haven't build the Grayscale Filter bundle yet, the filename will be red, indicating that the bundle has a meta-existence in the project, but not a physical existence in the filesystem.
After adding the Grayscale Filter to the Copy Files phase, you have to specify where we want the files copied to. Plug-ins go in the Plug-ins directory, which can be specified by selecting Plug-ins from the pop-up menu in the Copy Files view.

We're almost to the point that we can get everything to build correctly. If you attempt to build now you will get compile errors with respect to the absence of IAGrayscaleFilter in ImageApp, since it is no longer included in the build process. The only instance of IAGrayscaleFilter is found in the class MyDocument. All we need to do is remove the method makeGrayscale from the interface and the implementation and remove any import statements importing IAGrayscaleFilter.h.
With all of these changes in place you should now be able to built ImageApp, have it automatically build the filter bundle to satisfy the target dependency, and copy Grayscale Filter.plug-in into the ImageApp bundle. You can check that everything went off without a hitch by showing the Info for ImageApp in the finder. If the plug-in copied correctly, you will see a Plug-ins section in the Info window containing Grayscale Filter.plug-in.

The Plug-in Manager
What we have just described above is how to package code up in a bundle. In the remainder of this column we will discuss the creation of a class that will scan for plug-ins when ImageApp launches and add any discovered plug-ins to the filter menu in ImageApp. The class we create will be called IAPlug-inManager, and it will exist as an instance within MainMenu.nib.
In Interface Builder
Open MainMenu.nib now. From the Classes tab in the nib window, select NSObject and press return to create a subclass. Name the subclass IAPlug-inManager. To this class we want to add one outlet named filterMenu. This is the outlet we will use to access the filter menu in the menu bar. When you create this outlet, it will be helpful if we set the class of the outlet to NSMenu. Sometimes connecting an outlet to a menu can be tricky, and this will help to make sure we get it right. Next create the project files for IAPlug-inManager, and initiate it.
To connect the outlet to the filter menu, you have to make sure that the filter menu is open. You cannot make the connection to the label on the main menu bar that says filter. That is a menu item of the main menu. So click on the filter menu to open it. Now, when you drag your connection wire from IAPlug-inManager to the filter menu, you have to drop it on the filter menu label, not on any of the menu items.

The last thing we need to do here is to remove the Make Grayscale item from the filter menu. Since all the items for this menu will be added programmatically by our plug-in manager, we want this to be empty. With that, save your work, and return to Project Builder.
Back in Project Builder
IAPlug-inManager is comprised of four methods:
awakeFromNibdiscoverPlug-inssetupFilterMenuexecuteFilter:
The first one, awakeFromNib, is easy enough:
- (void)awakeFromNib {
[self discoverPlug-ins];
[self setupFilterMenu];
}
This method simply invokes the method discoverPlug-ins and setupFilterMenu.
The purpose of discoverPlug-ins is to search for any installed plug-ins. In Mac OS X, plug-ins may be installed in a number of well-known locations:
- ImageApp.app/Contents/Plug-ins: This is where the developer of the application would put plug-ins that ship with the product.
- ~/Library/Application Support/ImageApp/Plug-ins: Where a user would keep personal plug-ins.
- /Library/Application Support/ImageApp/Plug-ins: Where plug-ins would be kept so they are accessible to all users on a system.
In today's column we set things up so that Grayscale Filter is copied to the first of these locations, within ImageApp's bundle. However, to require a user put all of their plug-ins in this single location causes a serious problem. When it comes time to upgrade the app, any user-installed plug-ins will be lost when the new version of the app is copied over the old. Thus, users should keep their plug-ins in one of the Library folders so they are available after upgrading the application.
The goal of discoverPlug-ins will be to enumerate the contents of each of the potential plug-in directories, and whenever we come across a bundle with the plug-in extension we will add an instance of that bundle's principal class to a dictionary with the filter menu item title as the key. Before getting to dicovers, go into IAPlug-inManager.h and add the NSMutableDictionary instance variable plug-ins to the class interface. Additionally import the headers for MyDocument and IAFilter.
Now, Here is what discoverPlug-ins looks like:
- (void)discoverPlug-ins
{
Class filterClass;
NSString *appSupport = @"Library/Application Support/ImageApp/Plug-ins/";
NSString *appPath = [[NSBundle mainBundle] builtInPlug-insPath];
NSString *userPath = [NSHomeDirectory
stringByAppendingPathComponent:appSupport];
NSString *sysPath = [@"/"
stringByAppendingPathComponent:appSupport];
NSArray *paths = [NSArray arrayWithObjects:appPath,
userPath, sysPath, nil];
NSEnumerator *pathEnum = [paths objectEnumerator];
NSString *path;
plug-ins = [[NSMutableDictionary alloc] init];
while ( path = [pathEnum nextObject] ) {
NSEnumerator *e = [[[NSFileManager defaultManager]
directoryContentsAtPath:path] objectEnumerator];
NSString *name;
while ( name = [e nextObject] )
if ( [[name pathExtension] isEqualToString:@"plugin"] ) {
NSBundle *plugin = [NSBundle bundleWithPath: name];
if ( filterClass = [plug-in principalClass] )
if ( [filterClass
instancesRespondToSelector:@selector(filterImage:)] ) {
NSString *menuTitle = [[plug-in infoDictionary]
objectForKey:@"IAFilterMenuItemTitle"];
[plug-ins setObject:[filterClass filter] forKey:menuTitle];
}
}
}
}
And there you have it. Let’s take our time to pick this apart. It's actually simpler than all the enumerators. The variable filterClass holds the class object returned that we obtain from the bundle.
In the next line we define the path to the plug-in directory found in the Library paths. In the next three lines we define the three paths where we will look for plug-ins. The first path is the Plug-ins directory found in the ImageApp bundle. This path is obtained using the NSBundle method builtInPlug-insPath. This method is invoked in the object returned by the mainBundle class method of NSBundle. The method mainBundle returns the NSBundle object representing the application's main bundle, which in our case is the ImageApp application bundle.
The next path is the path to the Plug-ins directory in the user's Library. This path is created by appending the common appSupport path to the user's home directory, which is obtained using the Foundation function NSHomeDirectory.
The final path is the location of the system-wide Plug-ins directory found at /Library/Application Support/ImageApp/Plug-ins/. This path is created by appending the common appSupport path to the root directory, /.
These three paths are collected together into an array, from which we obtain an enumerator assigned to the variable pathEnum. Before getting to the nested control structures, we create an NSString variable path that will be used in the enumeration of paths, and we initialize the instance variable plug-ins to an empty mutable dictionary.
Now, the heart of it. What we're doing in this unattractive nest of control structures is first enumerating the three Plug-in paths, we've defined, and for each of those three paths we obtain the contents of that directory. We then enumerate through those contents and check the extension for each object in that directory. If the path extension is plug-in, then we create an instance of NSBundle from that path.
Next we try to obtain the principal class of the bundle and assign it to filterClass. This is done by sending a principalClass method to the bundle instance. If there is no principal class defined, then the principalClass method will return Nil (The uppercase "N" is not a typo. Nil is the equivalent of nil for class objects) and we continue with the enumeration.
If there is a principal class, then we check to see if an instance of this class responds to the filterImage: message, which is essential if this is all to function correctly. To check if an instance of a class implements a specific method, we can use the NSObject method instancesRespondToSelector:. This method takes a selector as an argument, and in the code above, we pass @selector(filterImage:) since that is the method in question.
Finally, if there is a principal class in the bundle and it responds to filterImage:, we add an instance of the principal class to the dictionary plug-ins. The key for the filter instance in the dictionary is the string for the menu item title. To get this string we get the bundle's infoDictionary, which is an NSDictionary that contains all of the information contained in the bundles Info.plist, and we use the same keys in Info.plist to access objects in the info dictionary. Therefore, we use the key IAFilterMenuItemTitle to get the NSString object containing the title we set for the plug-in.
And that's discoverPlug-ins.
Next we have setupFilterMenu. This method is pretty straightforward. The idea here is to enumerate the contents of the dictionary plug-ins and add menu items to the filter menu corresponding to each plug-in. Lets take a look to see how it looks:
- (void)setupFilterMenu
{
NSEnumerator *e = [plug-ins keyEnumerator];
NSString *name;
while ( name = [e nextObject] ) {
NSMenuItem *item = [[[NSMenuItem alloc] init] autorelease];
[item setTitle:name];
[item setTarget:self];
[item setAction:@selector(executeFilter:)];
[filterMenu insertItem:item atIndex:0];
}
}
In this menu we went through each key in the dictionary, created a new NSMenuItem with that title, set the target of the menu item to self, the action to executeFilter:, and inserted the item into the filter menu.
The last method to put in IAPlug-inManager is executeFilter:. This is the action of every filter menu item. The purpose of executeFilter: is to get the correct filter instance from the plug-ins dictionary, using the menu item title as the key, and then to invoke in the MyDocument instance associated with the frontmost window the method filterImageUsingFilter: (which hasn't been defined yet). The method looks like the following:
- (void)executeFilter:(id)sender
{
NSString *name = [sender title];
IAFilter *filter = [plug-ins objectForKey:name];
MyDocument *doc = [[NSDocumentController sharedDocumentController] currentDocument];
[doc filterImageUsingFilter:filter];
}
In the method above we got the name of the filter from the title of the sender object, which will always be one of the menu items in the filter menu added by the method setupFilterMenu. Using that name we obtain the filter object corresponding to the menu item from dictionary plug-ins. Finally, we get the current document, and invoke the as-of-yet-undefined method filterImageUsingFilter: with the filter object passed as the parameter. The last piece of this puzzle is to add to MyDocument the following implementation of the method filterImageUsingFilter:
- (void)filterImageUsingFilter:(IAFilter *)filter
{
activeImage = [[filter filterImage:activeImage] retain];
[windowController setImageToDraw:activeImage];
}
Here we follow the pattern of our old method makeGrayscale: of passing the image assigned to activeImage to the filter through filterImage: and setting the activeImage to the returned image. We then tell the window controller for the document to update the displayed image to the new activeImage. And there you have it. Compile away, and play around with this to your heart's content.
One Last Thing
In the column before last, where we first learned about image filters, we created a Grayscale Filter. We did this by simply averaging together the magnitudes of the red, green, and blue components of the color into a single gray value. However, as many of you know and pointed out to me, the human eye is not equally sensitive to all colors of light. The correct thing to do is to do a weighted average of the three color components. So, where we had:
grayValue = ( red + green + blue ) / 3
equivalent (very nearly) to:
grayValue = 0.333*red + 0.333*green + 0.333*blue
we should have had:
grayValue = 0.222*red + 0.707*green + 0.071*blue.
So, while the details of the colorspace conversion were incorrect, the rest of our technique was correct. One link provided in the comments of that column containing lots of useful information about color that I found interesting is the Color space FAQ.
The End
What we have seen in today's column is how to create an plug-in architecture for an application. You can download the application here. For another perspective on this subject, have a look at this article by Rainer Brockerhoff. Where we created a public class for plug-in developers to subclass, he provides a formal protocol for developers to conform to.
Coming up in the next two columns we will take another detour from our Image App series to learn about networking in Cocoa: in the first we will look at the Foundation classes that let us use rendezvous, and in the second we will learn how to make two apps talk to each other using sockets. See you then!
Michael Beam is a software engineer in the energy industry specializing in seismic application development on Linux with C++ and Qt. He lives in Houston, Texas with his wife and son.
Read more Programming With Cocoa columns.
Return to the Mac DevCenter.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 21 of 21.
-
complex Plug-Ins
2002-10-28 23:15:15 stevesheets [Reply | View]
-
complex Plug-Ins
2002-11-08 11:34:18 Michael Beam |
[Reply | View]
These are good points you raise here. For handling a great number of plugins I would agree that initially storing a path to the plugin would work. It doesn't take all that long to load a bundle, and the user probably wouldn't notice it. Plus, once it is loaded, it wouldn't need to be reloaded. Some might take this idea further and do something like keep a count of how many times certain plugins have been used over all sessions in the application. The application could check at startup if this count is above a threshold, and if so preload the plugin in anticipation of the user needing it.
As for complex plugins, a bundle is like a mini self contained application. I've made plugins that work with the architecture we setup here that will load a window with controls that let you tweak how the plugin operates. For example, in the method filterImage: you could load a nib included with your bundle's resources that contains a UI for interacting with the plugin. This would load a window with all of the controls for the plugin. there would probably also be OK and Cancel buttons. Cancel would cause filterImage: to return the original image, and OK would be made to execute the core filter code.
There are lots of possibilities with this, and quite likely this architecture may not be the best for many people. It is, however, one way of solving the problem, and gives an idea of the mechanics involved.
Mike
-
multiple plugin filters didn't work for me
2002-10-21 23:27:57 psheldon [Reply | View]
NSLog(NSStringFromClass([plugin principalClass]));
gave me no print if filter wasn't one targeted in the project, even if I copied plugin in application support. This was born out with other NSLogs showing the outer if jumps past inner codes.
I had gone out to give myself a latent period but I only put additional diagnostics to point to this and guessed that maybe I needed a menuIndex rather than always zero, hoping the word insert was misleading and really meant augment. But that didn't produce a fix.
So, I am left with my app not recognizing the principleClass being there for plugins it hasn't targeted.
Letting go and to sleep, now.
-
Apple Laptop Keyboards Unsuitable for Unix Users
2002-10-21 22:34:50 anonymous2 [Reply | View]
Apple laptops are effectively unusable for unix users.
I am a long-time Unix user. That means I need to have the Ctrl key to the left of the A key. This is a genuine need, not merely a want; it is based upon ergonomics. The Ctrl key is heavily used in unix, and it must be easily accessable. It cannot be off in the lower left corner of the keyboard where it is difficult to get at, and where it distorts the position of your left hand such that you can't easily type other keys while holding the Ctrl key down.
Apple desktop keyboards are now all USB. They are all OK. The CapsLock key can be re-mapped into a Ctrl key.
Unfortunately, even in this modern age, all Apple laptops have built-in ADB keyboards. The ADB keyboard is broken-by-design. It is, in general, not possible to remap the CapsLock key into a Ctrl key.
There are some exceptions, but they are horrible kludges. They are
horrible kludges because the original design of the ADB keyboard was a horrible kludge. The correct solution would be for Apple to re-design their laptop motherboards to use built-in USB keyboards. This hasn't happened yet. If you run Linux, use Debian's solution. For Mac OS X users, uControl works. There are no solutions (that I know of) for either NetBSD or OpenBSD. Please note once again that the "solutions" above are in fact kludges, because of the original bad design of the ADB keyboard.
Apple is (currently) ignoring Unix users! This is not merely speculation on my part. In an on-going email exchange I am having with an Apple employee (whom I won't name) in their marketing department, the Apple marketing person directly stated to me that Apple was catering to their historic Mac customers, and is purposely ignoring the Unix market. He also claimed that Apple would soon start paying more attention to the Unix market. I won't hold my breath. Apple has been ignoring Unix users for more than 12 years. I expect that trend to continue. (Also note that my Apple contact indicated that Macs would never ship with a 3-button mouse, even though Apple intended to port almost all X-window software and deliver it either on a CD/DVD or installed directly on each Mac's hard drive. How Unix friendly is a 1-button mouse with X programs that often require 3 buttons?)
Apple has now lost two opportunities to sell me hardware. I really wanted an Apple laptop for their superior battery life, and for the PowerPC with Altivec CPU. (The Altivec is vastly superior to the x86 line for DSP.) Because I can't live with the broken-by-design built-in ADB keyboard in all Apple laptops, Sony and IBM sold me laptops instead. If Apple fixes this problem, they will sell me a PowerBook next year; if they don't, I'll still be running OpenBSD on x86 hardware, and wishing I could use a Mac.
-
Apple Laptop Keyboards are fully customizable dude.
2003-07-06 23:18:52 anonymous2 [Reply | View]
You can remap the special function key-bindings by changing the /System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict file. Or if you're savy you can build your own input server and install it in your account library. There is even sample code in the Developer AppKit examples included with the Dev install. Heck, you can even customize on a per-application basis if you're smart enough. -
found pdf on keyboard remap, hope it helps
2002-10-23 13:50:05 psheldon [Reply | View]
http://developer.apple.com/technotes/tn2002/pdf/tn2056.pdf
No mention in it that it won't work with ADB or powerbook. So, there's hope. -
kchr resource in os 9 but no unix there
2002-10-21 23:38:18 psheldon [Reply | View]
Has next step put in a similar resource in os x?
kchr is the way to set different keyboard mappings in os 9 be they usb or ADB.
I found a music system wanted a special keyboard layout, so I imagine eMagic buy by Apple will make a change of that salesman's "tune".
I haven't a clue how to search how os x might remap keyboards that are ADB, but I believe that os 9 did.
Hope you have gotten a better clue for your needs than I feel I have given.
-
Plug-ins don't load if not running from ProjectBuilder
2002-10-21 18:25:30 anonymous2 [Reply | View]
When I run the app from projectbuilder, the filter menu is loaded with the plugins, but when i run the app from the finder, the menu is blank…
A look at console shows a message while runing in projectbuilder : using implementation from path to file…
but nothing when runing the app by itself…
Does anybody have any idea?
thx nicolas. -
deployment candidate fix but didn't work
2002-10-22 10:25:54 psheldon [Reply | View]
I went reading PB Help and found I could make a deployment as opposed to a development build style only from the command line interface.
There was some entendre about superuser stuff with mention of root, so I did sudo pbxbuild install to gain a successful build where, when I hadn't, I got permissions denied and failed build.
But, the deployment didn't go where I had put it in projectbuilder, so I couldn't click on it to see whether it would see the plugins.
The apps used to work from a click in the build folder, so I think this working hypothesis that deployment was all that important for our level was overly optimistic and that hacking away at how to deploy just might be sour grapes anyway.
I am siezing on another hopeful working hypothesis in my lateral thinking, that the solution to your problem might also solve my problem of actually making plugins plural.
When I simply drag files with .plugin into Application Support plugins aren't seen as having a principle class, somehow, unless I am running from the project that generated them. I checked this with my version of plugin app and Mikes.
So, this is just an idea, a step along the way, not the answer. I try to remember that victory only comes to the frustrated.
;-) -
deployment candidate fix but didn't work
2003-05-29 09:19:01 anonymous2 [Reply | View]
In some cases, build style will make some problems, but not this one.
The problem is caused by a small bug in the code:
we get the plugin by:
plugin = [NSBundle bundleWithPath: name];
and we go the name by enumerate the objetc returned by:
[[NSFileManager defaultManager] directoryContentsAtPath: path]
You will find that the name variable only contains the name of the bundle, but not the path. So the application has to find the bundle in the default directory. And apparently, the envionment setting of running within PB and as a standalone application is different, so it makes our application behave differently.
The solution is add a statement on the first of second while block:
name = [path stringByAppendingPathComponent: name];
You will find that it works fine now.
Good luck.
-
drawinginquartz2D frustration
2002-10-21 10:11:52 psheldon [Reply | View]
I forced myself to read through a pdf file in developers documents. I had figured ways to save images as pdf files, but only one image at a time into one page and this pdf book explained graphics contexts and states that seemed to allow for multiple pages.
It, however, did not hold my hand enough with sample code, so I couldn't port the information into what I am trying to do, save a pdf document with multiple pages with several images from this sort of app here.
What I suppose is that I can read various documents and get confidence that I can finish them without making an application and get something hooked up in my brain to make searching for code easier. Perhaps the hookup is language (eg. here quartz function calls, learned not to look only in a framework browser for object methods, saw importance of os x architecture diagram distinct places to look), perhaps it is one feature such as a bitmap can have interpolation so you don't have the jaggies giving you lateral inhibition to recognize text spatial frequencies.
I am excited to learn next about rendezvous and sockets. That different applications and different people should connect with each other means that folks who think different needn't be "excommunicated". I like the metaphor of celebrating differentness this way. Only with intercommunication can we do supercomputer things.
;-)
-
Can't build bundle
2002-10-16 23:14:20 johnts [Reply | View]
I got as far as making all of the changes to get the plug-in to compile, just before creating the new class to manage the plug-ins. I tried to build and got an error:
don't know how to make /Users/john/Devel/ImageApp3/build/Grayscale Filter.bundle
I made the all the modifications, as far as I can tell, but I have no idea how to make this work. The name of the bundle did not change under the products section. I get this even if I try to just build the bundle/plug-in only itself.
What did I miss?
Thanks. -
Brockerhoff on how to make...bundle
2002-10-28 08:04:49 psheldon [Reply | View]
Rainer Brockerhoff wrote (footnote 2 page 2) in recommended reading :
"...changing the extension (from bundle to plugin) after building even once may encounter even more resistance from Project Builder."
So, our difficulties with project builder being confused trying to "make a bundle" which we had specified, instead, to something in it to be a plugin, were abbreviated by this master hacker and we are in good company.
It is hard to write about such an intuitive experience as getting rid of bugs. It is the folklore of the trade. It is your curse (though you may make a bundle doing this) if you are doomed to get rid of other people's bugs as a master software engineer and never have time for your own creative stuff.
;-) -
Can't build bundle
2002-10-19 18:56:14 psheldon [Reply | View]
You didn't miss what I missed at first reading the log file. I couldn't make either. You saw that .bundle was at the end of the thing it couldn't make, but, from things we entered elsewhere, it should have read ".plugin" in the log file. I looked at targets and saw bundle was still there in target name, but I had done stuff to make it plugin. I quit project builder and then launched it again and found that name had changed.
Subsequent compiling didn't produce that "can't make error".
I am very curious whether I was able to observe around this target spelling "weakness" and if you will find a similar wierd "fix" or, instead, think even more differently than me.
;-)
In build phases frameworks and libraries, I also put in AppKit.framework, in addition to Cocoa.frameworks. This seemed plausible, but wasn't evidentally necessary as it wasn't in Mike's working source.
Project builder seemed to quit unexpectedly when I asked it to quit, so I am getting paranoid about saving rather than have it remind me. It can't remember to remind me if it is being surprised by quitting.
;-)
A working hypothesis for systems engineers at Apple is saving and quitting must subscribe to the same sort of internal stuff, a module that they got confused in the latest version of project builder.
Last night, I thought I would be crazy to write this in a thread, but today, having gotten the victory of finishing the column's whole program bug free, I am not so embarrassed to suggest that someone else might have made a mistake and even guess where to help. -
Can't build bundle - fixed - new compile error
2002-10-20 19:23:36 johnts [Reply | View]
It turns out, my error was I had the wrong target set when I created the new Copy Files section - it was trying to copy itself...I changed that and that compiled.
However (I still haven't done the second part of the article) I'm getting a compile warning:
AImageView.m: In function `-[IAImageView validateScrollers]':
IAImageView.m:34: warning: initialization from incompatible pointer type
At this line in the validateScrollers: method:
NSScrollView *scrollView = [[self superview] superview];
????? -
ran through article again with my own BlackAndWhite plugin
2002-10-21 14:45:05 psheldon [Reply | View]
Took about 1 and 1/2 hours this last time around. Things do get faster once you've learned to make decisions especially pruning one's understanding search space.
That was heartening. It meant I could study a column after going through the struggle and not have to struggle so hard. It is important to review fundamentals and getting the confidence that I didn't have to struggle so hard as the first time was like a liberation.
I think I had to add to Build Phases Frameworks & Libraries something beyond the column :
AppKit.Framework.
But, Mike didn't have to add that in his project, so I must have done something else. It is hard to keep track of what I did right, even after recapitulating most everything.
At one point copyfiles was on the wrong target and the right one for a long path recursion that log got confused about. I don't recall doing that mistake again. -
Can't build bundle - fixed - new compile error
2002-10-21 09:57:57 psheldon [Reply | View]
I too had the wrong target set at one time. It led to a deeper and deeper pathname to the plugin. Every once in awhile I got pathname too long. I started getting desperate and deleted from the build folder in addition to cleaing from PB. Then that error message would disappear for awhile until the file got too deep.
I don't know whether that was associated with the target not renaming itself. I think it was the PB restart that insured the renaming from bundle to plugin.
I too got IAImageView.m:34: warning: initialization from incompatible pointer type but didn't know what to do and got my version working even with the warning, so left understanding this alone.
So, that single warning is not enough to let you know if and why your program is not working.
If you rebuild without a clean all warnings go away and you forget that you ever had them.
;-)
You must ask yourself "Does my program work?" and not assume the warning means that you have still dones something wrong.
Does your program work now?
-
plugin vs. plug-in in discoverPlug-ins
2002-10-16 12:44:22 halliday [Reply | View]
- (void)discoverPlug-ins
{
...
while ( path = [pathEnum nextObject] ) {
...
while ( name = [e nextObject] )
if ( [[name pathExtension] isEqualToString:@"plugin"] ) {
/**/NSBundle *plugin = [NSBundle bundleWithPath: name];
/**/if ( filterClass = [plug-in principalClass] )
...
As you can see above, you declared the NSBundle as "plugin" but you used it as "plug-in". Unfortunately, hyphens are significant in all C languages. :-)
In fact, doesn't C disallow hyphens in identifiers, so it would try and take the difference between "plug" and "in"?
This will cause a compiler error, or two. :-) -
plugin vs. plug-in in discoverPlug-ins
2002-10-25 07:18:04 Michael Beam |
[Reply | View]
Wow, that's really bizarre. That's about all I can say about that. Something must have gone a little wonky in the final publishing process. At anyrate, as you correctly surmised, the hyphens should no be there at all. -
Apple shouldn't have used a popup with "plug-in" on it
2002-10-19 19:12:32 psheldon [Reply | View]
I see I was wise to download Mike's working code. I used to be scared to compare my nonworking code with working code, but now I have learned some confidence.
Comments, columns, or documentation might spell things ala Webster for a promised but late night release. To err is human too late. Deadlines doth make fools of us all.
;-)
I think Apple or Oreilly might need to fiddle with their spell checker to make their own English for c.
In addition, when you write something with a dash, you evidentally don't tend to capitalize after the dash for a sense of word break. So, when I scanned for dashes to remove, I had to also be careful to capitalize to get the defined method, because objective c distinguishes between capital and small letters.
Hope that helps others on this thread as yours post did.
Thanks.
Maybe life needs this source of frustration for there to be any sense of victory!
;-)






However, what about complex plug-ins, which consist of multiple objects taking up a sizable amount of memory? In these cases, you do not want to invoke an object for each plug in that you might have in your various paths. This would take up to much memory! You may want to store the name of the plug ins, and only create them when the user invokes them. This is especially true in the instance where each plug in object might be invoked more than one time at same time (ex. each plug in creates a different window, and you might want two instances of the same window up on the screen).
In these cases, should you only store the name of the plug in? And when user invokes it, find the path to correct bundle? Or would this take to long, and it be better to open each Bundle, and keep the Bundle object around, ready to be invoked when needed? Or as alternative, should you invoke a small primary object at startup (as your example program does), which would only create more complex objects when the user invokes it? In that case, if you have simple object from a plug in bundle, can it safely create new objects from that bundle without any problems (ex. your filter bundle has an EditFilterSetting method which invokes a Window/WindowController and new custom view)? I was not sure you could link to other code in the bundle, after the NSBundle instance has been autoreleased.
What do you think?
Keep up the great work!
Steve Sheets