1
votes

I am new to objective C and iOS. I am confused with 2 resulting values when using NSString stringWithFormat and initWithFormat like bellow:

NSString* str0 = @"Hello";
__weak NSString* str1 = [[NSString alloc] initWithFormat:@"%@", str0 ]; // warning: Assigning retained object to weak variable; object will be released after assignment
__weak NSString* str2 = [NSString stringWithFormat:@"%@",str0];

NSLog(@"Result: str0 %@, str1 %@, str2 %@", str0, str1, str2);

Output: Result: str0 Hello, str1 (null), str2 Hello

Looking around network, the answers for these callers are same under ARC example: stringWithFormat vs initWithFormat under ARC

However with above code, the str2 seems to not meaning regardless weak or strong here, i.e. I can remove __weak of str2 declaration and makes same result.

My concern is that, if the creation of string by stringWithFormat is owned by framework (or by some other way out of scope of user application)?

4
I tried your code and get "Hello" for all strings.Avi
What do you expect when you are using a weak variable and hold no other reference to the object?gnasher729
@gnasher729 I expected null for str2 if it is declared as __weak and 'Hello' if strong. But seems that stringWithFormat cause no effect (like Rob's answer) on these declaration.vnguyen

4 Answers

3
votes

The str1 is null, because you only have weak reference to the string, so ARC may deallocate it immediately because you have no strong references to it. In fact, compiler may even warn you of this:

warning: assigning retained object to weak variable; object will be released after assignment.

The str2 is Hello because stringWithFormat creates and "autorelease" object (an object with no strong references, but that is not deallocated until the pool is drained, i.e. after you yield back to the OS). It's not "owned by the framework", but merely hasn't be deallocated yet.

Note, looking at memory management with NSString can sometimes be problematic (especially when dealing with string literals) because there are internal optimizations that sometimes affect its memory management, but these results you show us are precisely what you'd expect.


Given the atypical NSString memory management, you may want to observe this pattern using your own, custom object:

@interface CustomObject: NSObject
+ (instancetype)customObject;
@end

@implementation CustomObject
+ (instancetype)customObject {
    return [[self alloc] init];
}
@end

And

__weak CustomObject *obj1 = [[CustomObject alloc] init];
__weak CustomObject *obj2 = [CustomObject customObject];

NSLog(@"obj1=%@; obj2=%@", obj1, obj2);

You will see that obj1 will consistently be nil in this case, but obj2 is not. The basic rule of memory management is that you own any object whose names start with “alloc”, “new”, “copy”, or “mutableCopy”. Thus, these objects have ownership transferred to you and ARC will release them for you (i.e. you only have one weak reference, so it's immediately released). Objects whose names start with anything besides those prefixes, have not passed ownership to you (and thus, in Objective-C, have been passed to your app as autorelease objects that will be deallocated if there are no strong references when the pool is drained.


To the root of your question, whether the object is "owned by the framework", the answer is generally no, but there are exceptions. For example NSString has some optimizations that keeps string references around. Likewise there are UIImage methods, notably imageNamed that cache images. Etc.

But, generally, you shouldn't worry about what the OS is doing. Just make sure you resolve your own strong references and let the OS do what it will do. Generally, if it does caching, these caches are purged upon memory pressure, anyway.

2
votes

My answer to your question

If you want to have a weak attribute to a property someone should have already retained it

.h

@property (nonatomic, weak) NSString *str1;
@property (nonatomic, weak) NSString *str2;

.m

@synthesize str1,str2;


-(void)viewDidLoad
{
   NSString* str0 = @"Hello";
   NSString* str1 = [[NSString alloc] initWithFormat:@"%@", str0 ];
   NSString* str2 = [NSString stringWithFormat:@"%@",str0];
   NSLog(@"Result: str0 %@, str1 %@, str2 %@", str0, str1, str2);
}

Output

Result: str0 Hello, str1 Hello, str2 Hello

Graver answer here

Detailed Explanation from Apple Forum Document

The difference is in how the return values are memory-managed. alloc/initWithFormat: returns a retained string that you will have to release or autorelease yourself. stringWithFormat: returns a string that has already been autoreleased. (An autoreleased object will be released when the current autorelease pool disappears; that usually happens right before the next event is processed.)

Which you should use depends on what you're doing with the string. If it's a temporary string, if it's going to be stored inside another object, or if it's going to be stored in your own object using the "self.property" syntax, use stringWithFormat:; if it's going to be assigned to a global, static or instance variable (without using "self.property"), use alloc/initWithFormat:.

In your case, you could go either way. stringWithFormat: would allow you to remove the explicit release call, but autoreleasing is also very slightly slower than releasing. It's up to you to decide whether you want your code to be smaller and simpler or longer and faster.

0
votes

A weak variable does not hold a reference to an object. The weak variable will be set to nil when the object is deallocated. Since you hold no reference elsewhere, that could happen at any time.

On the other hand, iOS is free to hold other references as it likes. So there is no guarantee that the weak variable will be set to nil. Therefore, according to your code, each variable may be nil, or may be not nil and have the value "Hello".

And that's what you got. Each variable can be set to nil or contain "Hello", and that's what happened.

0
votes

@gnasher729 : I expected to know in which case the weak/strong pointer to NSString stringWithFormat use correct or not, while it is very different with NSString alloc, init. I think that to understand it clearly that would help to make sure when and how I could use properly. By the way, I agreed with @Rob and @user3182143 that NSString stringWithFormat results an autoreleased object. To prove that, I did change to force release that object and it now is pretty clear:

NSString* str0 = @"Hello";
__weak NSString* str1 = [[NSString alloc] initWithFormat:@"%@", str0 ];

__weak NSString* str2_weak = nil;
NSString* str2 = nil;


@autoreleasepool {
    str2_weak = [NSString stringWithFormat:@"%@",str0];
    str2 = [NSString stringWithFormat:@"%@",str0];
}

NSLog(@"Result: str0 %@, str1 %@, str2_weak %@ str2 %@", str0, str1, str2_weak, str2);

Result: str0 Hello, str1 (null), str2_weak (null) str2 Hello

with above modified code, an autoreleasepool block to cover the result of stringWithFormat, str2_weak keep a weak reference to the autoreleased object hence it shall be null after the block; str2 actually keeps own the string hence shall be not released. The output is now 'make sense' to me: