using weak qualifier for variable in a method - ios

- (void)showWeakValue
{
NSString * __weak weakString = [[NSString alloc] initWithFormat:#"First Name: AA"];
NSNumber * __weak weakNum = [[NSNumber alloc] initWithInt:10];
NSLog(#"weakString = %#", weakString);
NSLog(#"weakNum = %#",weakNum);
}
the output is
weakString = (null)
weakNum = 10
why is weakNum not null? because there's no other strong reference to weakNum, it should be deallocated immediately after assignment. right?

NSNumber is toll-free bridged to CFNumber, which is open source.
From http://www.opensource.apple.com/source/CF/CF-476.19/CFNumber.c
you can see that NSNumber caches the created objects for integer values between
(-1) and 12, so that the object returned by
[[NSNumber alloc] initWithInt:10]
is never deallocated, and multiple calls return the same instance.
For
[[NSNumber alloc] initWithInt:1000]
you will probably see a different behaviour.

I think either weakNum is deallocated immediately (as expected) and your code then invokes undefined behavior, or NSNumber is making tricky optimizations and you get back a singleton which always holds the value 10 and is alive throughout the lifetime of the program.
Edit: aham, huh, nope. I just logged the pointer itself using
NSLog(#"weakNum pointer: %016llx", (uint64_t)weakNum);
and got
weakNum pointer: 000000000003e883
Now that's weird. A pointer-to-struct which is not 16 byte-aligned? So this is a tagged pointer.
Let's analyze the pointer value! The 0th (LSB) bit is set, that's clear - that indicates the tagged pointer.
The 1st bit is set too: according to the reference behind the link, that's the tagged object class for integers. That matches our expectations. That's the 0th nibble.
Then in the next (1st) nibble, i. e. bits 4...7, we have the value 8, which is binary 100, for which the quoted reference says 0100 for 16-bit integers. That's exact, 1000 doesn't fit into 8 bits, but it does fit into 16 bits.
And then what do we have next? 3e8, which is the hexadecimal for decimal 1000.
So, we've proved that this is a tagged pointer - at least on the system I tested this on (OS X 10.7.5 64-bit).

Related

How to understand ARC in objc

The code is as follows, Why the output of the following reference count is 1,2,3,4 respectively?
NSDictionary __weak *weak_dict;
#autoreleasepool {
//生成并持有对象 alloc,new,copy,mutableCopy (注意以这些词开头的命名)
NSDictionary *dict = [ [NSDictionary alloc] initWithObjects:#[#1, #2] forKeys:#[#"a", #"b"] ];
NSDictionary *dict1 = #{ #"1":#"c" };
weak_dict = dict;
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(dict)); //1
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(weak_dict)); //2
NSLog(#"%# CFGetRetainCount %ld", weak_dict, CFGetRetainCount((__bridge CFTypeRef)weak_dict) ); //3
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(weak_dict)); //2
}
First, it's critical that your realize that retainCount is useless. Using an internal function like _objc_rootRetainCount doesn't change that. The values output are completely compiler-dependent and can change at any time due to optimization or framework changes. While there's a certain benefit to digging into the implementation details, chasing the actual value of retain counts more often causes developers to go astray rather than illuminate anything. (But do read the comments between Alexander and me below; this is all true, but also misleading. There are reasons to look at retain counts.)
That said, it is not magic, and is totally knowable. There just isn't any specific rule. You have to look at exactly what the optimizer has done. All discussion here is for Xcode 13.0 Beta 5 in Debug, built for Mac ARM. Other versions or configurations could return different values because some of the retains here could be removed if the optimizer were smarter (and it might be in the future). You can explore this all by looking at the Assembly output, or by using the Hopper decompiler (which is what I did).
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(dict)); //1
No surprises here I hope. _objc_rootRetainCount prints the retain count and that's 1 because you called alloc to create it. There's no promise that this be 1, though. It could be any positive value. The only thing you're actually promised that you are responsible for sending exactly one release, and if you do that, everything will balance.
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(weak_dict)); //2
Since weak_dict is weak, it needs to first create a strong reference before passing to _objc_rootRetainCount (generally the caller for a function is responsible for ensuring that the arguments it passes continue to exist for the lifetime of the call). This effectively behaves like:
id strong_weak_dict = [weak_dict retain];
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(strong_weak_dict));
[strong_weak_dict release];
A smarter future optimizer might be able to figure out that the existing dict reference is equivalent and avoid this extra retain/release. There is no promise that it happens. The only requirement is that the caller must ensure the argument's lifetime extends beyond the call to _objc_rootRetainCount. It can do that any way it likes.
NSLog(#"%# CFGetRetainCount %ld", weak_dict, CFGetRetainCount((__bridge CFTypeRef)weak_dict) ); //3
Same thing here, there are just two copies of weak_dict passed to two different functions (NSLog and CFGetRetainCount). The compiler inserts two retains and two releases:
id strong_weak_dict = [weak_dict retain];
[strong_weak_dict retain];
NSLog(#"%# CFGetRetainCount %ld", strong_weak_dict, CFGetRetainCount((__bridge CFTypeRef) strong_weak_dict) );
[strong_weak_dict release];
[strong_weak_dict release];
It could be smarter and get rid of one of those (or it could even get rid of both if it realized that dict is doing the job already).
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(weak_dict)); //2
And as above, passing weak_dict wraps a retain/release, so you get 2.
One slightly odd thing in the output from 13.0 Beta 5 is that dict1 has an extra retain with no corresponding release. I'm not sure why that happens. (This is fine since retain and release are no-ops for constants, I just don't understand why the call to retain is inserted.)
There are several details I glossed over here to make the behavior a bit simpler. The compiler doesn't call retain, it calls objc_loadWeakRetained. The specific way it handles the "double retain" includes an extra temporary variable. And there's a call to objc_storeWeak that I didn't discuss, but also doesn't directly impact your question.
There is a performance lesson in here, which is that unnecessary use of weak can insert a lot of extra retains and releases, which are not free. Generally you should convert weak pointers to strong pointers one time in a function and then use the strong pointer. This also ensures that the object survives the entire function, which is better for reasoning about it. Weak pointers can become nil between one statement and the next. So this is a good question and worth exploring; you just shouldn't rely on any of these details.
Here's the full pseudo-C decompiled output:
int _main(int arg0, int arg1) {
var_48 = objc_autoreleasePoolPush();
var_20 = [objc_alloc() initWithObjects:r2 forKeys:0x1000080f8];
[0x100008110 retain];
objc_storeWeak(&saved_fp - 0x18, var_20);
stack[-192] = _objc_rootRetainCount();
NSLog(#"dict retain count : %ld", #selector(initWithObjects:forKeys:));
var_68 = objc_loadWeakRetained(&saved_fp - 0x18);
stack[-192] = _objc_rootRetainCount();
NSLog(#"dict retain count : %ld", #selector(initWithObjects:forKeys:));
[var_68 release];
var_80 = objc_loadWeakRetained(&saved_fp - 0x18);
r0 = objc_loadWeakRetained(&saved_fp - 0x18);
var_78 = r0;
r0 = CFGetRetainCount(r0);
stack[-192] = var_80;
*(&stack[-192] + 0x8) = r0;
NSLog(#"%# CFGetRetainCount %ld", #selector(initWithObjects:forKeys:), 0x1000080e0);
[var_78 release];
[var_80 release];
var_98 = objc_loadWeakRetained(&saved_fp - 0x18);
stack[-192] = _objc_rootRetainCount();
NSLog(#"dict retain count : %ld", #selector(initWithObjects:forKeys:));
[var_98 release];
objc_storeStrong(&saved_fp - 0x38, 0x0);
objc_storeStrong(&saved_fp - 0x20, 0x0);
objc_autoreleasePoolPop(var_48);
objc_destroyWeak(&saved_fp - 0x18);
return 0x0;
}

If NSString is class and it is passed by reference then why is this output? [duplicate]

I have a simple question. If I am declaring NSString (ref type) as shown below:
NSString *johnsMoney = #"200";
NSString *marysMoney = johnsMoney;
NSLog(#"Johns Money %#",johnsMoney);
NSLog(#"Marys Money %#",marysMoney);
johnsMoney = #"100";
NSLog(#"Johns Money %#",johnsMoney);
NSLog(#"Marys Money %#",marysMoney);
The output produced is this:
Johns Money 200
Marys Money 200
Johns Money 100
Marys Money 200
From my understanding when I assign #"100" to johnsMoney should it not also change the value of "marysMoney" to 100 since marysMoney is pointing to johnsMoney.
UPDATE:
I believe the following example shows what I initially was trying to do:
Dog *dog1 = [[Dog alloc] init];
dog1.name = #"Dog 1";
Dog *dog2 = dog1;
NSLog(#"Dog 1 %#",dog1.name);
NSLog(#"Dog 2 %#",dog2.name);
dog1.name = #"Dog 3";
NSLog(#"Dog 1 %#",dog1.name);
NSLog(#"Dog 2 %#",dog2.name);
johnsMoney and marysMoney are both pointers to strings.
When you write johnsMoney = #"100", it now points to a different string. This doesn't change marysMoney which still points to the original string.
If you were using NSMutableString, and you did [johnsMoney setString:#"100"], then it would change the underlying data (to which both variables would still be pointing).
They are not references. They are object pointers. If two variables happen to point to the same object, changing one pointer to point to another object has no effect on the other pointer.
Think of two people standing near each other. Both hold out an arm and point to the same table. Now one person turns and points to a chair. The other person isn't affected. They are still pointing to the table.
No. Remember you are dealing with pointers. So when you do
johnsMoney = #"100";
You are setting the johnsMoney pointer to a different memory address which contains the #"100" value. marysMoney still points to the original address with the #"200" value.
In your example the local variable marysMoney maintains a strong reference to the initial johnsMoney object. When the johnsMoney property is changed, the property no longer keeps a strong reference to the original value, but that value is still kept alive by the marysMoney strong variable.
#"200" is objective-c notation for an NSString object. It will have it's own memory space and johnsmoney will point to it. So, marysmoney never really points to johnsmoney.
What actually happens is this...
Johns Money 200 // pointer 1
Marys Money 200 // pointer 1
Johns Money 100 // pointer 2
Marys Money 200 // pointer 1
johnsmoney points to #"200". marysmoney also points to #"200". When johnsmoney gets assigned #"100", johnsmoney points to #"100". While marysmoney still points to #"200".
suppose the string :
#"200" pointer address : 0xeeff
#"100" pointer address : 0xeeaa
so ,your code may change like these:
NSString *johnsMoney = #"200";
(johnsMoney = 0xeeff)
NSString *marysMoney = johnsMoney;
(marysMoney = 0xeeff)
johnsMoney = #"100";
(johnsMoney = 0xeeaa)
(marysMoney = 0xeeff)
marysMoney pointer address not changed,but johnsMoney pointer address changed.
As the same:
suppose the object :
dog1 pointer address : 0xeeff
so ,your code may change like these:
Dog *dog1 = [[Dog alloc] init];
(dog1 pointer address: 0xeeff)
dog1.name = #"Dog 1";
Dog *dog2 = dog1;
(dog2 pointer address: 0xeeff)
dog1.name = #"Dog 3";
(dog1 pointer address still: 0xeeff)
(dog2 pointer address still: 0xeeff)
As they all point to the same address,they both changed。
in simply.
NSString *johnsMoney = #"200";
//here johnsMoney is declared as NSString, so it will be string type.
//any thing given inside #"" will be considered as string.
//currently, johnsMoney contains 200.
NSString *marysMoney = johnsMoney;
//here marysMoney is declared as NSString, so it will be string type.
//johnsMoney is already a string. therefore, marysMoney automatically reads the
//string in johnsMoney. so currently, marysMoney will also be 200.
NSLog(#"Johns Money %#",johnsMoney);
NSLog(#"Marys Money %#",marysMoney);
//both will be printed as 200. right?
johnsMoney = #"100";
//now johnsMoney reassigned as 100.
//but still marysMoney is 200.
//there is nothing written for changing maryMoney from 200.
NSLog(#"Johns Money %#",johnsMoney);
NSLog(#"Marys Money %#",marysMoney);
so i think you've got it. i don't want to think it in a complicated manner by including the pointers.
Note:if any one feels that it is rubbish, kindly please avoid my answer. am sorry to post it. i just only vomited the pointer concept. i don't know how much correct is my answer.
NSString is value type. (Immutable).
And there is a concept of sting iterning as well.
NSString *johnsMoney = #"200";
NSString *marysMoney = johnsMoney;
johnsMoney = #"100";
So when you are changing the string of johnsMoney, its now pointing to new memory address. But marysMoney still having old sting (i.e. 200) so pointing to previous address.
Go to this tutorial, you will learn really new things.
https://nshipster.com/equality/

Passing an object around increases retain count

iOS, transitioning to ARC. I've observed a curious behavior regarding CF/NS bridging. In the following scenario:
CFStringRef cfs = ComesFromSomewhere();
NSString *ns = (__bridge NSString*)cfs;
the retain count of the string object is 2 at the end. However, in the following:
NSString *ToNS(CFStringRef cfs)
{
return (__bridge NSString*)cfs;
}
CFStringRef cfs = ComesFromSomewhere();
NSString *ns = ToNS(cfs);
the retain count is 3 at the end. What's going on, please? Who holds the extra reference? Is the object being added to the autorelease pool by the mere act of passing it around?
Preemptive response to "don't worry, ARC just works": I'm mixing Core Foundation with Cocoa here, no way around it. This is leak prone. Without the ability to account for the retain counts explicitly, I'm flying blind.
EDIT: it's an artifact of the debug build. In the release build, the retain count under the latter scenario is still 2.
There's a tangible difference between a fragment that leaves large autoreleased objects around and one that doesn't; you don't want the former in a big loop without a pool in the loop body. Helps to know it's an artifact of zero optimization, but still, not cool.
CFStringRef cfs = ComesFromSomewhere();
// retainCount -> 1
NSString *ns = ToNS(cfs);
// ToNS(cfs)
//
// ToNS is not object creating method,
// thus the returned object was automatically autoreleased
// retainCount += 1
// NSString *ns
//
// It's __strong variable, ns variable has an ownership of the object
// retainCount += 1
// retainCount -> 3
The definition of object creating method is a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”, of a Objective-C class. See Basic Memory Management Rules - You own any object you create.
In the release build, the retain count under the latter scenario is still 2.
Also compiler can omit to send autorelease message to object if it's eligible.
EDITED
You can use C++ reference to avoid autorelease.
void ToNS(CFStringRef cfs, NSString __strong *& ns)
{
ns = (__bridge NSString*)cfs;
}
NSString *nsstr;
ToNS(cfstr, nsstr);
// retainCount -> 2
EDITTED
NS_RETURNS_RETAINED NSString *ToNS(CFStringRef cfs)
{
return (__bridge NSString*)cfs;
}
NS_RETURNS_RETAINED makes the framework treat the function as an object creating one (which it really is). Cocoa has a name convention that lets you designate a method as an object creator, but the convention only applies to Objective C class methods, not to C style functions and not to C++ class member functions.

type casting in objective-c, (NSInteger) VS. integerValue

I don't very understand about the diference between (NSInteger)aNumberValue and [aNumberValue integerValue], then do some test. For example, here is a response data from server:
You can see it's an int but the value is hold by NSNumber. I retrieve the data by writting NSInteger count = (NSInteger) dic[#"count"];, and in Xcode debug area, saw this:
it's really a strange value but when I run po count and saw this:
anyway, the value is correct, but another strange thing is:
the number 2 is not less than 100!
Then I try NSInteger a = [dic[#"count"] integerValue] and saw the normal value in Xcode debug area:
and:
So, I am a little bit confused, what's the deference between (NSInteger)aNumberValue and [aNumberValue integerValue]?
NSNumber is a class; NSInteger is just a typedef of long, which is a primitive type.
dic[#"count"] is a pointer, which means that dic[#"count"] holds an address that points to the NSNumber instance. NSNumber has a method called integerValue which returns an NSInteger as the underlying value that the NSNumber instance represents. So you can conclude that [dic[#"count"] integerValue] gets you a long, and that's how you retrieve the value out of NSNumber.
You don't retrieve the value of NSNumber by casting it to NSInteger. That's because dic[#"count"], as I said, is a pointer. So by writing
NSInteger count = (NSInteger) dic[#"count"];
you are actually casting the pointer itself to an NSInteger, which has nothing to do with the actual represented value. The value 402008592 you see is just a decimal representation of the value of the pointer, which is an address.
The command po is used for printing objects, so lldb will actually try to print out the object at the address of count. That's why you get 2 back using po. You can try p count and you'll get 402008592.
About po count < 100: The expression (count < 100) is evaluated first; since count is really just an NSInteger of 402008592, it will evaluate to false.
The root issue is that Objective-C collection classes can only store Objective-C objects, not primitive types like float, int or NSInteger.
Therefore NSNumber provides a mechanism to store numbers and booleans in object form.
The value 402008592 looks like an address, so it's probably an NSNumber object containing an NSInteger value.
Don't get confused by the NS prefix of NSInteger and NSUInteger; they are still primitive types and not objects like NSNumber.

Right way to use __attribute__((NSObject)) with ARC?

I just use CFNumber as a example,so it can be any type don't have a Fundation toll-free part!
I just write some test code like this:
typedef __attribute__((NSObject)) CFNumberRef MYNumberRef;
int main(int argc, const char * argv[])
{
#autoreleasepool {
MYNumberRef ptr = NULL;
double myDouble = 10.1;
ptr = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
CFIndex count = CFGetRetainCount(ptr);
}
return 0;
}
It is very strange that the count is 2. But if I use CFNumberRef, the count is 1. It seems the arc don't take the CFType name convention into account, it just retains the return value.
So if I use the __attribute__((NSObject)) to declare CFType property. This post said you shouldn't have to explicitly nil them out in dealloc. But if I use like this:
#property (strong, nonatomic, readwrite) __attribute__((NSObject)) CFNumberRef number;
Then:
self.number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
There is no memory leak if I don't release it in the dealloc method? Maybe I should use it like this:
CFNumbeRef ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
self.number = ref;
CFRelease(ref);
Does Apple say something about this?
The type attribute __attribute__((NSObject)) does not mean that ARC will manage the memory for that type exactly the same way as if that type was an Objective-C object.
The attribute only means that in situations where the compiler automatically generates code for handling that type and the generated code is different depending on whether that type is a normal pointer or an Objective-C object pointer, the compiler should generate the code version for Objective-C objects.
This basically only influences two situations (as of today):
When using these pointers within blocks (^{ }), as objects get automatically retained when being captured by blocks and released again when the block is destroyed.
When assigning values to properties (#property) because when assigning objects to strong (= retain) properties, they get retained and the old property value gets released.
Both situations have nothing to do with ARC. Even in a non-ARC environment the compiler will generated code that works this way.
The reason why your code didn't work as expected is simply because it is wrong.
self.number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
CFNumberCreate() creates a new object with a retain count of (at least) 1 but this object is not managed by ARC, applying the attribute __attribute__((NSObject)) to the type won't change that. So somewhere this create must be balanced with a release.
When you assign the value to self.number, it gets retained again, this time because of the __attribute__((NSObject)) attribute, so now the retain count is (at least) 2. When your object is deallocated, self.number is set to nil, which balances the second retain but there is nothing that would balance the initial create and thus the object will leak.
Consider this code:
#property(nonatomic) NSNumber * number;
self.number = [[NSNumber alloc] initWithInt:10];
Without ARC this code would leak as well, because again, alloc/init produces an object with a retain count of (at least) 1, when you assign it to self.number, the retain count becomes (at least) 2. Even if you do self.number = nil; in dealloc, this only balances the retain of the property, but where would the alloc/init get balanced?
Only with ARC this code won't leak as with ARC, the code the compiler generates looks like this:
NSNumber * tmp = [[NSNumber alloc] initWithInt:10];
self.number = tmp;
[tmp release];
And last line balances the first line, the object is still kept alive by self.number, unless you set self.number = nil; in dealloc which ARC will also do for you so with ARC, there is nothing else you have to do because ARC takes care of any object created with alloc.
That said, keep in mind that
[NSNumber numberWithInt:10]
which is the same as #(10) in modern Obj-C, is in fact equal to
[[[NSNumber alloc] initWithInt:10] autorelease]
But even when you use ARC, it won't care for references created by CFNumberCreate(). To fix your code, it should be:
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
self.number = num;
CFRelease(num);
Now the last line balances the first line, but the property keeps the CFNumberRef alive and this is automatically balanced by dealloc when using ARC or when not using ARC, must be balanced with a self.number = nil; in dealloc.
If you use ARC, you can also do this instead:
self.number = (__bridge CFNumberRef)(__bridge_transfer id)
CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
(__bridge_transfer id) tells the compiler that the value returned by CFNumberCreate() is an Objective-C object that has a retain which needs to be balanced and ARC should take ownership of this object and take care of the balance. (__bridge CFNumberRef) then casts the type back to CFNumberRef but without transferring ownership away from ARC. Without ARC the code above leaks again, yet the compiler will warn you without ARC that bridge casts do nothing.
By the way, to transfer ownership away from ARC, the cast would be (__bridge_retained CFNumberRef), in which case ARC will once retain the object for you and then give you a reference that you must release yourself to balance that retain (now you own the object reference and ARC won't manage it anymore for you).
Do not do this.
Apple does have something to say about it in the Clang documentation:
The use of __attribute__((NSObject)) typedefs is not recommended. If it’s absolutely necessary to use this attribute, be very explicit about using the typedef, and do not assume that it will be preserved by language features like __typeof and C++ template argument substitution.
CFGetRetainCount is meaningless. Worse than meaningless because you think it might mean something.

Resources