4

I think I may made a silly mistake, but I can't figure out why:

I have a method and Block to handle some network API like:

-(IBAction)confirm:(id)sender {         
    __weak typeof(self) weakSelf = self;
    __weak NSString *anotherNumber = self.nextPhoneTextField.text;
    [SharedInstance bindNewPhoneNumber:self.nextPhoneTextField.text pinCode:self.verifyCodeTextField.text sucess:^(id result) {

      // update phone number
      SharedInstance.phoneNumber = anotherNumber;

    }]; 
}

before the block, I can see newNumber has value correctly,

However when the block is invoked, the newNumber is nil, instead of the text. But I was able to print weakSelf.nextPhoneTextField.text, which is not changed.

Any explainations is appreciated!

UPDATE:

After creating a sample project, I found it's not reproducible. the weak string pointer has valid text. Then I start debugging it, and I found that,

In order to avoid the new keyword, I changed the pointer name to anotherNumber

In my real project, when calling __weak NSString *anotherNumber = self.nextPhoneTextField.text; the anotherNumber has a new address, rather than the self.nextPhoneTextField.text; address:

(lldb) p anotherNumber
(__NSCFString *) $2 = 0x00007f88b3ff2960 @"12345678901"
(lldb) p self.nextPhoneTextField.text
(__NSCFString *) $3 = 0x00007f88b15f8690 @"12345678901"

However in the sample project, I have the similar function,

- (void)clickBlock:(void (^)(NSString * string))block {
    if (block) {
        block(@"haha");
    }
}

- (IBAction)clicked:(id)sender {
    __weak typeof(self) weakSelf = self;
    __weak NSString *text = self.textField.text;

    [self clickBlock:^(NSString *string) {
        NSLog(text);
        NSLog(string);
    }];
}

it is the same address:

(lldb) p text
(NSTaggedPointerString *) $2 = 0xa000000747365744 @"test"
(lldb) p self.textField.text
(NSTaggedPointerString *) $3 = 0xa000000747365744 @"test"

and the class type changed also... Looking for answers!!!

Another update:

I delete the block, simply create two weak pointers with some strings like "hello" and "12345678901", the formmer one has the same address and marked as NSTaggedPointerString, however the latter one has different address and marked as NSCFString

It seems to me that once the text reach a specific length, it will have the NSCFString and different address, and after some tests, the bounty is 9. once more than 9 words, it will be NSCFString, tested on iOS 9.1 iPhone 6S simulator.

on iOS 8.4 simulator, all the strings with different length result in different mem adress and NSCFString

sample project:https://github.com/liuxuan30/WeakStringPointer

