‘NSAutoreleasePool’ may not respond to ‘-drain’ on Ubuntu - objective-c

I'm trying to compile the following Objective-C program on Ubuntu Hardy, but for some reasons, I'm getting warnings.
#import <Foundation/Foundation.h>
int main (int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog (#"Hello");
[pool drain];
return 0;
}
Output of the compiler:
$ gcc `gnustep-config --objc-flags` -lgnustep-base objc.m
This is gnustep-make 2.0.2. Type 'make print-gnustep-make-help' for help.
Making all for tool LogTest...
Compiling file objc.m ...
objc.m: In function ‘main’:
objc.m:6: warning: ‘NSAutoreleasePool’ may not respond to ‘-drain’
objc.m:6: warning: (Messages without a matching method signature
objc.m:6: warning: will be assumed to return ‘id’ and accept
objc.m:6: warning: ‘...’ as arguments.)
Linking tool LogTest ...
Here's the result of the execution:
$ ./a.out
2009-06-28 21:38:00.063 a.out[13341] Hello
Aborted
I've done:
apt-get install build-essential gnustep gobjc gnustep-make libgnustep-base-dev
How do I fix this problem?

First, the simple answer: use -release instead. I believe -drain was added in 10.4 as an alias for -release, and in 10.5 it gained GC-specific behavior of its own. This allows code to use it in 10.5 and still work under 10.4. GNUstep probably doesn't yet have the new functionality.
Obviously you're trying out some boilerplate Objective-C code on Ubuntu, but it causes me to wonder what you're hoping to accomplish in the long term. Don't let me deter you if it's just out of curiosity or for the challenge of it. However, if you're planning to use GNUstep to develop Objective-C for serious programming, I would advise against it, for several reasons.
Objective-C is an interesting programming language with a number of powerful features, but not significantly more so (by itself) than other object-oriented languages. Objective-C really becomes compelling when you pair it with the cool features in Cocoa and other related frameworks. Apple (primarily) drives those frameworks, and only for Mac/iPhone.
Apple generally has the best tools and user experience for Objective-C development. They're also investing heavily in development of LLVM and Clang as a replacement for gcc. This will (an already does) make possible some very cool things that gcc wasn't designed for.
GNUstep is an admirable project, but since it depends on volunteers and reverse-engineering new features added by Apple, it always lags behind the state-of-the-art. The new shiny Objective-C features will always start with Apple and (usually) eventually trickle down.
Building cross-platform apps could be done in Objective-C, but other languages are much better suited for the task. (I'm not so much of a fanboy as to suggest that Objective-C is the best solution for every problem. Use the best tool you have at hand.)
I'm not saying using languages on something other than their "native platform" is bad. I'm just suggesting that if that's what you're going to do, you should be aware of the potential problems and be sure you're convinced that the pros outweigh the cons.

Sounds like the class library is out of date in GNUStep, at least in the version you're using -- [NSAutoreleasePool drain] was added in OS X 10.4 IIRC. I don't know anything about GNUStep though, so I don't know if newer libraries are available.
You can work around the problem by replacing 'drain' with 'release'. They do basically the same thing; the 'drain' method was added for use in a garbage-collected app, because 'release' becomes a no-op in that environment.

In my app main loop using GNUStep:
int main(int argc, const char *argv[])
{
NSAutoreleasePool *pool;
AppController *delegate;
pool = [[NSAutoreleasePool alloc] init];
// ...
[pool release];
return NSApplicationMain (argc, argv);
}

Related

Objective-C Scripting Bridge and Apple Remote Desktop

Trying to automatically view a computer in Apple Remote Desktop via Scripting Bridge in Objective-C with this:
#try {
SBApplication *RD = [SBApplication applicationWithBundleIdentifier:#"com.apple.RemoteDesktop"];
// (code to check for ARD running and installed omitted here)
[RD activate]; // works just fine
RemoteDesktopComputer *computer = [[[RD classForScriptingClass:#"computer"] alloc] initWithProperties:
[NSDictionary dictionaryWithObjectsAndKeys:
ipAddress,#"InternetAddress", // looked up from header
nil
]
];
// attempt to add it to a container first:
[(SBElementArray*)[(RemoteDesktopApplication*)RD computers] addObject:computer];
// this is what raises the exception:
[computer observeZooming:Nil];
}
#catch (NSException *e) {
NSLog(#"Exception: %#", [e description]);
}
Running this yields the following exception in the log:
Exception: *** -[SBProxyByClass observeZooming:]: object has not been added to a container yet; selector not recognized [self = 0x6050004819b3]
I've done as much research as there is available on this subject and have learned that SB isn't the easiest to deal with because of how it's wired under the hood, but any experts or veterans of native Scripting Bridge (no third party frameworks or languages other than obj-c, please) is much appreciated.
All prerequisites like linking to the ScriptingBridge.framework and importing Remote Desktop.h are performed - the typecasts are to avoid what appear to be unavoidable link-time errors when building...
Edit 1: Reading the documentation on SBObject (parent of RemoteDesktopComputer) says that it's a reference rather than an actual instance, which you can fetch by calling SBObject's get method (returns id). So I tried running this as well but unfortunately received the same results:
[[computer get] observeZooming:Nil];
Here's the documentation on SBObject: https://developer.apple.com/library/mac/documentation/cocoa/Reference/SBObject_Class/SBObject/SBObject.html#//apple_ref/occ/instm/SBObject/get
Still trying...
(FWIW, I already had the following How To written up, so I'm leaving it here for future reference.)
How to use AppleScript-ObjC in place of Scripting Bridge
Scripting Bridge is, at best, an 80/20/80 "solution" (i.e. 80% of the time it works, 20% of the time it fails, and 80% of the time you've no idea why). There's little point trying to argue with SB when it breaks on stuff that works perfectly well in AppleScript - the Apple engineers responsible designed it that way on purpose and simply refuse to accept they broke spec [1] and screwed up. As a result, the AppleScript language, for all its other deficiencies, remains the only supported solution that is guaranteed to speak Apple events correctly [2].
Fortunately, since OS X 10.6 there has been another option available: use ObjC for all your general programming stuff, and only call into AppleScript via the AppleScript-ObjC bridge for the IPC stuff.
From the POV of your ObjC code, your AppleScript-based ASOC 'classes' are more or less indistinguishable from regular ObjC classes. It requires a bit of fiddling to set up, and you'll pay a bit of a toll when crossing the bridge, but given the crippled, unreliable nature of the alternatives, it's the least horrid of the supported options for anything non-trivial.
Assuming you've already got an existing ObjC-based project, here's how to add an ASOC-based class to it:
In Targets > APPNAME > Build Phases > Link Binary With Libraries, add AppleScriptObjC.framework.
In Supporting Files > main.m, add the import and load lines as shown:
#import <Cocoa/Cocoa.h>
#import <AppleScriptObjC/AppleScriptObjC.h>
int main(int argc, const char * argv[]) {
[[NSBundle mainBundle] loadAppleScriptObjectiveCScripts];
return NSApplicationMain(argc, argv);
}
To define an ASOC-based class named MyStuff that's callable from ObjC, create a MyStuff.h interface file that declares its public methods:
// MyStuff.h
#import <Cocoa/Cocoa.h>
#interface MyStuff : NSObject
// (note: C primitives are only automatically bridged when calling from AS into ObjC;
// AS-based methods with boolean/integer/real parameters or results use NSNumber*)
-(NSNumber *)square:(NSNumber *)aNumber;
#end
along with a MyStuff.applescript file containing its implementation:
-- MyStuff.applescript
script MyStuff
property parent : class "NSObject"
on square_(aNumber)
return aNumber ^ 2
end square_
end script
Because the MyStuff class doesn't have an ObjC implementation, the linker can't link your ObjC code to it at build-time. Instead, use NSClassFromString() to look up the class object at run-time:
#import "MyClass.h"
...
MyStuff *stuff = [[NSClassFromString(#"MyStuff") alloc] init];
Otherwise it's pretty much indistinguishable from a native ObjC class in normal use:
NSNumber *result = [stuff square: #3];
NSLog(#"Result: %#", result);
HTH
--
[1] Apple management broke up the original AppleScript team shortly after its initial release, causing its designers to quit in response, so a lot of knowledge of precisely how this stuff should work was lost. In particular, a full, formal specification was never produced for application developers to follow when designing their scripting support, so all they could do was use personal judgement and best guesses, then test against AppleScript to check it worked as hoped. Thus, AppleScript's own Apple event bridge is the de facto specification that every single scriptable app has been implemented against in the last twenty years, so the only way that other AE bridges can ever work correctly is if they mimic AS's own bridge down to every last query and quirk - a lesson, unfortunately, that the current AS team have repeatedly failed to understand [2].
[2] JavaScript for Automation's Apple event supported is equally crappy and busted, incidentally.
Scripting Bridge is a defective, obfuscated mess, so when an application command fails to work you've no idea if the problem is SB being defective or the application itself being buggy or simply requiring you to phrase it in a different way.
Therefore, the first step is to write a test script in AS to see it works there. If it does, it's SB that's crap; if not, try fiddling with your AS code (e.g. try phrasing the reference for the at parameter in different ways, or omitting it entirely) till it does.
You should also ask on Apple's AppleScript Users and ARD mailing lists and anywhere else that ARD scripters are likely to hang out, as most apps' scripting documentation is grossly inadequate, so a lot of knowledge of how to do things is word of mouth. (The guy you really want to talk to is John C Welch, aka #bynkii, as he's the guru of ARD scripting.)

Support newer features in older versions of OS X SDK

I've been learning more about Cocoa, Objective-C, and Xcode by contributing to an open-source project (IPMenulet); the project originally supported OS X 10.5.
In my haste, it seems that I have added features using SDK elements (NSJSONSerialization and the #autoreleasepool compiler directive) that aren't supported by the older SDK. Now, I'm trying to determine what if anything I can do to restore support for 10.5
Options:
NSJSONSerialization - I suppose that I could switch to JSONKit
#autoreleasepool {} - ?
#properties - add #synthesize and IVARs
Questions:
is there a way (compiler directives?) to use newer SDK elements if the OS supports it, switching to the older element if necessary? if so, is it better to refactor the functionality in version-specific methods (e.g. getJSONlegacy, getJSON)?
would it be better to mark the original project as a separate branch (to allow it to be enhanced)?
Different features involve different OS components, which defines how a feature can be used in multiple OS X versions. Here is my rough classification:
functionality is completely provided by some framework. For example, NSJSONSerialization is available in Mac OS X 10.7+. You can use the same solution for all OS versions or check at runtime if some functionality is available. For example,
if ([view respondsToSelector:#selector(setAcceptsTouchEvents:)])
[view setAcceptsTouchEvents:YES];
More details regarding multiple SDKs support can be found in SDK Compatibility Guide. Using SDK-Based Development.
functionality is completely provided by compiler. For example, #autoreleasepool, literals.
functionality is provided by compiler and runtime. For example, default #property synthesis. See Objective-C Feature Availability Index for more details.
functionality which depends on SDK against which an application is linked. It is more about behavior changes, such a mechanism is described in Backward Compatibility section in AppKit Release Notes.
And now back to your question. There is a mechanism to check in runtime if a feature is available, pretty often respondsToSelector: can do the job. I recommend to expose a single method which works on all OS versions. And only inside this method differences between OS versions are present. For example,
- (NSString *)base64EncodingForData:(NSData *)data {
NSParameterAssert(data);
if ([data respondsToSelector:#selector(base64EncodedStringWithOptions:)]) {
return [data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];
}
else {
// Manual encoding using <Security/SecEncodeTransform.h> and kSecBase64Encoding.
}
}
You can create some 1.1 maintenance branch, perform all work in master, and merge to maintenance branch only bugfixes. So from maintenance branch you'll release 1.1.1 and from master 1.2. It's a viable approach. But you cannot support Mac OS X 10.5 indefinitely, so you need to decide in which IPMenulet version you'll drop 10.5 support.
To the extent that it helps at all, the classic version of:
#autoreleasepool { ... code ... }
Was:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
... code ...
[pool drain];
drain is preferred to the normal release because it then all works properly with (also now deprecated) OS X garbage collection. But it counts as a release so there's no memory leak and you shouldn't also release.

Error when compliling Objective-C program

I am trying to compile my first Objective-C program. It is just the simple "Programming is fun!" code:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog (#"Programming is fun!");
[pool drain];
return 0;
}
I downloaded the gnustep-msys-system-0.30.0-setup.exe and gnustep-core-0.34.0-setup.exe and installed them in that order. I copied the file named prg1.m (which contain the previous code) to the /GNUstep/msys/1.0/home/username folder. I opened the GNUstep shell and proceeded to issue the following command:
gcc -o prg1 prg1.m -I /GNUstep/System/Library/Headers -L /GNUstep/System/Library/Libraries -lobjc -lgnustep-base -fconstant-string-class=NSConstantString
I get the following output:
In file included from C:/GNUstep/GNUstep/System/Library/Headers/GNUstepBase/GSCo
nfig.h:281:0,
from C:/GNUstep/GNUstep/System/Library/Headers/GNUstepBase/GSVe
rsionMacros.h:219,
from C:/GNUstep/GNUstep/System/Library/Headers/Foundation/Found
ation.h:30,
from prg1.m:1:
c:\mingw\include\w32api.h:27:2: warning: #warning WARNING - The w32api.h header
file is deprecated and will be removed. [-Wcpp]
#warning WARNING - The w32api.h header file is deprecated and will be removed.
^
In file included from C:/GNUstep/GNUstep/System/Library/Headers/Foundation/NSPor
tMessage.h:30:0,
from C:/GNUstep/GNUstep/System/Library/Headers/Foundation/Found
ation.h:99,
from prg1.m:1:
C:/GNUstep/GNUstep/System/Library/Headers/Foundation/NSPort.h:210:3: error: unkn
own type name 'WSAEVENT'
WSAEVENT eventListener;
^
Anyone know why I am getting this error at the end? I have been googling it for a couple of hours without much luck. Any help would be greatly appreciated. Thanks in advance.
The issue you have seems to be in the foundation/foundation.h library you downloaded. The real question here is it seems you are using a PC as you downloaded a .exe thus I wonder, why are you programing in Objective-C? The only platform that still uses Objective-C is the iOS/Apple platform which you really can not effectively code for with out an Apple computer. If you just want to learn Objective-C because you plan to get into iOS soon then I understand but if you do not plan to do this then there is no reason to learn Objective-C, you are better off learning Java, C# or C++.
The more specific answer to your question is that WSAEVENT is a signal used in windows threads. Chances are somehow your system is not finding the support for it correctly it may be because the w32api.h was removed which had the reference to where the WSAEVENT was located.

What is a non-fragile ABI?

It may seem implied that everyone knows what a "Non Fragile ABI" is - considering the frequency and matter-of-fact-nature to which it is referred to - within Xcode. For example...
Subscript requires size of interface node which is not constant in non-fragile ABI
or
Select the Objective-C ABI version to use. Available versions are 1 (legacy "fragile" ABI), 2, (non-fragile ABI 1), and 3 (non-fragile ABI 2).
That said... What is a non-fragile ABI? (and why isn't it called something less-abstract / explained more clearly?)
The non-fragile ABI refers to the ability to add instance variables to a class without requiring recompilation of all subclasses.
I.e. in v1 (there really aren't true versions of ObjC), if Apple were to add an instance variable to, say, NSView (on Cocoa, 32 bit), then every subclass of NSView (or subclass of subclass) would have to be recompiled or they would blow up. v2 and v3 fix this.
It is explained in detail in this weblog post.
The documentation you are referring to is in the llvm/clang man page. Pretty rare place to be for most developers most of the time; unless you are writing a Makefile that is driving the compiler directly, there isn't much reason to read that page (unless spelunking -- which is quite educational, of course).
It is written in the style of a Unix man page and, no surprise, is a bit... obtuse. For almost all tasks, it is best to stick to the higher level documentation. I.e. the Xcode build settings documentation is general quite a bit less obtuse.
After some poking around, one of the best summaries / pieces of advice on the subject is the following…
The non-fragile ABI allows for things like changing the ivars of a superclass without breaking already compiled subclasses (among other things). It's only supported on 64-bit on the Mac though, because of backwards compatibility concerns which didn't allow them to support it on existing 32-bit architectures.
It goes on to say, basically.. that if Xcode, which often is configured to build for the "Active Architecture Only", aka 64-bit only.. one may run into issues when switching to a "Release" scheme, which is usually set to build for both (63bit/32bit) architectures, aka "Universal"..
You may you want to use ARC on the Mac, I'm pretty sure you'll have to drop 32-bit support to do so. You can change the targeted architectures in the build settings for your target in Xcode.
In my own experience, I believe that what the non-fragile ABI benefits us with is an abbreviated syntax, and patterns such as…
// source.h - readonly public properties.
#interface SuperClassy : NSObject
#property (readonly) NSArray *cantTouchThis;
#end
// source.m set readonly properties, internally.
#implementation SuperClassy
// look, no #synthesize… just use _ivarName.
-(void) touchIt:(NSArray*)a { _cantTouchThis = a; }
#end
int main(int argc, const char * argv[]) {
SuperClassy *it = [SuperClassy new];
// you cannot set it.cantTouchThis = #[anArray].
[it touchIt:#[#"cats"]];
// but you can via a method, etc.
NSLog(#"%#", it.cantTouchThis);
}
NSLOG ➜ ( cats )

Beginner: Difference between Xcode 3 and Xcode 4 specifically NSfunctions

I'm learning objc and Xcode from a handy free ebook called How To Become and Xcoder, which is quite handy. Except it was written in 2007 with Xcode 3 and its samples are all from that version unfortunately I have OSX Lion and thus Xcode 4. So to the grit of my question. They provide a sample block of code as seen here:
//start
#import <Foundation/Foundation.h
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(#"Hello, World!");
[pool drain];
return 0;
}
//end
So my problems are that I get over 20 errors and Xcode doesn't recognize the NSAutoreleasePool or NSLog commands.
Does anybody know why this won't work? I already added the Foundation framework.
The I've already realized that the printf command works better than the NSLog command (which to my knowledge is more so used for error reporting) so yeah any help would be nice.
If NSLog isn't being recognised, this isn't a problem to do with Xcode 3 versus Xcode 4. Your code isn't including the most basic Objective-C runtime code. I would assume that it's being compiled as C not Objective-C.
You say that you had to add the Foundation framework - this indicates that you didn't start your project using an Objective-C-based template. When you select New Project..., which template did you pick? Any of the iOS application templates should work, and most of the Mac OS X application templates should work. If you chose to build a command line tool, you should pick Foundation as the type.
Edit: Also, I should add that the syntax for autorelease pools has changed in the latest version of Xcode, as ARC is used by default. You can either switch ARC off, or read up on it to see what the differences are. Chances are you'll find it easier with ARC as there is much less memory management for you to worry about, but you will have to bear it in mind if you are following a book that doesn't account for it.
The Foundation is located on the screen where you need to give name to your project. As the default value it is set to C, in your case you need to click on the drop down menu and choose Foundation instead.
Good luck.