Some advice requested on debugging a difficult problem. iOS.


Alex Zavatone
 

In the iOS app that I’m working on now, we have a problem that I’ve never seen in my years as an iOS programmer.

While we have a 99.7% crash free user experience, what does crash is weird as fuck.

By that, I mean that data that normally exists in a dictionary most of the time suddenly becomes nil.

It was today when I noticed this.

@property(strong, nonatomic, readwrite,getter=getRootAPIData) NSDictionary *rootUrlMap;

See how we have an accessor defined for rootUrlMap to be getRootAPIData? (Yes, that get is bad form, i know.)

Well, we never use it.

We always access it like so, [AppEnvironment sharedEnvironment].appContext.rootUrlMap;

And there is only one instance of getRootAPIData in the app… where it is defined.

And here’s my question. Would directly accessing the property via the property name, even when its getter is defined to be something else, would this occasionally result in a nil return?

So, what happens when you define a getter of an object’s property to be something else, yet still try to get a value from the name of the property?


We have a lot of sloppy programming, including a lot of the hacky safeObjectForKey: to prevent crashing when accessing a value from a dictionary when you assume that the dictionary is not nil and I’m trying to sort out all this slop.

Thanks in advance,
Alex Zavatone


Alex Zavatone
 

I just replicated this in a sample and ran a short test to see what the results are.  

@interface MyObject : NSObject

@property(strong, nonatomic, readwrite,getter=getRootAPIData) NSDictionary *rootUrlMap;

@end



@implementation MyObject

- (id)init {
    if (self == [super init]) {
        _rootUrlMap = [NSDictionary dictionaryWithObjectsAndKeys:@"Hi",@"myKey", nil];
    }
    return self;
}

@end



    MyObject *myObject = [MyObject new];

    

    NSLog(@"%@", myObject.rootUrlMap);
    NSLog(@"%@", myObject.getRootAPIData);
    NSLog(@"%@", [myObject getRootAPIData]);


2019-03-26 11:01:30.116353-0500 Test[80252:7074112] {
    myKey = Hi;
}
2019-03-26 11:01:30.116528-0500 Test[80252:7074112] {
    myKey = Hi;
}
2019-03-26 11:01:30.116654-0500 Test[80252:7074112] {
    myKey = Hi;
}

So, the getter and the property name both allow accessing the property.

But should we expect it to if the getter has been redefined?

Thanks again.
Alex Zavatone


On Mar 26, 2019, at 10:53 AM, Alex Zavatone via Groups.Io <zav@...> wrote:

In the iOS app that I’m working on now, we have a problem that I’ve never seen in my years as an iOS programmer.

While we have a 99.7% crash free user experience, what does crash is weird as fuck.

By that, I mean that data that normally exists in a dictionary most of the time suddenly becomes nil.

It was today when I noticed this.

@property(strong, nonatomic, readwrite,getter=getRootAPIData) NSDictionary *rootUrlMap;

See how we have an accessor defined for rootUrlMap to be getRootAPIData?  (Yes, that get is bad form, i know.)

Well, we never use it.

We always access it like so, [AppEnvironment sharedEnvironment].appContext.rootUrlMap;

And there is only one instance of getRootAPIData in the app… where it is defined.

And here’s my question.  Would directly accessing the property via the property name, even when its getter is defined to be something else, would this occasionally result in a nil return?

So, what happens when you define a getter of an object’s property to be something else, yet still try to get a value from the name of the property?


We have a lot of sloppy programming, including a lot of the hacky safeObjectForKey: to prevent crashing when accessing a value from a dictionary when you assume that the dictionary is not nil and I’m trying to sort out all this slop.

Thanks in advance,
Alex Zavatone




 



On Mar 26, 2019, at 8:53 AM, Alex Zavatone via Groups.Io <zav@...> wrote:

@property(strong, nonatomic, readwrite,getter=getRootAPIData) NSDictionary *rootUrlMap;

See how we have an accessor defined for rootUrlMap to be getRootAPIData?  (Yes, that get is bad form, i know.)

At the runtime level, properties are implemented as getter and/or setter methods. The `getter=` syntax just lets you rename the getter method. It doesn’t affect the property name.

It seems a little weird that you can use the custom getter method name with property syntax; I guess that’s just because for backward-compatibility reasons Obj-C lets you call any no-arguments method using “.” syntax (if it has a non-void return type.)

