Overriding methods in Objective-C - objective-c

I am relatively new to Objective-C, and I have been thinking about the fact that all methods are, in effect, virtual. I created this little console program:
#import <Foundation/Foundation.h>
#interface BaseClass : NSObject
{
}
- (void) virtualMethod: (NSInteger) integer;
#end
#interface DerivedClass : BaseClass
{
}
- (void) virtualMethod: (NSString *) string;
#end
#implementation BaseClass
- (void) virtualMethod: (NSInteger) integer
{
NSLog(#"%ld", integer);
}
#end
#implementation DerivedClass
- (void) virtualMethod: (NSString *)string
{
NSLog(#"%#", string); // Program received signal: "EXC_BAD_ACCESS". -- as expected
}
#end
int main (int argc, const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *arr = [NSArray arrayWithObjects: [BaseClass new], [DerivedClass new], nil];
for (int i = 0; i < arr.count; i++)
{
BaseClass *b = (BaseClass *)[arr objectAtIndex: i];
[b virtualMethod: i];
}
NSLog(#"\n\nTapos na!\n\n");
[pool drain];
return 0;
}
As expected, I got an EXC_BAD_ACCESS in the derived virtualMethod:, since after all, it doesn't take an integer, it takes an NSString * as parameter. The virtual mechanism is based on selectors, and doesn't seem to take signatures into account.
My question: is there something in the language that could prevent such an override with a different signature from happening? Some way to tell the compiler that virtualMethod: should always have the same signature, or to make the compiler issue some kind of hint, warning or error if the signature doesn't match?
I know that a good programmer always indicates the names of types a method should have, but that is a convention, not a language rule. I am asking about a compiler feature to prevent the problem from happening.

Well, there is... but you probably don't want to use it. There's a build setting in Xcode called "Strict Selector Matching" (which passes through to the compiler as -Wstrict-selector-match). This will warn you if the compiler finds two selectors that have different parameter or return types.
Unfortunately, the warning comes up even if the types are different, but compatible. As such, if you turn it on, you'll get a bunch of spurious warnings places that you wouldn't expect to be ambiguous. Feel free to try it out, though.
If you'd like to read more about it, Matt Gallagher wrote up a nice post about it. In the meantime, I'm afraid there's not a great solution here.

The current version of XCode gives you the following warning:
"Conflicting parameter types in implementation of '[method]': '[the type needed]' vs '[the type you wrongly chose]'"

Related

How to properly code "init" method in Objective-C, prevent "Expression result unused" warning

I was given a main by my professor that I have to make the .h and .m files for in Objective-C using Xcode. I have it almost done but have two warnings that I can't figure out how to get rid of, "Expression result unused". The errors have to do with my init method for making an array of size 100. Here is where the main creates an object and then calls the method I have coded improperly, this part is given from my teacher and I can't change it:
// main.m
// ChutesAndLadders
#import <Foundation/Foundation.h>
#import "ChutesAndLadders.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
ChutesAndLadders *cl = [[ChutesAndLadders alloc]init];
[cl initBoard];
[cl makeChutes:10];
[cl makeLadders:10];
int chutes=0;
int ladders=0;
for(int i=0;i<cl.board.count;i++){
NSString * cell = (NSString *)[cl.board objectAtIndex:i];
int additionalSpaces = (int)[cl addToMove:cell];
if(additionalSpaces>0)
ladders++;
else if (additionalSpaces<0)
chutes++;
}
[cl printBoard];
}
return 0;
}
Here is relevant parts of my method:The error is from the "initBoard" method above, and "initWithCapacity" line below. I know that the two warnings are describing the same error. The code below is mine so I can change it, unlike above.
#synthesize board = _board;
-(id) initBoard
if((self = [super init]){
[board intiWithCapacity: 100];
for(int i = 0; i < 100; i++){
[board addObject:#""];
}
}
return self;
}
and here is the relevant code from my declaration of "board" in the .h file:
#interface ChutesAndLadders : NSObject{
#private
NSMutableArray * board;
}
#property (readwrite, retain) NSMutableArray *board;
-(id) initBoard;
I have tried a lot of small changes in syntax but to no avail. Please don't tell me the solution directly, but a nudge or a shove in the right direction I think is all I need and I will post the solution once I figure get it.
Pseudo solution:
#synthesize board=board;
-(void) initBoard{
_board = [board initWithCapacity: 100];
for(int i =0; i < 100; i++){
[_board addObject:#""];
}
}
This eliminated one of the warnings, but my program is not running properly so I still have work to do in other methods. This specific question as been addressed though and I have a better understanding of how this is working now.
Actual Solution
#synthesize board=_board;
-(void) initBoard{
_board = [NSMutableArray arrayWithCapacity:100];
for(int i =0; i < 100; i++){
[_board addObject:#" "];
}
}
This is the one that finally worked per Hermann. I swear that I tried this once and it didn't work, so I kept playing around and changing things(messing it up more) and then eventually got back to this and it of course worked. Xcode lies sometimes I think to irritate me.
Edit because at first I overlooked something:
Simply use:
ChutesAndLadders *cl = [[ChutesAndLadders alloc]initBoard];
because within initBoard you are calling [super init] anyway.
Does your professor try to trick you?
It is difficult to say without having a look at all of the code.
You are synthesizing a property board which has the iVar _board.
Meaning you can access it's getter or setter respectively by self.board or [self board] or [self setBoard: ...].
But within the initBoard method it is accessed differently:
[board intiWithCapacity: 100];
Despite the fact that we do not see where board is actually allocated and that the result of the initWithCapacity method is not used, I am asking myself what board actually is. It is not the property (that would be self.board etc) nor is it the iVar (that would be _board).
Apparently just board refers to the private iVar that you declared. But
#synthesize board = _board;
creates an iVar with the name _board of the same type (NSMutableArray*).
Therefore in your method, board is different from _board.
Althoug I am editing this for the 3rd time now, I am still not getting it. The .m file was given by the teacher and you have to create a matching .h file, right? In that case you should go for
#private
NSMutableArray * _board;
Well, that is done automatically anyway but writing it with "_" would avoid the creation of a similar named iVar and therefore avoid likely errors.
Background AFAIK: Theoretically or even practically init may create a different instance than the one that alloc created. Therefore you should always use the return value of init.
In general - independent from the strange objective-c notation around object initialization - when ever a method returns a value, then
[object method];
is syntactically correct, runs fine and everything but the return value is unused. And that is what your warning message is all about. Even if you assign it to a dummy object like
SomeClass *someObject = [object method];
you would get the warning that `someClass' is an unused variable.
Last edit:
You are nearly there. However, this is a shortcut for current versions of Obj-C, not for older ones:
.h file:
#interface ChutesAndLadders : NSObject
#property (readwrite, retain) NSMutableArray *board;
-(void) initBoard;
#end
.m file:
// omitting #synthesize forces the compiler to autosynthesize. In that case it is named "_board"
-(void) initBoard
_board = [NSMutableArray arrayWithCapacity: 100]; // this implies the alloc
for(int i = 0; i < 100; i++){
[_board addObject:#""];
}
}
You're returning something that you're not using. Also read up on writing proper constructors/initializing objects in Objective-C.
As Hermann said you should do
ChutesAndLadders *cl = [[ChutesAndLadders alloc]initBoard];
and then in your initBoard method
self.board = [NSMutableArray alloc] intiWithCapacity: 100];
You are not following standard Objective-C conventions, and that gets you trapped.
You have an instance variable board. Objective-C convention is that all instance variables should start with an underscore character.
You have a property board, and #synthesize board = _board. The synthesize creates an instance variable with an underscore character, named _board. Now you have two instance variables: board and _board. One of them, _board, is the instance variable backing the board property. The other, board, has nothing to do with board property at all.
And then somewhere you have a harmless looking but fatal statement board = whatever. Which assigns to that totally useless instance variable board. Not _board, but board. If you wrote _board then you would be following Objective-C conventions: Assignment to instance variable using highly visible name. or self.board which would be the property.
And [board initWithCapacity:100] doesn't do anything. First, because the board instance variable (totally unrelated to board property or _board instance variable) hasn't been allocated. It's nil. Sending initWithCapacity to nil does nothing. Sending addObject to nil does nothing either.

Subclassing NSMutableString

I know the first thing to do when one has a question with Obj-C is to read the Apple docs.
And I did. I read p.95-p.102 of the Cocoa Fundamentals Guide. So this is what I know about composite object creation from class clusters.
Subclassing of class clusters should be done if provided functions need to be modified.
Composite objects must override primitive methods of the super class. (And I also read that the primitive methods of the super class of the super class have to be overridden as well.)
So I applied what I know.
Here's my code...
#import <Foundation/Foundation.h>
#interface GOObjectKeyName : NSMutableString
{
NSMutableString *embeddedNSMutableString;
}
//+ GOObjectKeyName;
//- init;
//From NSString
- (NSUInteger) length;
// From NSString
- (unichar) characterAtIndex: (NSUInteger) index;
// From NSString
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange;
// From NSMutableString
- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString;
//- (void) dealloc;
#end
#import "GOObjectKeyName.h"
#implementation GOObjectKeyName
/*
+ GOObjectKeyName
{
return [[[self alloc] init] autorelease];
}
- init
{
self = [super init];
if (self)
{
embeddedNSMutableString = [[NSMutableString allocWithZone: [self zone]] init];
}
return self;
}
*/
- (NSUInteger) length
{
return [embeddedNSMutableString length];
}
- (unichar) characterAtIndex: (NSUInteger) index
{
return [embeddedNSMutableString characterAtIndex: index];
}
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange
{
[embeddedNSMutableString getCharacters: buffer range: aRange];
}
- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString
{
[embeddedNSMutableString replaceCharactersInRange: aRange withString: aString];
}
/*
- (void) dealloc
{
[embeddedNSMutableString release];
[super dealloc];
}
*/
#end
The commented methods are commented because I wasn't sure if they were necessary. I just wrote them down because I saw them in the example. And you also might see that it's basically another NSMutableString class. But don't worry, there is a bigger purpose to it, I just want to know that the basics are correct before I continue since this is my first time with composite objects.
And as you know... IT DOESN'T WORK!!
Please understand my frustration after hours of trial and error.
If someone could guide me here it would be a great relief.
Oh, and if you don't mind I have another small question.
The example on the apple docs describe the NSNumber analogy. But is NSNumber actually more efficient then using int, char, and other fundamental types?
Just curious.
It would certainly help if you would characterize the failure beyond "it doesn't work."
However, your -init method is commented out, so there's no way that embeddedMutableString is going to get created. And even if you uncomment it, you call +alloc to create embeddedMutableString, but you never initialize it.
After looking at the documentation, it seems that a subclass's -init method should look like this (warning: untested code):
-(id)init
{
if (self = [super init]) {
embeddedMutableString = [[NSMutableString alloc] init];
}
return self;
}
You will, of course, also have to provide a -dealloc that releases embeddedMutableString and calls [super dealloc].
As far as NSNumber, no, it's not more efficient than basic types like int but it does have other advantages. NSNumber is a class, so you can store instances of NSNumber in Cocoa's collection classes such as NSArray and NSSet. You can write them to property lists. NSDecimalNumber avoids the imprecision that comes from converting between decimal and binary notations. And so on...
To answer your NSNumber question, no, NSNumber is not more efficient than the C primitives. NSNumber is an encapsulation for the C primitives, essentialy allowing primitives to be inserted into objects like NSArray and NSSet. If you need that encapsulation ability, but want the speed of an actual int or float, it's simple enough to write your own NSNumber-like class that works for just one type.

NSMutableArray - force the array to hold specific object type only

Is there a way to force NSMutableArray to hold one specific object type only?
I have classes definitions as follow:
#interface Wheel:NSObject
{
int size;
float diameter;
}
#end
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray *wheels;
}
#end
How can I force wheels array to hold Wheel objects only with code? (and absolutely not other objects)
Update in 2015
This answer was first written in early 2011 and began:
What we really want is parametric polymorphism so you could declare, say, NSMutableArray<NSString>; but alas such is not available.
In 2015 Apple apparently changed this with the introduction of "lightweight generics" into Objective-C and now you can declare:
NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];
But all is not quite what it seems, notice the "lightweight"... Then notice that the initialisation part of the above declaration does not contain any generic notation. While Apple have introduced parametric collections, and adding a non-string directly to the above array, onlyStrings, as in say:
[onlyStrings addObject:#666]; // <- Warning: Incompatible pointer types...
will illicit the warning as indicated, the type security is barely skin deep. Consider the method:
- (void) push:(id)obj onto:(NSMutableArray *)array
{
[array addObject:obj];
}
and the code fragment in another method of the same class:
NSMutableArray<NSString *> *oops = [NSMutableArray new];
[self push:#"asda" onto:oops]; // add a string, fine
[self push:#42 onto:oops]; // add a number, no warnings...
What Apple have implemented is essentially a hinting system to assist with automatic inter-operation with Swift, which does have a flavour of type-safe generics. However on the Objective-C side, while the compiler provides some extra hints the system is "lightweight" and type-integrity is still ultimately down to the programmer - as is the Objective-C way.
So which should you use? The new lightweight/pseudo generics, or devise your own patterns for your code? There really is no right answer, figure out what makes sense in your scenario and use it.
For example: If you are targeting interoperation with Swift you should use the lightweight generics! However if the type integrity of a collection is important in your scenario then you could combine the lightweight generics with your own code on the Objective-C side which enforces the type integrity that Swift will on its side.
The Remainder of the 2011 Answer
As another option here is a quick general subclass of NSMutableArray which you init with the kind of object you want in your monomorphic array. This option does not give you static type-checking (in as much as you ever get it in Obj-C), you get runtime exceptions on inserting the wrong type, just as you get runtime exceptions for index out of bounds etc.
This is not thoroughly tested and assumes the documentation on overriding NSMutableArray is correct...
#interface MonomorphicArray : NSMutableArray
{
Class elementClass;
NSMutableArray *realArray;
}
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id) initWithClass:(Class)element;
#end
And the implementation:
#implementation MonomorphicArray
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems
{
elementClass = element;
realArray = [NSMutableArray arrayWithCapacity:numItems];
return self;
}
- (id) initWithClass:(Class)element
{
elementClass = element;
realArray = [NSMutableArray new];
return self;
}
// override primitive NSMutableArray methods and enforce monomorphism
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if ([anObject isKindOfClass:elementClass]) // allows subclasses, use isMemeberOfClass for exact match
{
[realArray insertObject:anObject atIndex:index];
}
else
{
NSException* myException = [NSException
exceptionWithName:#"InvalidAddObject"
reason:#"Added object has wrong type"
userInfo:nil];
#throw myException;
}
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[realArray removeObjectAtIndex:index];
}
// override primitive NSArray methods
- (NSUInteger) count
{
return [realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [realArray objectAtIndex:index];
}
// block all the other init's (some could be supported)
static id NotSupported()
{
NSException* myException = [NSException
exceptionWithName:#"InvalidInitializer"
reason:#"Only initWithClass: and initWithClass:andCapacity: supported"
userInfo:nil];
#throw myException;
}
- (id)initWithArray:(NSArray *)anArray { return NotSupported(); }
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { return NotSupported(); }
- (id)initWithContentsOfFile:(NSString *)aPath { return NotSupported(); }
- (id)initWithContentsOfURL:(NSURL *)aURL { return NotSupported(); }
- (id)initWithObjects:(id)firstObj, ... { return NotSupported(); }
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { return NotSupported(); }
#end
Use as:
MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3];
[monoString addObject:#"A string"];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:#"Another string"];
Since Xcode 7, generics are available in Objective-C.
You can declare a NSMutableArray as:
NSMutableArray <Wheel*> *wheels = [[NSMutableArray alloc] initWithArray:#[[Wheel new],[Wheel new]];
The compiler will give you a warning if you try to put non-Wheel objects in the array.
I could be wrong (I'm a noob), but I think, if you create a custom protocol and make sure the objects you are adding to the array follow the same protocol, then when you declare the array you use
NSArray<Protocol Name>
That should prevent objects being added that do not follow the said protocol.
as per i know.. before you added any object in wheels mutableArray, u have to add some check mark. Is the object which i am adding is class "wheel". if it is then add, other wise not.
Example:
if([id isClassOf:"Wheel"] == YES)
{
[array addObject:id)
}
Something like this. i dont remember the exact syntax.
I hope this will help (and work... :P )
Wheel.h file:
#protocol Wheel
#end
#interface Wheel : NSObject
#property ...
#end
Car.h file:
#import "Wheel.h"
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray<Wheel, Optional> *wheels;
}
#end
Car.m file:
#import "Car.h"
#implementation Car
-(id)init{
if (self=[super init]){
self.wheels = (NSMutableArray<Wheel,Optional>*)[NSMutableArray alloc]init];
}
return self;
}
#end
Xcode 7 allows you to define Arrays, Dictionaries, and even your own Classes as having generics. The array syntax is as follows:
NSArray<NSString*>* array = #[#"hello world"];
I don't believe there's any way to do it with NSMutableArray out of the box. You could probably enforce this by subclassing and overriding all the constructors and insertion methods, but it's probably not worth it. What are you hoping to achieve with this?
That's not possible; an NSArray (whether mutable or not) will hold any object type. What you can do is to create your own custom subclasses as already suggested by Jim. Alternatively, if you wanted to filter an array to remove objects that weren't of the type you want, then you could do:
- (void)removeObjectsFromArray:(NSMutableArray *)array otherThanOfType:(Class)type
{
int c = 0;
while(c < [array length])
{
NSObject *object = [array objectAtIndex:c];
if([object isKindOfClass:type])
c++;
else
[array removeObjectAtIndex:c];
}
}
...
[self removeObjectsFromArray:array otherThanOfType:[Car class]];
Or make other judgments based on the result of isKindOfClass:, e.g. to divide an array containing a mixture of Cars and Wheels into two arrays, each containing only one kind of object.
You can use the nsexception if you dont have the specific object.
for (int i = 0; i<items.count;i++) {
if([[items objectAtIndex:i] isKindOfClass:[Wheel class]])
{
// do something..!
}else{
[NSException raise:#"Invalid value" format:#"Format of %# is invalid", items];
// do whatever to handle or raise your exception.
}
}
Here's something I've done to avoid subclassing NSMutableArray: use a category. This way you can have the argument and return types you want. Note the naming convention: replace the word "object" in each of the methods you will use with the name of the element class. "objectAtIndex" becomes "wheelAtIndex" and so on. This way there's no name conflict. Very tidy.
typedef NSMutableArray WheelList;
#interface NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index;
- (void) addWheel: (wheel *) w;
#end
#implementation NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index
{
return (wheel *) [self objectAtIndex: index];
}
- (void) addWheel: (wheel *) w
{
[self addObject: w];
}
#end
#interface Car : NSObject
#property WheelList *wheels;
#end;
#implementation Car
#synthesize wheels;
- (id) init
{
if (self = [super init]) {
wheels = [[WheelList alloc] initWithCapacity: 4];
}
return self;
}
#end
protocol maybe a good idea:
#protocol Person <NSObject>
#end
#interface Person : NSObject <Person>
#end
to use:
NSArray<Person>* personArray;
There is one-header file project which allows this:
Objective-C-Generics
Usage:
Copy ObjectiveCGenerics.h to your project.
When defining a new class use the GENERICSABLE macro.
#import "ObjectiveCGenerics.h"
GENERICSABLE(MyClass)
#interface MyClass : NSObject<MyClass>
#property (nonatomic, strong) NSString* name;
#end
Now you can use generics with arrays and sets just as you normally do in Java, C#, etc.
Code:

Objective-C Imports, primitive types with Cygwin

I understand the basic syntax of Objective-C, have installed Cygwin, and want to experiment. However i am unsure of two things:
What i would import, and
what the primitive type names are.
Can someone help me?
The only object you can inherit from is called Object. Bare in mind that this offers nowhere near the same amount of functionality as NeXTStep's or Cocoa's NSObject. Object does not even have anything like reference counting. In order to get the same sort of reference counting memory management that NSObject has you'll need to implement it yourself.
#import <objc/Object.h>
#interface MyObject : Object
{
int retainCount;
}
- (id) retain;
- (int) retainCount;
- (void) release;
#end
#implementation MyObject
+ (MyObject *) alloc
{
// In Cocoa, allocated objects have an implicit retain.
MyObject *anObject = [super alloc];
anObject->retainCount = 1;
return anObject;
}
- (void) release
{
retainCount--;
if (retainCount == 0)
[self free];
}
- (id) retain
{
retainCount++;
return self;
}
- (int) retainCount
{
return retainCount;
}
#end
int main (int argc, char *argv[])
{
MyObject *test = [[MyObject alloc] init];
[test retain];
[test release];
[test release];
// (test should be deallocated now)
return 0;
}
When linking, you have to make sure you link with -lobjc, this is where the definition of Object lies (I think).
The other big catch is with static string instances, i.e. strings in code that appear #"like this". With the GNU runtime, static instances of strings need to have a particular ivar layout, which is:
// Let's assume that we have a protocol <MyObject> that defines all the basic methods
// like retain, release etc. In this case, these should be no-ops because the static
// string is never deallocated. In Cocoa, there is a protocol <NSObject> which provides
// the same common methods.
#interface MyStaticStringClass : Object <MyObject>
{
char *str;
unsigned len;
}
- (const char *) cString;
#end
#implementation MyStaticStringClass
- (void) retain
{
return;
}
- (id) retain
{
return self;
}
- (int) retainCount
{
return INT_MAX;
}
- (const char *) cString
{
return str;
}
#end
int main (int argc, char *argv[])
{
id aString = #"Hello world!";
fprintf (stdout, "aString has the contents: %s\n", [aString cString]);
return 0;
}
When compiling, you can use the flag -fconstant-string-class=MyStaticStringClass. You can provide whatever methods you like for the string class but it must have only two ivars and they must be in the right order. If you don't want to use Objective-C style strings, then you don't have to define a static string class. If you do define a static string class it should be able to replicate the behaviour of your dynamic string class (i.e. string objects that are allocated during run time) so that you can use either in a given situation.
For command-line utilities and basic apps I choose not to use Cocoa or GNUstep but rather define my own classes. It has many drawbacks, but I find that object abstraction and metamorphism in Objective-C is much easier to implement than in the other languages that I program in.

%d doesn't show integer properly

As I try to write
NSLog(#"%d", myObject.myId);
where myId is int, console gives some hight number like 70614496. And when I use #"%#", I get exception -[CFNumber respondsToSelector:]: message sent to deallocated instance 0x466c910.
Why is it so?
Here's definition of myObject:
#interface myObject : NSObject {
int myId;
NSString *title;
}
#property(nonatomic) int myId;
#property(nonatomic, retain) NSString *title;
#end
#implementation myObject
#synthesize myId, title;
- (void)dealloc {
[title release];
[super dealloc];
}
#end
Carl's doing a good job of trying to get your code to compile. Just another word on the %# format specifier, since your question seems to imply some confusion.
When you use %# in an NSLog format, you're actually sending the argument a description message, and printing the resulting string. If myId is an int and not an instance of NSObject, it doesn't respond to the description message. Use %d for int values and %# for NSNumber instances.
Here's an example showing that it should work the way you first tried it, if your class/object/instance variable are all set up correctly:
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
{
int myId;
}
#property int myId;
#end
#implementation MyClass
#synthesize myId;
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MyClass *myObject = [[MyClass alloc] init];
myObject.myId = 5;
NSLog(#"%d", myObject.myId);
[myObject release];
[pool drain];
return 0;
}
Your code shows you doing the equivalent of this line:
NSLog(#"%d", MyClass.myId);
Which doesn't even compile, giving the error:
foundation.m:21: error: accessing unknown 'myId' class method
Given that second error message, I think the %d is working fine, but somewhere else you’re assigning a CFNumber or NSNumber to the myID property when you should be assigning an integer.
It looks like your instance of myObject was autoreleased at some time and is no longer actually valid. That is why you see the strange NSNumber issue when you try to dereference it as an object. When you just ask for an integer, you won't get any kind of error, it will just display the pointer's address coerced into an integer.
To see when your myObject gets dealloced, try adding something like this to the dealloc method in your class:
- (void)dealloc
{
NSLog(#"dealloc called on %#", self);
[title release];
[super dealloc];
}
I bet you will find this logging a dealloc before you see the log for your myObject.myID.
I would try simply changing this:
#property(nonatomic) int myId;
to this:
#property(nonatomic, assign) int myId;
And let me know your result, I suspect that obj-c is doing something funky when assigning an old int to the new int?
Check out the String Format Specifiers to see how to format NSLog statements. It's easy to get lazy with NSLog because objects have a built-in -description method that returns a formatted string. For scaler values, you have to use the proper formatter.
Because precision changes as you move from hardware to hardware, it's better to get in the habit of using object conversions to log values. In this case:
NSLog(#"%#", [[NSNumber numberFromInt:myObject.myId] stringValue]);
This will print correctly always.
Edit#1: I apologize. I was sleep deprived when I wrote the above. What I actually intended was to warn against using a simple int versus NSInteger as well as printing with NSNumber numberWithInteger:.
Consider the following run on 64-bit hardware.
int x=pow(2,63);
NSLog(#"x=%d",x); //prints x=2147483647
NSInteger n=pow(2,63);
NSLog(#"n=%d",n); // prints n=-1
NSLog(#"n=%#",[[NSNumber numberWithInteger:n] stringValue]); // prints n=9223372036854775807
In the old days of 8-bit systems, you ran into problems with problems with using 8-bit 'int' all the time. Running a for-loop with more than 256 iterations required a long. With a 32-bit int you won't see those kinds of issues and will never develop the habit of tracking the size of your intvariables.
This can lead to pernicious bugs that are nearly impossible to track down because they only occur with very specific and rare values in the data.
Writing for the iPhone (or other future mobiles/platforms) means writing on potentially highly variable hardware just like we had to do in the old days. It's best to acquire the habit early of using system and API specific definitions.
Edit#2:
where myId is int, console gives some
hight number like 70614496.
(1) If it prints a different number each time you run, then you're probably assigning a pointer to the int when you set it. The NSLog is correctly printing the value of the pointer as an int.
(2) if it prints the same number each time, the you probably have an overflow issue like in my first edit above.
In either case, you need to look at code where you assign the value to the id property and not where you print it.