17

I have 1 target in my project and have 3 build configuration

Debug Production & Release

While I am running my app with debug or production configuration and its working fine with no issues at all, but when I change my build configuration to release it's giving me some weird issues like some string values are not storing in plist file and when I debug my release mode, I didn't get any value in my debugger.

After too much investigation on my code and build setting and some suggestions from SO I have done the below change in my Build setting as mentioned below.

Change Optimization Level under Apple LLVM 8.0 Code Generation header

Getting issue in below Optimization level:

enter image description here

Resolved issue with below change in Optimization level:

enter image description here

Don't know why it's working in None[-O0].

Can anyone help me with the above patch?

Here is one sensitive part where the above Optimisation level affected.

My saved bookmark code where I have value but still it's storing NULL in plist.

- (IBAction)bookmark:(id)sender{
    
    DimensionModel *aDimensionModel = APPDELEGATE.selected_dimensionModel;
    EmirateModel *aEmirateModel = APPDELEGATE.selected_emirateModel;
    DivisionModel *aDivisionModel = APPDELEGATE.selected_divisionModel;
    __weak NSString *aStrVCName = self.bookMarkViewModel.selectedStrVCName;
    
    //Add Title string in Dictionary and then in Plist
    __weak NSString *aStrTitle = [NSString stringWithFormat:@"%@",aDimensionModel.dimension_description];
    aStrTitle = [aStrTitle stringByReplacingOccurrencesOfString:@"(null)" withString:@""];
    
    if (IS_ENGLISH) {
        aStrTitle = [aStrTitle stringByAppendingString:[NSString stringWithFormat:@" - %@ - %@ - %@",aDivisionModel.division_name,aEmirateModel.emirate_name,[self.bookMarkViewModel.strDataType isEqualToString:@"Number"]?@"(0)":@"(%)"]];
    }else{
        aStrTitle = [aStrTitle stringByAppendingString:[NSString stringWithFormat:@" - %@ - %@ - %@",aDivisionModel.division_name,[Utilities getEmirateNameinArabicByCode:aEmirateModel.emirate_code],[self.bookMarkViewModel.strDataType isEqualToString:@"Number"]?@"(0)":@"(%)"]];
    }
    
    if (APPDELEGATE.selectedDimRecordTimeModel.record_year == -1) {
        aStrTitle = [aStrTitle stringByAppendingString:[NSString stringWithFormat:@" - %@",[LanguageUtility getLocalizedStringForKey:@"All Years"]]];
    }else{
        aStrTitle = [aStrTitle stringByAppendingString:[NSString stringWithFormat:@" - %ld",(long)APPDELEGATE.selectedDimRecordTimeModel.record_year]];
    }
    
    
    NSMutableDictionary *aMutDicGraphDetail = [NSMutableDictionary new];
    
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",self.bookMarkViewModel.strSelectedDim] forKey:@"DimCode"];
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",aStrVCName] forKey:@"VCName"];
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",PLIST_TITLE_BOOKMARK] forKey:@"PlistBookmark"];
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",IS_ENGLISH?@"ENGLISH":@"ARABIC"] forKey:@"Language"];
    if (self.bookMarkViewModel.strSelectedDim == nil) {
        [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",[LanguageUtility getLocalizedStringForKey:aStrVCName]] forKey:@"Title"];
    }else{
        [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",aStrTitle] forKey:@"Title"];
    }
    
    
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",APPDELEGATE.selectedDimRecordTimeModel.record_time] forKey:@"RecordTime"];
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%ld",(long)APPDELEGATE.selectedDimRecordTimeModel.record_year] forKey:@"RecordYear"];
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",aEmirateModel.emirate_code] forKey:@"EmirateCode"];
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",self.bookMarkViewModel.strDataType] forKey:@"DataType"];
    
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",aDivisionModel.division_code] forKey:@"DivisionCode"];
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%d",APPDELEGATE.isDataTypeSwitchHide] forKey:@"switchHide"];
    [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",APPDELEGATE.strYearOrMonthFromRbtnCtrl] forKey:@"YearOrMonthFromRbtnCtrl"];
    [aMutDicGraphDetail setObject:@(self.bookMarkViewModel.selectedIndexPath).stringValue forKey:DimSelectedIndex];
    
    /* Check for RegionVC  and CountryVC*/
    if ([aStrVCName isEqualToString:@"RegionMainVC"] || [aStrVCName isEqualToString:@"CountryMainVC"]){
        [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%@",self.bookMarkViewModel.strComparison] forKey:@"Comparison"];
        [aMutDicGraphDetail setObject:[NSString stringWithFormat:@"%ld",self.bookMarkViewModel.intSelectedRegionId] forKey:@"RegionId"];
    }
    [self.bookMarkViewModel saveBookmark:aMutDicGraphDetail];
    
    aStrVCName = nil;
}

Using the above code all values are successfully stored into plist but aStrTitle has value in it and still, it's storing as null.

CodeChanger
  • 7,953
  • 5
  • 49
  • 80
  • 2
    Usually when strange things happen only at higher optimization, a bug in your code is relying on undefined behavior. (See, for instance, http://blog.regehr.org/archives/213 and later, and http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html.) Less often, it’s a compiler bug, in which case please post reduced code at either https://llvm.org/bugs/enter_bug.cgi or https://bugreport.apple.com. – Flash Sheridan Sep 26 '16 at 16:54
  • 1
    Dhanesh, could you please provide mcve? http://stackoverflow.com/help/mcve As @FlashSheridan said, most likely, the reason of your problem is that some part of your code relies on undefined behavior. And this UB can be literally anything: from very obvious things like null-pointer dereferencing to somewhat more tricky ones like signed integer overflow or expressions like `i++ + ++i`. And without seeing your actual code no one could tell you what's your problem is and how to solve it. – Dim_ov Oct 03 '16 at 18:24
  • Dim thanks for your comment but its not related to any code and there is not major thing in my project its just showing tableview list just store or pass string data to another view but all are working file with Optimisation level as NONE but its not working in fastest and smallest option and its only creating problem in XCode 8 not in Xcode 7.3 or older version so its not related to code its related to compiler who treat its as wrong way. – CodeChanger Oct 04 '16 at 04:30
  • 1
    @Dhanesh, well, it's possible that you encountered a compiler bug, but it's very-very unlikely to be the case. And even if it's a compiler bug, in order to have it fixed you would need to provide a code to reproduce this buggy behaviour anyway :). I understand that your actual project may contain some sensitive and/or NDA-protected data. But try to make a reduced version of it with fake data/simplified class names/etc. It's impossible to advise you something useful now. Your problem is too broad and may be caused by many different things. – Dim_ov Oct 04 '16 at 16:05
  • 1
    BTW, the fact that your code broke only in Xcode 8 doesn't prove that Xcode or clang is a culprit. Code with UB can work well for decades and then suddenly break after a minor compiler update. – Dim_ov Oct 04 '16 at 16:09
  • @Dim_ov for your request I have added one of my scenario where I am getting issue .. there are many but its one example regarding my issue. – CodeChanger Oct 05 '16 at 05:28
  • 1
    A good starting point is to run your code with the Address Sanitizer enabled! – Joky Oct 08 '16 at 23:41

2 Answers2

9

Your problem is in this line:

__weak NSString *aStrTitle = [NSString stringWithFormat:@"%@",aDimensionModel.dimension_description];

+[NSString stringWithFormat:] creates a new string with retain count equal to zero. And this is usually okay, since this value is either assigned to a strong variable or passed as a parameter to the method call. In both cases it's retain count gets incremented and the value remains untouched. But you are assigning this new string to the __weak variable! Weak variables don't affect retain count so it remains zero after this line executed. And so the created string gets deleted immediately after creation.

Solution is simple: remove __weak specifier and you'll be good to go.

There is no point in declaring local variables as weak (unless they are captured by the blocks). Also "cleanups" like this one: aStrVCName = nil; have no effect at all.

  • All local variables are automatically released when the method finishes its execution.
  • aStrVCName is weak anyway, so it never affects retain counts of the objects it's pointing to.
Dim_ov
  • 1,421
  • 9
  • 14
  • 2
    This not entirely correct. `stringWithFormat:` creates an *autoreleased* string (hidden behind ARC), that is not the same as a string with a release count of 0. It just means it gets deallocated by the autorelease pool the next time it has the chance. That doesn't necessarily happen when the method is left, though obviously the local variable is no longer valid then (which is exactly the point why it needs to be added to the autorelease pool). Using `__weak` in this context is not necessary, though, and I'm not sure whether some ARC specifics resulting from that might indeed be the issue. – Gero Oct 05 '16 at 09:49
  • after removing `__weak` still same issue is there so i don't think so its a issue with ARC. – CodeChanger Oct 05 '16 at 09:56
  • @Gero, yes, technically, you are right, but I think that it's more safe to treat such objects as objects with 0 retain count, than as autoreleased objects. First of all, because, as you said, exact implementation is hidden behind ARC and what today is autoreleased object may tomorrow become something else without warning, and writing code that relies on assumptions about internal implementation of memory management is very risky. And secondly, because such assumptions are already causing bugs when optimizer comes into play :) – Dim_ov Oct 05 '16 at 10:00
  • In general that's true, but we're not making assumptions here. What ARC does is clearly and well defined. "Treating as if..." statements could also be said to lead to the same problems, since that's just another assumption you make. Besides, as you can see @Dhanesh tried it and it didn't work, as I guessed (`__weak` does basically nothing here). Unfortunately I don't see immediately where the problem is either. I might have a look later today, though. – Gero Oct 05 '16 at 10:04
  • 1
    Maybe one more thing. @Danesh, could you please clarify to us *why* you had `__weak` there in the first place? No offense, but it looks like you had a misunderstanding there, so chances might be you had one elsewhere, too. If I can approach the issue from your position that might hep me understand the issue better. – Gero Oct 05 '16 at 10:11
  • Okay, just tried to run it myself with different optimisation levels and @Gero is right: `__weak` does nothing here. I thought that clang "optimizes" `[NSString stringWithFormat:]` to `[[NSString alloc] initWithFormat:]` for which my suspicions would be correct. @Dhanesh, one more thing to try is to NSLog `aStrTitle` value after each use in order to track, when it gets nil-ed. BTW, @Gero, can you point me to the documentation explaining when ARC does use autorelease and when it does not? I remember such specifications and conventions from MRC days but I never saw this documented for ARC. – Dim_ov Oct 05 '16 at 10:41
  • @Garo above weak is putted by mistake as and i have removed that one and one more thing before this code I have used block over there so its there due to strong reference so its there by mistake.Generally I used weak ref for blocks usage. – CodeChanger Oct 05 '16 at 10:44
  • @Dhanesh, if your variables point to the locally-created objects which don't get assigned or passed anywhere else (that is true for `aStrTitle`), then capturing them by strong reference would not create a retain cycle. Moreover, making them weak may get you in a similar trouble. If your block is not executed synchronously right after it's creation, then your object may get deleted by the time your block is called (even if it was an autoreleased object). But if you eliminated all blocks and removing of `__weak ` didn't help, then your problem should be caused by something else... – Dim_ov Oct 05 '16 at 10:58
  • 1
    @Dim_ov: For what it's worth, it's basically all explained [here](https://developer.apple.com/library/content/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW17). Specifically the FAQ, first question. Together with the explanation of [class factory methods](https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaEncyclopedia/ClassFactoryMethods/ClassFactoryMethods.html#//apple_ref/doc/uid/TP40010810-CH8-SW1) I find it reasonable to say "don't pretend ARC does this and this for memory management". :) – Gero Oct 05 '16 at 12:52
5

I had a similar issue when migrating to xcode 8 and swift 3. Turns out, swift doesn't like the fact I override functions from the base classes in extensions. see Overriding methods in Swift extensions

For some reason, the compiler doesn't complain about it, and to make things worse, the "strange issues" only started to happen in the Fastest, Smallest [-Os] optimization level.

Seems that in the Fastest, Smallest [-Os] optimization level, the compiler will call the function from the base class, and not the function from the extension. In the None [-O0] optimization level, the compiler will call the function from the extension.

for example:

public class BaseController : UIViewController {
    override func reloadInterface() {
      println("ok")
    }
}

public class NewController : BaseController {
    override func viewDidLoad() {
      super.viewDidLoad()

      reloadInterface()
     }

}

extension NewController {
   override func reloadInterface() {
      println("extended function")
    }
}

will print out "extended function" in none optimization level, and will print out "ok" in fastest optimization level...

to make story short, don't override functions in swift extensions

Community
  • 1
  • 1
aporat
  • 5,922
  • 5
  • 32
  • 54
  • 1
    That does seem important, and please do file a Swift bug at https://bugs.swift.org. But it can’t be what Dhanesh is complaining about, since his screenshot is for the [Obj]C[++] compiler—the optimizations are ``-O0`` and ``-Os``, not ``-Onone`` and ``-O``. – Flash Sheridan Oct 05 '16 at 03:31
  • @aporat above issue is related to Sift 3.0 and I am getting issue in Objective C class also and most of my code is in Objective c so i don't think so this was the issue with base class and extension. – CodeChanger Oct 05 '16 at 05:12