Objective-C, retaining a variable after NSLog-output - objective-c

I would like to know how weak property work in Objective-C.
In this example the value of the weak property "myString" in "myClass" is kept only when I print it with NSLog. Why is that?
#import <Foundation/Foundation.h>
#include <stdio.h>
#interface myClass : NSObject
#property (nonatomic, weak)NSString *myString;
- (void)readString;
#end
#implementation myClass
#synthesize myString;
- (void)readString
{
const int MAXBUFFER = 80;
char buffer[MAXBUFFER+1];
NSLog(#"Input string:");
fgets(buffer, MAXBUFFER, stdin);
NSString *tempString = [[NSString alloc] initWithUTF8String:buffer];
myString = tempString;
NSLog(#"myString: %#", myString); // Why does this line make all the difference?
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool
{
myClass *myInstance = [[myClass alloc] init];
[myInstance readString];
NSLog(#"myInstance.myString: %#", myInstance.myString);
}
return 0;
}
If the NSLog-line in the readString-method is commented out myInstance.myString becomes "(null)". Why??

From Apple:
weak Specifies that there is a weak (non-owning) relationship to the
destination object. If the destination object is deallocated, the
property value is automatically set to nil.
So Basically when arc insert code into [readString], he does:
NSString *tempString = [[NSString alloc] initWithUTF8String:buffer];
myString = tempString;
// + arc [tempString release]
So your tempString no longer exist outside the method, because nothing retain it.
But when you add NSlog inside [readString] with myString, NSLog will keep reference to the pointer (i don't know exactly how), but he actually does since he logs them.

Related

Why weak property of associated object is not nilled out if I call its getter?

Though it's kind of stupid in 2020 that I'm still asking question about ObjC, please be patient and considerate...
I'm reading the source code of BloksKit and ran into a weird situation.
#import <objc/runtime.h>
#interface _WeakAssociatedObjectWrapper : NSObject
#property (nonatomic, weak) id object;
#end
#implementation _WeakAssociatedObjectWrapper
#end
#interface NSObject (AddWeak)
#end
#implementation NSObject (AddWeak)
- (void)setWeakProp:(id)weakProp {
_WeakAssociatedObjectWrapper *wrapper = objc_getAssociatedObject(self, #selector(weakProp));
if (!wrapper) {
wrapper = [[_WeakAssociatedObjectWrapper alloc] init];
objc_setAssociatedObject(self, #selector(weakProp), wrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
wrapper.object = weakProp;
}
- (id)weakProp {
id value = objc_getAssociatedObject(self, _cmd);
if ([value isKindOfClass:_WeakAssociatedObjectWrapper.class]) {
return [(_WeakAssociatedObjectWrapper *)value object];
}
return value;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
{
NSObject *prop = [[NSObject alloc] init];
[obj setWeakProp:prop];
[obj weakProp]; // *Weird!!
}
NSLog(#"Now obj.weakProp = %#", [obj weakProp]);
}
return 0;
}
This code is adding a weak associated object for category.(BlocksKit does so)
Note the *Weird!! line. If this line is commented out, then it prints (null), which is reasonable since prop is deallocated outside the {} scope. On the other side, if not commented out, it prints <NSObject: 0xxxxx>, which indicates that prop is somehow retained by someone(Or any other reason?).
What is happening here??! (BlocksKit behaves the same!)
Environment: XCode 10.3
This is a feature. For the case (and any similar)
[obj weakProp];
by properties/accessors naming convention ARC returns autoreleased instance, so in your case #autoreleasepool holds it and testing as below can show this.
int main(int argc, const char * argv[]) {
NSObject *obj = [[NSObject alloc] init];
#autoreleasepool {
{
NSObject *prop = [[NSObject alloc] init];
[obj setWeakProp:prop];
[obj weakProp]; // *Weird!!
}
NSLog(#"Now obj.weakProp = %#", [obj weakProp]);
}
NSLog(#"After autoreleased >> obj.weakProp = %#", [obj weakProp]);
return 0;
}

Automatically trim NSString in custom setter?

I have an object w/ an NSString for a property. I would like to use a custom setter for this property so that it always trims all whitespace when the string is set.
Normally this is done with NSString *blah2 = [blah stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];, but I'm not sure about how to formulate this for a custom setter. Isn't it bad to use autoreleased objects like this in the setter? Should I use a mutable string instead and then convert it to unmutable?
Thanks for any help you guys can provide.
I'm going to presume ARC.
#import <Foundation/Foundation.h>
#interface Foo:NSObject
#property (nonatomic,copy) NSString *myString;
#end
#implementation Foo
#synthesize myString = _myString;
- (void)setMyString:(NSString *)aString;
{
_myString = [[aString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] copy];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Foo *someFoo = [[Foo alloc] init];
someFoo.myString = #"Blah ";
NSLog(#"Length = %ld",[[someFoo myString] length]); // prints 4
}
}
In the setter, I do copy the parameter of the setter in the event someone tries to provide an instance of NSMutableString - presuming that Foo is not expected to modify the original string.
EDIT:
Or if manual reference counting, the setter might be:
- (void)setMyString:(NSString *)aString;
{
if( aString != _myString ) {
[_myString release];
_myString = [[aString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] copy];
}
}

How to return an NSString * in Objective C (keep getting invalid summary)

So I presume this is a memory issue, here's the code:
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = #"a string";
return s;
}
And the calling code:
NSString *aString;
aString = [self giveMeAStringGoddammit];
However after this call, aString has an invalid summary when debugged and crashes when run.
I suspect I'm missing a retain or something, can someone help? Thanks.
What you've got works just fine:
#import <Foundation/Foundation.h>
#interface Test : NSObject
- (NSString *)giveMeAStringGoddammit;
#end
#implementation Test
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = #"a string";
return s;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
Test *t = [[Test alloc] init];
NSLog(#"t says: %#", [t giveMeAStringGoddammit]);
}
return 0;
}
The output of this program is:
t says: a string
To make this a little more realistic, let's change it to use a property:
#import <Foundation/Foundation.h>
#interface Test : NSObject
#property(copy, nonatomic) NSString *string;
- (NSString *)giveMeAStringGoddammit;
#end
#implementation Test
#synthesize string;
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = self.string;
return s;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
Test *t = [[Test alloc] init];
t.string = #"Hello world!";
NSLog(#"t says: %#", [t giveMeAStringGoddammit]);
}
return 0;
}
This does what you'd expect:
t says: Hello world!
You have created a pointer object and it's expected to increment it's retain count whenever you referring them, for increasing retain count the string should be initiated and allocated with memory else you could use [NSString stringwithString:[self giveMeAStringGoddammit]. You can use this definition only when you exactly need it reference locally. because whenever you try to refer it out side the auto release pool will crash the app (hence it's not retained manually). So if you need to use it out side of the function, better use [NSString alloc]init] and then load your string to the pointer object. Well the way to make your code to work is add the lines NSString *aString = [NSString stringWithString:[self giveMeAStringGoddammit]];
NSLog(#"My Str:%#",aString); Hooray now the goddammit string was given......

Pass data between objects

I have a variable in a class, NSNumber. I want to pass the value of this var to another class var. The problem is that I release the object of the first class and obtain an error message when I try to set the value of the second class var.
In C++ this is so easy to do. But here with memory management and pointers confused me so much.
Solution code, for testing:
#import <Foundation/Foundation.h>
#interface A : NSObject
{
NSNumber *a;
}
#property (nonatomic, retain) NSNumber *a;
#end
int main(int argc, char *argv[])
{
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
A *instance1 = [[A alloc] init];
A *instance2 = [[A alloc] init];
[instance1 setA:[NSNumber numberWithFloat:5.43f]];
instance2.a = [instance1.a copy];
[instance1 release];
NSLog(#"Valor de la que sigue viva, parte2: %#", instance2.a);
[instance2 release];
[p release];
[pool drain];
return 0;
}
You should use a retain property or copy the instance variable:
#interface A {
NSNumber *a;
}
#property (nonatomic, retain) NSNumber *a;
#end
...
A *instance1 = [[A alloc] init];
A *instance2 = [[A alloc] init];
instance1.a = instance2.a;
//or
instance2.a = [instance1.a copy];
Read some docs about retain-counted memory management which is what Objective-C uses.

Set readonly attribute in ObjC

Is there a way to set a value to readonly attribute in Objective-C?
I actually don't care how nasty the code is unless it isn't stable anymore.
Never mind my comment, here's the two ways you do it:
#interface Grimley : NSObject
#property (readonly, copy) NSString * blabber;
#property (readonly, copy) NSString * narwhal;
- (id) initWithBlabber:(NSString *)newBlabber;
#end
#implementation Grimley
#synthesize blabber;
#synthesize narwhal = unicorn;
- (id) initWithBlabber:(NSString *)newBlabber {
self = [super init];
if( !self ) return nil;
// Any object can of course set its own ivar regardless
// of how the property it backs is declared.
blabber = [newBlabber copy];
// Refer to the _ivar_, not the property.
unicorn = #"One horn";
return self;
}
#end
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Grimley * g = [[Grimley alloc] initWithBlabber:#"Excelsior"];
// This is how you get around the property.
[g setValue:#"Nimitz" forKey:#"blabber"];
// Again, use the name of the variable, not the property
[g setValue:#"Pearly horn" forKey:#"unicorn"];
NSLog(#"%#", [g blabber]);
NSLog(#"%#", [g narwhal]);
[g release];
[pool drain];
return 0;
}