I am testing some simple Objective-C code on Windows (cygwin, gcc). This code already works in Xcode on Mac. I would like to convert my objects to not subclass NSObject (or anything else, lol). Is this possible, and how?
What I have so far:
// MyObject.h
#interface MyObject
- (void)myMethod:(int) param;
#end
and
// MyObject.m
#include "MyObject.h"
#interface MyObject()
{ // this line is a syntax error, why?
int _field;
}
#end
#implementation MyObject
- (id)init {
// what goes in here?
return self;
}
- (void)myMethod:(int) param {
_field = param;
}
#end
What happens when I try compiling it:
gcc -o test MyObject.m -lobjc
MyObject.m:4:1: error: expected identifier or ‘(’ before ‘{’ token
MyObject.m: In function ‘-[MyObject myMethod:]’:
MyObject.m:17:3: error: ‘_field’ undeclared (first use in this function)
EDIT My compiler is cygwin's gcc, also has cygwin gcc-objc package:
gcc --version
gcc (GCC) 4.7.3
I have tried looking for this online and in a couple of Objective-C tutorials, but every example of a class I have found inherits from NSObject. Is it really impossible to write Objective-C without Cocoa or some kind of Cocoa replacement that provides NSObject?
(Yes, I know about GNUstep. I would really rather avoid that if possible...)
EDIT This works:
// MyObject.h
#interface MyObject
#end
// MyObject.m
#include "MyObject.h"
#implementation MyObject
#end
Not very useful though...
It's possible to make classes without a base class. There are a couple of things going on. First, your compiler doesn't seem to like the "()" class extension syntax. Other compilers would be OK with it. If you remove those "()" on line four of MyObject.m then your compiler will complain that you've got two duplicate interfaces for the MyObject class. For the purpose of your test you should move that _field variable into the declaration of MyObject in the header file, like:
#interface MyObject {
int _field;
}
-(void)myMethod:(int)param;
#end
Then you can completely remove that extra #interface in the .m file. That should get you started at least.
It's possible, but note that NSObject implements the memory allocation API in objective-c, and if you don't implement NSObject's +alloc and -dealloc or equivalent on a root class, you'll still need to implement the same functionality for every class.
Related
I am trying to learn objective c on windows. My program compiles with warnings
My code is
#include <objc/Object.h>
#interface Greeter:Object
{
/* This is left empty on purpose:
** Normally instance variables would be declared here,
** but these are not used in our example.
*/
}
- (void)greet;
#end
#include <stdio.h>
#implementation Greeter
- (void)greet
{
printf("Hello, World!\n");
}
#end
#include <stdlib.h>
int main(void)
{
id myGreeter;
myGreeter=[[Greeter alloc] init];
[myGreeter greet];
[myGreeter release];
return EXIT_SUCCESS;
}
I compile my program on GNUStep using the following command
gcc -o Greeter Greeter.m -I /GNUstep/System/Library/Headers -L /GNUstep/System/Libra
/Libraries -lobjc -lgnustep-base -fconstant-string-class=NSConstantString
I get the following warnings on compilation
: 'Greeter' may not respond to '+alloc' [enabled by default]
: (Messages without a matching method signature [enabled by default]
: will be assumed to return 'id' and accept [enabled by default]
: '...' as arguments.) [enabled by default]
: no '-init' method found [enabled by default]
: no '-release' method found [enabled by default]
And so when I run my executable the object does not get instantiated.
I am using gcc from MinGW where gcc version is 4.6.2
--UPDATE---
The program runs fine when I extend from NSObject instead of Object
--UPDATE 2 ----
My Object.h looks like
#include <objc/runtime.h>
#interface Object
{
Class isa;
}
#end
--UPDATE 3 ----
I have modified my code as follows. It compiles fine, but I am not sure if this is the right way to go about things
#interface Greeter
{
/* This is left empty on purpose:
** Normally instance variables would be declared here,
** but these are not used in our example.
*/
}
- (void)greet;
+ (id)alloc;
- (id)init;
- release;
#end
#include <stdio.h>
#implementation Greeter
- (void)greet
{
printf("Hello, World!\n");
}
+ (id)alloc
{
printf("Object created");
return self;
}
- (id)init
{
printf("Object instantiated");
return self;
}
- release {}
#end
#include <stdlib.h>
int main(void)
{
id myGreeter;
myGreeter=[[Greeter alloc] init];
[myGreeter greet];
[myGreeter release];
return EXIT_SUCCESS;
}
Unless you are studying the history of Objective-C, trying to learn the language based on the Object class is a complete waste of time. The Object class was last used commonly as a root class in pre-1994 NEXTSTEP.
If your goal is to learn pre-1994 Objective-C, then state that because, if so, the answers you have so far are entirely wrong. Even if the goal is to go with modern patterns, the answers are more along the lines of How do I recreate NSObject? than anything else. Note that if that is your goal.... well... go for it! Pre-1994 Objective-C was kinda like OOP macro-assembly and, through that, there was a ton of power through at the metal simplicity.
For example, you say that "I have modified my code as follows. It compiles fine, but I am not sure if this is the right way to go about things".
That code compiles, but -- no -- it doesn't work. Not at all. For starters, the +alloc method doesn't actually allocate anything. Nor does the Greeter class implement near enough functionality to act anything like an NSObject.
If your goal is to learn something akin to modern Objective-C and use Windows to do so, the best possible way would likely to be to install the GNUStep toolchain. With that, at least, you would be programming against an NSObject rooted set of APIs akin to modern Cocoa (and, to a lesser extent, iOS).
If your goal is to learn truly modern Objective-C, you'll want an environment that can run the latest versions of LLVM, at the very least. And, of course, if you want to write Objective-C based iOS or Mac OS X apps, you'll want a Mac running Lion.
From memory, the Object class does not implement retain counts, so it wouldn't have release, it'll have free or some other method. It should have +alloc and -init though. Since there's no “Objective-C standard”, you'll have to open up your objc/Object.h and see exactly what it offers.
Note that on GCC 4.6.2, objc/Object.h actually includes objc/deprecated/Object.h, meaning support for Object as a class may be fairly limited. If it doesn't include it, try including it yourself:
#import <objc/deprecated/Object.h>
Import Foundation.
#import <Foundation/Foundation.h>
Extend NSObject instead of Object.
#interface Greeter : NSObject
What I did was to install gcc-4.6 alongside the 4.7 that came with the linux system.
It seems to work, as it has a compatability layer for older code.
In my basic Makefile I specify
> CC=gcc-4.6
> LIBS=-lobjc -lpthread
>
> all: objc-test.m
> $(CC) -o objctest objc-test.m $(LIBS)
There is nothing "wrong" with using and older version of gcc.
The new 4.7 version has gutted
the objc system so it is not a stand-alone compilation suite. That sucks. I imagine there is some reason, possibly a political one, possibly just that it is difficult to make one compiler do it all for everyone. I have successfully made objc programs with gnustep in X86_64 Linux with gcc 4.7.3 after banging out failure for two days the old way.
It involves a bunch of setup:
setting up the environment variables with
source /usr/share/GNUstep/Makefiles/GNUstep.sh
and conforming to their build system.
A Basic GNUmakefile:
include $(GNUSTEP_MAKEFILES)/common.make
TOOL_NAME = test
test_OBJC_FILES = main.m
include $(GNUSTEP_MAKEFILES)/tool.make
for
main.m:
#import <Foundation/Foundation.h>
int
main (void)
{
NSLog (#"Executing");
return 0;
}
running
gs_make
builds the binary in a subdir obj.
It is actually quite frustrating to fight with the build system like that
and have to spend hours teasing out tidbitsfrom docs just to get basic functionality
from such a great compiler. I hope they fix it in coming iterations.
Have you tried with [Greeter new]; ? Open Object.h and take a look at the methods defined in the Object class...
EDIT:
To implement the alloc, retain and release you have to call the objc runtime.
So.. I think you have to write something like this:
#interface RootObject : Object
+ (id)alloc;
- (id)init;
- (id)retain;
- (void)release;
#end
#implementation RootObject
{
unsigned int retainCount;
}
+ (id)alloc
{
id myObj = class_createInstance([self class], 0);
/* FOR NEWBIES this works for GCC (default ubuntu 20.04 LTS)
id myObj = class_createInstance(self, 0);
*/
return myObj;
}
- (id)init
{
retainCount = 1;
return self;
}
- (id)retain
{
retainCount++;
return self;
}
- (void)release
{
retainCount--;
if (retainCount == 0) {
object_dispose(self);
}
}
#end
And then you can subclass RootObject.
Edit: I just noticed this other Stack Overflow question asking much the same thing: Why does a subclass #property with no corresponding ivar hide superclass ivars?
This is some interesting behavior that I cannot find documented in anything official or unofficial (blog, tweet, SO question, etc). I have boiled it down to its essence and tested this in a fresh Xcode project, but I can't explain it.
MyBaseClass has an instance variable:
#interface MyBaseClass : NSObject {
NSObject *fooInstanceVar;
}
#end
MySubclass extends MyBaseClass, and declares a totally unrelated property (that is, the property is not intended to be backed by the instance variable):
#import "MyBaseClass.h"
#interface MySubclass : MyBaseClass { }
#property (nonatomic, retain) NSObject *barProperty;
#end
If the implementation of MySubclass does not synthesize the property but implements the accessor methods, everything is fine (no compiler error):
#import "MySubclass.h"
#implementation MySubclass
- (NSObject*)barProperty {
return [[NSObject alloc] init]; // pls ignore flagrant violation of memory rules.
}
- (void)setBarProperty:(NSObject *)obj { /* no-op */ }
- (void)doSomethingWithProperty {
NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
NSLog(#"%#", array);
}
#end
But if I remove the property accessor methods and replace them with a synthesize declaration for the property, I get a compiler error: 'fooInstanceVar' undeclared (first use in this function).
#import "MySubclass.h"
#implementation MySubclass
#synthesize barProperty;
- (void)doSomethingWithProperty {
NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
NSLog(#"%#", array);
}
#end
This error goes away if I remove either the synthesize declaration, or if I do not refer to the fooInstanceVar instance variable from within MySubclass.m, or if I put all interface and implementation definitions in a single file. This error also seems to happen in both GCC 4.2 and GCC/LLVM build settings.
Can anyone explain what's happening here?
As replied in this question : objective c xcode 4.0.2: subclass can't access superclass variables "was not declared in this scope"
From the doc : Apple Objective-C Programming Langage :
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF125
The instance variable is accessible within the class that declares it and within classes that inherit it. All instance variables without an explicit scope directive have #protected scope.
However, a public instance variable can be accessed anywhere as if it were a field in a C structure. For example:
Worker *ceo = [[Worker alloc] init];
ceo->boss = nil;
I have the compilation error using LLVM GCC 4.2 (for an iOS project, on device) :
error: 'fooInstanceVar' undeclared (first use in this function)
and the same one using GCC 4.2 :
error: 'fooInstanceVar' undeclared (first use in this function)
I can compile using LLVM Compiler 2.0 whithout error.
For compiling with LLVM GCC 4.2 and GCC 4.2 with the use of self-> :
[NSArray arrayWithObjects:self.barProperty, self->fooInstanceVar, nil];
in the doSomethingWithProperty method.
The compiler is behaving correctly; synthesis in a subclasss using storage in a superclass is verboten.
There was a bug about this filed against llvm at some point. It may be in the publicly accessible bug database.
In any case, please file a bug asking for clarification of this particular rule.
I just tried this and it compiles without warning. What am I not doing?
#interface MyBaseClass : NSObject {
NSObject *fooInstanceVar;
}
#end
#interface MySubclass : MyBaseClass { }
#property (nonatomic, retain) NSObject *barProperty;
#end
#implementation MyBaseClass
#end
#implementation MySubclass
#synthesize barProperty;
- (void)doSomethingWithProperty {
NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
NSLog(#"%#", array);
}
#end
It isn't clear what problem you are trying to solve. All instance variables are non-fragile everywhere but 32 bit Mac OS X.
I can't reproduce your error either. Do you have a non-default compiler flag set? Could you provide a copy of your project? It definitely appears to be a bug in the compiler.
Check out this article here for the best use of #property/#synthesize. A quick summary is to remove all of your ivars from your objects (unless you need to use the 32-bit runtime for some reason). Then only use your getters and setters, rather than accessing the synthesized ivars directly. Following this will avoid any future problems with this bug.
I just came across Clang/LLVM today, and decided to try it out.
My system is FreeBSD8.1-Release.
The default system compiler is GCC4.2.1, which is what I have been using to compile my Objective-C project up until now.
I'm playing around with the Static Analyzer, and would like to know how to eliminate one of the warnings that is being generated.
MyClass.h
#import <objc/Object.h>
#interface MyClass: Object {
}
-(MyClass*) init;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
-(MyClass*) init {
self = [super init];
if (self) {
// do stuff
}
return self;
}
#end
The warning:
%clang --analyze MyClass.m
MyClass.m:7:9: warning: method '-init' not found (return type defaults to 'id')
self = [super init];
^~~~~~~~~~~~
1 diagnostic generated.
I take it that the analyzer does not know how to determine super's type (Object, in this case). Is there any way to eliminate this warning (other than by suppression)? I looked into casting super, but it looks like that is not allowed.
Thanks!
Max
Update
Thanks to Dave and bbum for pointing me in the right direction for eliminating the warning. Now I'm trying to figure out why the warning occurs in the first place.
If anyone has any ideas or leads, I love the hear them.
Thanks,
Max
Two issues:
You should be inheriting from NSObject, not Object.
Your init method should return id, not MyClass*.
The warning is saying that it does not know of any method named -init at all in scope at that point. You need to import a header file that declares -init, which is probably Foundation.h or something, depending on what system you're using.
The following seems simple enough. There's a superclass with an ivar, and a subclass which accesses the (#protected) superclasses ivar:
// Testclass.h
#interface TestClass : NSObject {
NSString *testIvar;
}
#end
//TestClass.m
#implementation TestClass
#end
//TestSubclass.h
#interface TestSubClass : TestClass {
}
#property (nonatomic, retain) NSString *testProperty;
- (void) testMethod;
#end
//TestSubclass.m
#import "TestSubClass.h"
#implementation TestSubClass
#synthesize testProperty;
- (void) testMethod{
NSLog(#"The value was: %#", testIvar);
}
#end
Simple and correct-seeming enough. However, attempting to compile (for iOS 4.2 SDK, with GCC 4.2) produces this error pointing to the NSLog line: 'testIvar undeclared'.
I'm new to Objective-C, but can't for the life of me see why this should be an error. Comment out the testProperty stuff, and it compiles OK. It seems like adding a synthesized property in a subclass, without a corresponding ivar, is actually hiding an unrelated superclass ivar.
Can anyone enlighten me as to what's happening here? Relatedly, was the compilation error foreseeable? (Foreseeing it would have saved me some time and frustration).
LLVM compiles the source without complaints, switch to LLVM: Select target → Get Info → Build → C/C++ Compiler Version → LLVM 1.5. From my limited experience it’s a better compiler anyway. No idea why GCC behaves the way it does – interesting catch.
The testIvar undeclared error is actually red herring in this case. This message seems to be caused by testProperty not having a corresponding ivar. To resolve the issue either declare a testProperty ivar in TestSubClass.h or make testProperty #dynamic in TestSubClass.m.
I have the following objective-C++ header with the simple method to return this pointer.
#interface MyObj
{
MyCPPObj * cpp;
}
-(MyCPPObj *) getObj;
I have created the simple method
#implementation MyObj
-(MyCPPObj *) getObj
{
return cpp;
}
Everything seems to work until I actually try to use the object in another file
newObj = [createdMyObj getObj];
It complains: error: cannot convert 'objc_object*' to 'MyCPPObje *' in initialization.
It seems that the method is return an objective-c object, but I specifically requested a C++ pointer.
MyCPPObj is an honest C++ class:
class MyCPPObj
{
public:
int x;
}
How can I fix that?
On my 10.6.3 machine, the following combination worked without any problem: aho.h
#import <Foundation/Foundation.h>
class MyCPPObj{
};
#interface MyObj:NSObject
{
MyCPPObj * cpp;
}
-(MyCPPObj *) getObj;
#end
and aho.mm
#import <Foundation/Foundation.h>
#import "aho.h"
void foo(){
MyObj*objcObj=[[MyObj alloc] init];
MyCPPObj*cppObj=[objcObj getObj];
}
Two pitfalls you might have fallen into:
Unlike C++, a class in Objective-C which doesn't inherit from NSObject won't work. (Well, you can make it work, but you don't want that usually.) Note the line #interface MyObj:NSObject.
To use NSObject, do #import <Foundation/Foundation.h>
Don't forget to use the extension .mm for Objective-C++ files.
Most likely you have forgotten to #import the header file with the #interface into the .mm file where you use getObj.
The error states what happens, and JeremyP is right on the money. When you forget to include a header file with the prototypes of the selectors, the compiler assumes the selector returns an object of type id. Well id is a typedef to objc_object*, which is incompatible with your C++ class. To fix the error, you simply need to include your header file in the file where you called getObj.