objectiveC autorelease issue, what is wrong with code? - objective-c

New to objective-C,
#import <objc/objc.h>
#import <Foundation/Foundation.h>
#interface Test:NSObject
{
int x,y, abc;
NSString *v1, *v2;
}
#property int x , y, abc;
-(void) print;
#end
#implementation Test
#synthesize x,y, abc;
-(void) print
{
NSLog (#"v1 and v2 values %i, %i ", v1, v2);
}
#end
int main ( int argc, char **argv)
{
Test *t = [[Test alloc] init];
/* Synthesized Set Method */
[t setX:100];
[t setY:200];
/* Synthesized Get Method */
NSLog (#"Retrieving Values %i, %i ",[t x], [t y]);
/* another Way to retrieve the throuhg KVC Model */
NSLog (#" KVC Retrieveal %i ", [t valueForKey:#"x"]);
}
I did not get compile time error, but run time error i got :
2012-04-11 16:25:08.470 testpgm[22237] Retrieving Values 100, 200
2012-04-11 16:25:08.513 testpgm[22237] autorelease called without pool for object (0x8e78ca0) of class NSMethodSignature in thread <NSThread: 0x8e23a08>
2012-04-11 16:25:08.514 testpgm[22237] autorelease called without pool for object (0x8e94610) of class NSIntNumber in thread <NSThread: 0x8e23a08>
2012-04-11 16:25:08.514 testpgm[22237] KVC Retrieveal 149505552
Looks like it is something to do with memory issue. some one point out the issue ?
NOTE: With all your inputs, i could resolve the autorelease issue, but still
NSLog (#" KVC Retrieveal %i ", [t valueForKey:#"x"]);
does not print the proper value but the garbage. Am i doing something wrong?

When you are in the run loop of an application, there is a default autorelease pool created for you. However, when you are running with your own main, you need to create an autorelease pool manually at the top of your main, and drain it periodically.
NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init];
// Your code that uses autorelease...
[myPool drain];
If you are compiling with the new LLVM compiler, use the new #autoreleasepool feature instead.

The main routine does not create an autorelease pool.
Use one of these methods depending on the version and compiler you are using.
Newer or with ARC:
int main(int argc, char *argv[])
{
#autoreleasepool {
// your code
}
}
or
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// your code
[pool drain];
}
The code has numerous other problems such as:
NSLog (#"v1 and v2 values %i, %i ", v1, v2);
which should be
NSLog (#"v1 and v2 values %#, %# ", v1, v2);
%# is used to print objects, %i for integers.
The line:
NSLog (#" KVC Retrieveal %i ", [t valueForKey:#"x"]);
is interesting because valueForKey returns an object (in this case an NSNumber) so the correct statement is:
NSLog (#" KVC Retrieveal %# ", [t valueForKey:#"x"]);
Running the program with these corrections produces:
Retrieving Values 100, 200
KVC Retrieveal 100

main function must have an autorelease pool.
int main(int argc, char *argv[])
{
#autoreleasepool {
// you code
return ...;
}
}
EDIT:
Regarding your second part of question. valueForKey returns id, cast it to int
NSLog (#" KVC Retrieveal %i ", [[t valueForKey:#"x"] intValue]);

int main ( int argc, char **argv)
{
NSAutoreleasePool *myPool = [NSAutoreleasePool new];
Test *t = [[Test alloc] init];
/* Synthesized Set Method */
[t setX:100];
[t setY:200];
/* Synthesized Get Method */
NSLog (#"Retrieving Values %i, %i ",[t x], [t y]);
/* another Way to retrieve the throuhg KVC Model */
NSLog (#" KVC Retrieveal %i ", [t valueForKey:#"x"]);
[pool drain];
}
may be it will work

Related

How does the Xcode breakpoint system interact with memory management?

Here's a little experiment:
#interface Model : NSObject
#property (copy) NSString *value;
-(instancetype)initWith:(NSString *)value;
#end
#implementation Model
-(instancetype)initWith:(NSString *)value {
self = [super init];
self.value = value;
return self;
}
#end
#import <Foundation/Foundation.h>
#import "Model.h"
void experiment(Model *m);
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
Model *ma = [[Model alloc] initWith:[[NSUUID UUID] UUIDString]];
experiment(ma);
NSLog(#"yyy %#", [ma value]);
}
return 0;
}
void experiment(ModelA *m) {
NSString *testValue = nil;
testValue = [m value];
NSLog(#"xxx %#", testValue);
}
When run, this produces the following:
Hello, World!
xxx 6005A7B0-F71C-4755-B1BF-792D6296B716
yyy 6005A7B0-F71C-4755-B1BF-792D6296B716
Program ended with exit code: 0
But suppose I make this line:
testValue = [m value];
part of a breakpoint:
And this changes everything:
Hello, World!
(__NSCFString *) $0 = 0x000000010071e220 #"1C0DCB39-BFBB-4E67-A041-E6B58615BDFD"
xxx 1C0DCB39-BFBB-4E67-A041-E6B58615BDFD
yyy 1C0DCB39-BFBB-4E67-A041-E6B58615BDFD
*** -[CFString release]: message sent to deallocated instance 0x10071e220
And a crash. I see what's happening--the string is released once we exit the function scope, and the second time when the Model object is destroyed, which is an overrelease. But why doesn't the breakpoint (or more precisely, the lldb expression inside the breakpoint) handle the reference count correctly?

#property copy & manual memory management with an autorelease pool

I have a class that copy's an NSString and print's it's retain count and address.
#interface TestStringPointer : NSObject
#property (copy) NSString *stringTest;
- (void)printPointer;
#end
#implementation TestStringPointer
- (void)printPointer {
NSLog(#"PrintPointer:%p\n RetainCount:%lu", _stringTest, [_stringTest retainCount]);
}
#end
In my main function I was doing a bit of investigation on the String's pointer and ran into an issue.
int main(int argc, const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
TestStringPointer *test = [[TestStringPointer alloc] init];
NSString *myString = [NSString stringWithUTF8String:"Hello World"];
NSLog(#"MyString: %p \n RetainCount:%lu", myString, [myString retainCount]);
[test setStringTest:myString];
[test printPointer];
[myString release];
[pool drain];
while (1) {
[test printPointer];
}
return 0;
}
When I debug the app it crashes the 3rd time through the while loop (the number of times through the loop varies). I understand that the copy doesn't occur cause the string isn't mutable.
After it's released in main, I would've expected it to go back to 1.
1) If an object isn't autoreleased, is it still affected the autorelease pool?
2) Wouldn't having a max retain count prevent the object from being drained in the pool, or does that flag it for deletion?
3) Shouldn't the copy have stepped in at some point and actually made a copy before it was deleted?
StringPointerTest[2253:303] MyString: 0x100100f60 RetainCount:1
StringPointerTest[2253:303] PrintPointer:0x100100f60 RetainCount:2
StringPointerTest[2253:303] PrintPointer:0x100100f60
RetainCount:1152921504606846975
StringPointerTest[2253:303] PrintPointer:0x100100f60 RetainCount:1152921504606846975
If I modify main and remove the pool
int main(int argc, const char * argv[])
{
TestStringPointer *test = [[TestStringPointer alloc] init];
NSString *myString = [NSString stringWithUTF8String:"Hello World"];
NSLog(#"MyString: %p \n RetainCount:%lu", myString, [myString retainCount]);
[test setStringTest:myString];
[test printPointer];
[myString release];
while (1) {
[test printPointer];
}
return 0;
}
All is right... Forever...
StringPointerTest[423:303] MyString: 0x10010a670 RetainCount:1
StringPointerTest[423:303] PrintPointer:0x10010a670 RetainCount:2
StringPointerTest[423:303] PrintPointer:0x10010a670 RetainCount:1
...
The mistake is when you release myString. This is wrong because stringWithUTF8String returns an autoreleased string (remember that every method has the autoreleased version: a static method, and the non-autoreleased version: init or initWithSomething: ). So when the autorelease pool gets drained, myString retain count goes to zero and the object gets deallocated (maybe later, you don't know exactly when, the fact that it crashes at the 3rd loop iteration is casual).
So you solve the problem by calling alloc + initWithUTF8String: instead of stringWithUTF8String:. The fact that you see an incredibly high retain count is just dued to the fact that the memory has been freed and maybe written again, before the object gets really deallocated, it's just a pitfail, you don't own the object anymore.

objective-c synthesizeing leads to error?

I m pretty new to objective-c, and trying some examples on my own. Here is my sample code
#import <objc/objc.h>
#import <Foundation/Foundation.h>
#interface Test:NSObject
{
int noOfWheels;
int total;
}
#property int noOfWheels;
#property int total;
-(void) print;
#end
#implementation Test
#synthesize noOfWheels, total;
-(void) print
{
NSLog (#" noofWheels is %i, total %i ", noOfWheels, total);
}
#end
int main ( int argc, char ** argv)
{
Test *t = [Test alloc];
t = [t init];
[t setnoOfWheels: 10];
[t settotal: 300];
[t print];
}
and it compiled with no error, but when i run the program i get the following error.
Uncaught exception NSInvalidArgumentException, reason: -[Test setnoOfWheels:]: unrecognized selector sent to instance 0x87aef48
What am i doing wrong in my code ?
By default 1st letter of iVar is capitalized in setter method name. So correct call will be:
[t setNoOfWheels: 10];
[t setnoOfWheels: 10];
should be
[t setNoOfWheels: 10];
or even better, since you're declaring a property:
t.noOfWheels = 10;

fatal error on NSArray.h

I have ubuntu machine and compiling objective-c using GNUStep. I wrote the following code:
#import <objc/objc.h>
#import <Foundation/Foundation.h>
#import <objc/NSArray.h>
int main ( int argc, char ** argv)
{
int ar[100] = {0};
int i;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *arr = [[NSArray alloc] initWithObjects:#"stackOverflow", #"1", #"2", nil];
NSLog (# "Counts in the array %i", [arr count]);
#try {
NSString *str;
str = [arr objectAtIndex:1];
NSLog (#" String value is %# ", str);
}
#catch (NSRangeException * excep)
{
NSLog (#"Reached Range caught for %#:%#" [excep name], [excep reason]);
}
[pool release];
}
But I get the following fatal error:
fatal error: objc/NSArray.h: No such file or directory
I tried <NSArray.h> also, but getting same error.
Which path I have to provide?
The file objc/NSArray.h does not exist, hence the fatal error.
Remove the #import <objc/NSArray.h> as NSArray should already be available via the Foundation import.
See https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html

NSThread thread-safe implementation of initialize.?

The following code compiles and executes as expected.
#import <objc/objc.h>
#import <Foundation/Foundation.h>
BOOL loopValue = YES;
#interface myThread:NSObject
-(void) enterThread: (NSArray *) elemt count: (NSString *) x;
#end
#implementation myThread
-(void) enterThread : (NSArray *) elemt
{
NSLog (#" Inside mythread ");
NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc] init];
int i;
int cnt =10;
for(i=0; i<cnt; i++) {
NSLog (#"Number of elemennts in array %i ", [elemt count]);
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
loopValue = NO;
[pool drain];
}
#end
int main ( int argc, char ** argv)
{
NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc] init];
// id tobj = [[myThread alloc] init];
id tobj = [ myThread new ];
NSLog (#"Starting New Thread ");
[NSThread detachNewThreadSelector:#selector(enterThread:) toTarget:tobj withObject:[NSArray arrayWithObjects:#"ram",#"20",nil]];
while(1)
if ( loopValue )
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
else
break;
NSLog (#".. Exiting.. \n");
[pool drain];
return 0;
}
MY Question:
While compilation i do get the following warnings..
mythread.m:24:1: warning: incomplete implementation of class ‘myThread’ [enabled by default]
mythread.m:24:1: warning: method definition for ‘-enterThread:count:’ not found [enabled by default]
While Execution
WARNING your program is becoming multi-threaded, but you are using an ObjectiveC runtime library .... Removed due to redability]hich does not have a thread-safe implementation of the +initialize method. ......
What am i dong wrong ? how to avoid both warning/runtime errors.
The method you declared is enterThread:count: but the method you implement is enterThread:. Also, that warning you are getting, I'm sure I've only seen that from the old GNUstep runtime… but I guess not.