I can’t imagine how this would cause any weird results like you’re getting. You get exactly the same compiled code, whichever name you use to access the property.

—Jens


Quincey Morris
 

Yes, unfortunately, that is the correct behavior. This is described here:


under “Search Pattern for the Basic Getter”, step 4:

"If no simple accessor method or group of collection access methods is found, and if the receiver's class method accessInstanceVariablesDirectly returns YES, search for an instance variable named _<key>, _is<Key>, <key>, or is<Key>, in that order. If found, directly obtain the value of the instance variable and proceed to step 5."

In other words, as long as the underlying instance variable is “_rootURLMap”, using a method named “rootURLMap” will find it.

I strongly recommend overriding class method “accessInstanceVariables” to return NO in *all* data model classes in all production apps. There’s been no valid use case for the legacy behavior of step 4 for 10 years or more, except for compatibility with older code that might have accidentally relied on it.

On Mar 26, 2019, at 09:09 , Alex Zavatone via Groups.Io <zav@...> wrote:

I just replicated this in a sample and ran a short test to see what the results are.  

@interface MyObject : NSObject

@property(strong, nonatomic, readwrite,getter=getRootAPIData) NSDictionary *rootUrlMap;

@end



@implementation MyObject

- (id)init {
    if (self == [super init]) {
        _rootUrlMap = [NSDictionary dictionaryWithObjectsAndKeys:@"Hi",@"myKey", nil];
    }
    return self;
}

@end



    MyObject *myObject = [MyObject new];
    
    NSLog(@"%@", myObject.rootUrlMap);
    NSLog(@"%@", myObject.getRootAPIData);
    NSLog(@"%@", [myObject getRootAPIData]);


2019-03-26 11:01:30.116353-0500 Test[80252:7074112] {
    myKey = Hi;
}
2019-03-26 11:01:30.116528-0500 Test[80252:7074112] {
    myKey = Hi;
}
2019-03-26 11:01:30.116654-0500 Test[80252:7074112] {
    myKey = Hi;
}

So, the getter and the property name both allow accessing the property.

But should we expect it to if the getter has been redefined?



Jack Brindle
 

Alex;

I think you may be going down the wrong path on this one. It sounds more like a second instance is being created somehow. Did you guard the init method so that no one can create a second one, or are you sure that the dictionary is not being released before you actually use it?

As for guarding the init - in a singleton I always have the singleton accessor call a method named something like initOnce that is not accessible outside the class file, the code an init method that returns nil. Anyone calling init on the class will get the nil response.

Hope this helps.

Jack

On Mar 26, 2019, at 8:53 AM, Alex Zavatone via Groups.Io <zav@...> wrote:

In the iOS app that I’m working on now, we have a problem that I’ve never seen in my years as an iOS programmer.

While we have a 99.7% crash free user experience, what does crash is weird as fuck.

By that, I mean that data that normally exists in a dictionary most of the time suddenly becomes nil.

It was today when I noticed this.

@property(strong, nonatomic, readwrite,getter=getRootAPIData) NSDictionary *rootUrlMap;

See how we have an accessor defined for rootUrlMap to be getRootAPIData? (Yes, that get is bad form, i know.)

Well, we never use it.

We always access it like so, [AppEnvironment sharedEnvironment].appContext.rootUrlMap;

And there is only one instance of getRootAPIData in the app… where it is defined.

And here’s my question. Would directly accessing the property via the property name, even when its getter is defined to be something else, would this occasionally result in a nil return?

So, what happens when you define a getter of an object’s property to be something else, yet still try to get a value from the name of the property?


We have a lot of sloppy programming, including a lot of the hacky safeObjectForKey: to prevent crashing when accessing a value from a dictionary when you assume that the dictionary is not nil and I’m trying to sort out all this slop.

Thanks in advance,
Alex Zavatone


Chris Hanson
 

On Mar 26, 2019, at 3:53 PM, Quincey Morris <quinceymorris@...> wrote:

Yes, unfortunately, that is the correct behavior. This is described here:


under “Search Pattern for the Basic Getter”, step 4:

The document you cite is about Key Value Coding, not about Objective-C properties. The latter are not implemented using the former.

If a @property declaration specifies a getter or setter method, the compiler will emit a call to that when dot syntax is used to access the property. Dot syntax does not use -valueForKey: or -setValue:forKey: under the hood, just straight objc_msgSend().

  — Chris