Wingzero
  • 9,644
  • 10
  • 39
  • 80
  • Why not just `SharedInstance.phoneNumber = self.nextPhoneTextField.text;`? – trojanfoe Nov 04 '15 at 07:20
  • no particular reason, just trying. I don't add much logic, so I just read the value before user can change it – Wingzero Nov 04 '15 at 07:22
  • guys, I know other ways, but just curious why the newNumber is nil – Wingzero Nov 04 '15 at 07:23
  • I think maybe it's gone out-of-scope as the reference to it within the block doesn't count? It shouldn't be `weak` anyway; it should be strong and it should be a copy. – trojanfoe Nov 04 '15 at 07:23
  • @trojanfoe need confirmation, not guessing :) – Wingzero Nov 04 '15 at 07:24
  • It's really weird. Try this `__weak UITextField *field = self.nextPhoneTextField` and check its value inside the block. And tell us the output – Sergii Martynenko Jr Nov 04 '15 at 07:24
  • the problem is why weakSelf works but not the weak string – Wingzero Nov 04 '15 at 07:25
  • @SergiiMartynenkoJr `field` is valid object in the block. but the string not :( – Wingzero Nov 04 '15 at 07:26
  • Try yo make a new project (single view app), add a UITextField, set some value, and add a button in which action method do the same thing. But instead of `bindNewPhoneNumber...` method use `dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ });` – arturdev Nov 04 '15 at 07:43
  • @arturdev what's this for... you need a demo project? I think I just need some details why only nsstring not work – Wingzero Nov 04 '15 at 07:45
  • Can you just do that please? – arturdev Nov 04 '15 at 07:50
  • @arturdev I just tried, use __weak for the string pointer, but now it actually has a valid text! why?? – Wingzero Nov 04 '15 at 08:01
  • That's what I wanted to hear :) Check your project settings. Or maybe you have a Category that affects it. – arturdev Nov 04 '15 at 08:03
  • And make sure that your target's `Build Configuration` is set to Debug and not Release – arturdev Nov 04 '15 at 08:05
  • it's debug, but I am truely confused now. what to look for then? I'm using ARC, and it's just a normal block using AFNetworking... – Wingzero Nov 04 '15 at 08:06
  • I am not sure why only the weak string pointer is nil but not weak self in my code. – Wingzero Nov 04 '15 at 08:14
  • Guys, I debugged more, and find the two project has different memory address for setting the text. See my updates in the question. Looking for answers! – Wingzero Nov 04 '15 at 08:45
  • Is the block being invoked asynchronously in your real project? If so, your sample project does not reproduce it correctly. – trojanfoe Nov 04 '15 at 08:46
  • then why it has different class type in my real project and different address? I think it's not related to block, but the setter op. in my real project, given the weak pointer points to another address, it will be dealloc after the execution. – Wingzero Nov 04 '15 at 08:49
  • The asynchronous aspect is very important in order to reproduce the issue. Work through the execution of both sync/async blocks in that code to see the difference. – trojanfoe Nov 04 '15 at 08:52
  • @trojanfoe I don't think so, because before the block, the address is different. – Wingzero Nov 04 '15 at 08:57
  • I tried some different strings, one is "hello" and one is "12345678901" the formmer one has the same mem address, but the latter one has different mem address. uploaded demo project to https://github.com/liuxuan30/WeakStringPointer – Wingzero Nov 04 '15 at 09:11
  • I have tested more, interesting thing is, the text setter has different behaviour on iOS 9 and iOS 8. see the updates. – Wingzero Nov 04 '15 at 09:29
  • @arturdev what I tried is not always true, and I can reproduce it. I doubt Apple has something to do with it. – Wingzero Nov 04 '15 at 09:29

1 Answers1

0

__weak NSString *anotherNumber = self.nextPhoneTextField.text;

with this line , NSString is copied by value and NOT by reference, so after assigning a NSString to another NSString , it creates new copy of it and creates a reference to newly created copy and not the original one, and because the reference is weak , the object will be nil after the current function goes out of context ,

If you try to change the textfield's text it will only change the textFields text and not anotherNumber object.

NSString *test = self.nextPhoneTextField.text;
self.nextPhoneTextField.text = @"Something else";
NSSLog(@"Test object contains %@ , the textField contains %@ ",test,self.nextPhoneTextField.text);

Your code does following:

  1. Creates a new Copy NSString from self.nextPhoneTextField.text
  2. Asigns a new Copy NSString to anotherNumber
  3. Since anotherNumber is __weak , it will not retain the object (NSString), it will hold __weak reference to this object and after this function goes out of context and it becomes nil.

To confirm this behavior, you can directly log anotherNumber after setting its value and in different context

__weak NSString *anotherNumber = self.nextPhoneTextField.text;
NSString *strongAnotherNumber = self.nextPhoneTextField.text;
NSLog(@"Weak number - %@ , strong - %@",anotherNumber,strongAnotherNumber);
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"Block Weak number - %@ , strong - %@",anotherNumber,strongAnotherNumber);
});

please also have a look at Why do weak NSString properties not get released in iOS?

Community
  • 1
  • 1
ogres
  • 3,660
  • 1
  • 17
  • 16
  • Using your code snipper, `anotherNumber` is logged correctly, and not `nil`, as you stated in your answer. – Iulian Onofrei Nov 04 '15 at 10:12
  • yeah , its still there until the function is still running and context is not released – ogres Nov 04 '15 at 10:31
  • You could also write out comments after `NSLog` calls that prints the expected output. – Iulian Onofrei Nov 04 '15 at 10:35
  • have a look at NSString trickery - http://stackoverflow.com/questions/11107729/why-do-weak-nsstring-properties-not-get-released-in-ios , this will only happen to NSString , other objects would have been released at the same time, NSString tries to reuse the strings and thats why its not releasing at the same time, but its released after the context – ogres Nov 04 '15 at 10:46
  • @ogres, try "123456" on iOS9 with my demo project, you will see they are the same address, as NSTaggedPointerString. – Wingzero Nov 04 '15 at 11:06
  • also, are you saying the op '=' for NSString is not like other classes' op? Any doc? – Wingzero Nov 04 '15 at 11:08
  • yeah , it tries to reduce memory usage by reusing the string literals – ogres Nov 04 '15 at 11:08
  • you should always compare string with isEqualToString method – ogres Nov 04 '15 at 11:09
  • so you think the inconsistence is not bug? I am surprised because the length on iOS9 matters so much. One is by value and one is by reference. – Wingzero Nov 04 '15 at 11:09
  • I don`t think its a bug, NSString is immutable and created only once, compiler and then the system tries to reuse the same immutable NSString when its possible , you wont be able to do anything with NSString , right ? only to read or create a new NSString , so its safe for them to just reuse NSString when its possible and reduce memory usage – ogres Nov 04 '15 at 11:11
  • so you are saying, the op '=' for NSString is a copy method? not like other classes? – Wingzero Nov 04 '15 at 11:18
  • I need some doc for `NSString is copied by value and NOT by reference`, do you have any? – Wingzero Nov 04 '15 at 11:19
  • read the doc about NSString at Apple , Quote :" Over distributed-object connections, mutable string objects are passed by-reference and immutable string objects are passed by-copy." https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/ – ogres Nov 04 '15 at 11:20