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 )
Related
I have a protocol that has a method returning NSArray*.
In the implementation I had made the return type of that method to be NSView*
I see this is happening only in case of Objective C class pointers and not in other cases like returning void vs returning int.
I would expect a complier warning at the minimum but the compilation happens just fine.
#protocol prot <NSObject>
-(NSArray*)array;
#end
#interface impl : NSObject<prot>
#end
#implementation impl
//Should return NSArray. Returns NSView instead
- (NSView *)array
{
return nil;
}
#end
First things first:
impl should be Implementation since class names are written in upper camel case and abbreviations are bad(TM). Moreover, Class is a class pointer, NSView* and NSArray* are instance pointers.
To your Q, even I'm a bit tired of this discussion (dynamic vs. static typing, early vs. late binding):
A: Why should the compiler warn? Both are instance pointers and maybe the messages sent to the object are supported by both. The compiler does not care about binding, it is done at runtime.
B: But this is very unsafe!
A: Did you ever ship code with such an error?
B: No. But it is unsafe by theory.
A: Yes, that's true for alle theories that ship code without running it at least one time.
B: But you have to admit, that this is more unsafe than type checking at compile time.
A: Yes, theoretically that's true.
B: So why do you support it?
A: Because there are many situations in which dynamic typing has advantages. I. e. it is very easy to write generic code without having templates. (Even sometimes they are called generics, they are still silly templates.) It is very easy to give around responsibility, what needs contra-conceptual extensions in other languages (signals & slots in C++, delegates in C#, …) It is very easy to create stand-in objects for lowering memory pressure. It is very easy to write an ORIM. Shall I continue?
B: Yes
A: Is is that flexible that you can write a whole AOP framework within that language. It is that flexible that you can write a prototype based framework within that language.
However, sometimes it is easy to detect for the compiler that something makes no sense at all. And sometimes the compiler warns about that. But in many cases the compiler is not more intelligent than the developer.
Agreed that it should generate a warning, but it doesn't. Part of the issue is that all ObjC objects are id at runtime, which is why you're seeing different behavior for int (which isn't id). But that's not really an excuse. It's a limitation of the compiler. There are numerous places where it doesn't do a good job of distinguishing between ObjC object types. ObjC objects are duck-typed, so as long as they respond to the right messages "they work."
Sometimes this is a benefit; for example, NSArray is actually a class cluster, and there are several (private) types that pretend to be NSArray by just implementing the same interface. That's something that is easy in ObjC, but hard in Swift. Still no excuse, since it would be easy to get that benefit without this frustrating lack of a compiler warning, but it gets back to how ObjC thinks about class types.
This limitation is fixed in Swift, and another benefit of moving over, but that doesn't really help you, I know.
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.)
While working on on open source project, I came across the following C function declaration and implementation:
// FSNData.h
NSString *stringForMimeType(MimeType type);
#interface FSNData : NSObject
// All the expected objective-c property and instance method declarations
#end
// FSNData.m
#import "FSNData.h"
// where 'type' is an enum
// this does work as expected
NSString *stringForMimeType(MimeType type) {
switch (type) {
case MimeType_image_jpeg: return #"image/jpeg";
case MimeType_image_png: return #"image/png";
default:
NSLog(#"ERROR: FSNData: unknown MimeType: %d", type);
// do not return "application/octet-stream"; instead, let the recipient guess
// http://en.wikipedia.org/wiki/Internet_media_type
return nil;
}
}
#implementation
// all properties and methods defined in FSData.h implemented as expected
#end
This example could easily be re-written as a class level method with out any problem. As it is, using stringFormMimeType() sill requires importing the FSNData header file anyway.
Looking at the Apple docs, it states only:
Because Objective-C rests on a foundation of ANSI C, you can freely
intermix straight C code with Objective-C code. Moreover, your code
can call functions defined in non-Cocoa programmatic interfaces, such
as the BSD library interfaces in /usr/include.
There is no mention of when C functions should favour Objective-C methods.
The only benefit I can see at this point, is that calling the above function, as opposed to a class method, some Objective-C runtime call(s) would be skipped. In a typical use case of FSNData, this would not give a noticeable boost in performance to the user (probably even to developers)*.
What benefit exists (other than coding style) for favouring a C function over a class method?
*FSNData is used as part of the FSNetworking library, so I doubt there would be thousands upon thousands of network operations being performed during any application's life cycle.
In short, C (or C++) implementations are very useful:
For Abstraction
For Reusability
When making medium and large scale programs
In performance critical paths
For 'Interior' implementations
What benefit exists (other than coding style) for favouring a C function over a class method?
ObjC messaging introduces indirect function calls. These are firewalls for optimizers.
C functions can easily restrict access, whereas 'private' ObjC implementations may be looked up using the ObjC runtime, or accidentally overridden.
C functions may be removed from your executable if not referenced, or they may be made private. If you write reusable code (and you should), this can have a huge impact on your binary sizes and load times -- C functions which are not referenced/used may be removed, but ObjC types and methods will be preserved (including everything they reference). This is why your app's binary size may grow significantly when you use only small part of an ObjC static library -- every objc class in the library is preserved. If that library were C or C++, then you could get by with very small growth because you need only what is referenced. What is or is not referenced is easier to prove with C and C++.
C functions can be inlined, either during compilation or during Link Time Optimization phases.
The compiler and optimizers are able to do much optimization with C functions (e.g. inter-procedural optimizations), but very little with ObjC methods because they are always indirect.
To avoid ObjC message dispatch overhead (as you mentioned)
Potential for additional reference counting operations and autorelease pool activity when interacting with ObjC objects.
Of course you won't always hurt paying for things you don't need or use -- and remember that ObjC class methods have some benefits over C functions, too. So, just look at C or C++ implementations as another tool in your toolbox. I find them very useful as complexity and project sizes increase, and they can be used to make your programs much faster. Just do what you are least likely to regret in 2015 ;)
You already touched on the marginal performance difference of avoiding an objc_msgSend call. Objective-C class methods are also subject to overriding in subclasses, so implementing a method in C will prevent it from being overridden in a subclass. Relatedly, because of that runtime inheritance/polymorphism, an Objective-C method can never be inlined, whereas a C function can potentially be inlined by the compiler for added performance.
When it comes to avoiding objc_msgSend, a wise man once told me, "If the overhead of objc_msgSend is too great for you, Objective-C is probably the wrong tool for the job."
I've just started learning Obj-C and i'm a little confused. The videos I've been watching on Lynda.com were created with Xcode 4, but there are so many differences that I find it hard to believe that all of them occurred in 2 point releases. For instance:
In the video you could write:
#property NSString * myString
And it would be fine, but now in 4.2 it throws an error unless you write something like:
#property (nonatomic, retain) NSString * myString
In addition, there are no longer init or dealloc methods in the implementation code by default and NSAutoReleasePool is implemented completely differently. What gives?
While I can't guarantee that this list is exhaustive, the differences you'll find on the net are:
Objective-C 1.0 or 2.0
Old or modern runtime
Manual or automatic reference counting
My personal take on the main differences is:
Objective-C 2.0 brought properties and synthesized accessors among other things
The modern runtime has a different way of organizing instance variables (non-fragile instance variables), but you probably won't notice in day-to-day development work
The modern runtime also allows 64-bit apps if the OS supports it
Automatic reference counting lets you do away with retain/release code at the modest cost of following the coding and naming conventions
There are more differences, but these are the most important ones as I see it - personally I rarely have to use autorelease pools, and if I understand correctly the new syntax does not change the functionality.
If you create a project with "automatic reference counting" option "on" then there wouldn't be any init or dealoc methods.
When creating project
CHECK the Use Automatic Reference Counting.
When creating a project you can check the option "Use Automatic Reference Counting". If you do check this, then there won't be any init or dealloc methods, because Xcode automatically does the reference counting.
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);
}