I'm very new in the world of xcode and Objective- C- Programming. Right now I'm learning programming via "Objective C- Programming: The big Nerd Ranch Guide". Because of an older OSX-Version, I was just able to install xcode 3.2.6. But the book uses the newest xcode version.
while going through the chapters, I faced a problem:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool{
NSDate *now = [NSDate date];
NSLog(#"The date is %#", now);
}
return 0;
}
this code sample gives me following error:
"expected expression before #-token"
While searching for a solution in web, I found out that it's a new syntax to xcode 4... I didn't know that there are so major differences between 3.2.6 and the newest version. Now my question: Does that mean all the syntax in xcode 4 has changed to the previous versions and the book is senseless for me? Or is it just this statement? (If yes, how to write in older versions? I even don't know what that statement is good for since I'm a bloody beginner)
You're confusing Xcode (the IDE), with the SDK. The #autorelease pool annotation was added in the iOS 5 SDK, which Xcode 4 happens to give you. If you want this to run in Xcode 3.x you need to make sure you are running it with the iOS 5 SDK.
In a word, YES.
Apple, since they pretty much own the entire stack, is free to change the language at whim, and 3.0 to 4.0 have some changes. I really would not waste my time trying to write IOS programs in 3 at this point personally.
The API's for the classes have changed with iOS as well between 3 and 4 and 4 and 5.
I would really suggest, upgrading your Mac to something that will support at least XCODE 4 at this point.
Replace #autoreleasepool {} with this code:
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSDate *now = [NSDate date];
NSLog(#"The date is %#", now);
[pool release];
The message you get refers to a new feature of Objective-C that is known as ARC and is meant to simplify memory management. It is available on Apple ObjC compilers starting with Xcode4.
You can still use the book, but you should remove all ARC-related statements (this is not only #autoreleasepool), and in practice it will not be easy because you will also need to add memory management.
Related
I am (re)building a project written in objective c in XCode 3.2.6 64 bit under a 10.6.8 VM based on the 10.5 SDK (for compatibility with 10.5 and up - so this is a given.) The build is up and working. But. I'm trying to address the last four warnings in this project. They're all the same warning. Specifically...
In the project, there are four ASCII c strings that need to be converted to four corresponding instances of objective c's NSString. There are four essentially identical cases. This is how it's being done:
[tf setStringValue: [NSString stringWithCString: strg]]
This works, but results in (four) warnings that stringWithCString is deprecated and looking further, I find that's been true since about 10.4. So I'd expect the 10.5 SDK to have whatever replacement is required.
Looking at the docs, the suggested replacement is:
[tf setStringValue: [NSString stringWithCString:NSASCIIStringEncoding: strg]]
However, when this is used, XCode says:
'NSString' may not respond to '+stringWithCString::'
Which probably means it really won't respond. And besides, even if it does, replacing one warning with another.... yech.
Anyone know what I should be doing differently? I realize that this is old, old stuff, but surely Back In The Day people didn't just let these warnings clutter up their builds? Have I just got some kind of syntax error here, or... ?
[NSString stringWithCString:NSASCIIStringEncoding: strg]
The correct syntax for calling this method is:
[NSString stringWithCString:strg encoding:NSASCIIStringEncoding]
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.)
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.
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.
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);
}