Re: Making an array property without a backing store. How?

2015-04-07 Thread Graham Cox

> On 8 Apr 2015, at 3:58 am, Daryle Walker  wrote:
> 
> Looking at the debugger again, I noticed that my BOOL property with a custom 
> getter (which simulates its result based on other member data) also has a 
> “_myFlag” backing store. Is there some setting we’re missing, or do I have to 
> file a bug to add no-auto-@synthesize?
> 


Others have given you a much more complete answer than I did about 
-myDatumList, but just to pick up on this one.

I assume you've declared this something like:

@property (assign) BOOL  myFlag;

But then implemented it something like:

- (BOOL)  myFlag
{
return someOtherMemberData.flag;
}


Without any @synthesize for it.

The problem here is that the property is declared (assign). That means it's 
something that requires both a getter and a setter. So it has auto-synthesized 
the setter, as you only supplied the getter. In auto-synthesizing the setter, 
it has to provide backing store for it, hence _myFlag.

To fix that, you have a couple of options depending on what you need.

First, you could declare the property readonly. Then, the compiler knows no 
setter is required and won't synthesize one, and won't create backing store.

The other is to write the setter method as well. It could either set the 
indirect property, or just do nothing (though in the latter case that would be 
a readonly property, so you'd be better off declaring it as such).



As I mentioned, I feel that auto-synthesizing generally is somewhat evil. It 
adds code that you can't see and can't easily debug, and adds ivars that you 
can't see which may be masking (or masked by) other ivars that you might assume 
are the ones in use (if you forgot a corresponding @synthesize, which is an 
easy error to make since auto-synthesize covers it up). Since you can't turn 
off auto synthesizing, the next best thing is at least to enable the warning 
that it's done it: CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS, shown as 
"Implicit Synthesized Properties" in the build settings.

Code that isn't magic is more readable, so for me declaring explicit ivars, 
writing getters, setters or @synthesize statements is always better than 
leaving it to invisible compiler magic, even if it adds a little more work. 
That small amount of extra work is easily repaid in time saved debugging an 
auto-synthesized ivar that you didn't even know was there. It has happened to 
me so many times now that I can usually find the problem quickly because I can 
smell the symptoms, but I feel for newbies to this stuff who have been misled 
into writing hard-to-debug code by something that really saves almost nothing 
in coding effort. 

@property was a great improvement to Obj-C, but auto-synthesizing them was not. 
One of Apple's less useful design decisions, IMHO.

--Graham



___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Making an array property without a backing store. How?

2015-04-07 Thread Greg Parker

> On Apr 6, 2015, at 7:01 PM, Daryle Walker  wrote:
> 
> I have an object like:
> 
> @interface MyClass : NSObject
> @property (readonly) NSArray *  myDatumList;
> @property NSArray *  myDataList;
> @end
> 
> The second member is meant to be an actual data member, an array of mutable 
> dictionaries. The first member isn’t supposed to have a backing store; 
> accessing a member of the first array references the corresponding member of 
> the second, then access a dictionary attribute with a custom key.
> 
> I have a custom array count and array element read methods for the first 
> member.
> 
> The program does nothing now. I checked with my limited debugging skills that 
> the second member has one element, but the first member is looped though as 
> if it has no elements. I see that BOTH “_myDatumList” and “_myDataList” 
> internal members exist, with the former set as NIL. I’ve heard that we used 
> to need to use the “@synthesize” command to create internal members, then a 
> later version of the Objective-C compiler did it automatically. Now I have 
> the reverse problem; the first member gets a backing store automatically, but 
> I do NOT want that; I always want to use the custom array accessor methods to 
> simulate “myDatumList” with pieces of “myDataList.”
> 
> So, how can I turn off auto-@synthesize? Using “@dynamic” doesn’t work; it 
> causes a crash due to a missing [MyClass myDatumList] method.

@dynamic turns off automatic property synthesis. The compiler will not create 
ivar storage nor accessor methods for an @dynamic property. 

Did you implement the method -[MyClass myDatumList] ? If you don't synthesize 
the property then you need to write its accessor methods yourself.


> I also saw a random page saying that “self.myDatumList” causes this, but 
> “myDatumList” wouldn’t (like the “self.” gives the unwanted backing store 
> have priority over the array accessor methods). Is that accurate?

`self.property` reads or writes the property via its accessor methods. `ivar` 
or `self->ivar` accesses an instance variable's storage directly, bypassing any 
property accessor methods (synthesized or not) that use that storage. If you 
have a property and an ivar with the same name then `self.property` and 
`property` will both be legal and will behave differently. 


-- 
Greg Parker gpar...@apple.com Runtime Wrangler



___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Making an array property without a backing store. How?

2015-04-07 Thread Charles Srstka
On Apr 7, 2015, at 2:33 PM, Jens Alfke  wrote:
> 
> What Quincey said. I banged my head against this a lot back in 2005 or so and 
> gave up on this approach. It sounds lovely — I can expose this property as an 
> NSArray in my class’s public API even though it’s not really implemented as 
> an NSArray! — but it just doesn’t work, not unless you make all of your’ 
> class’s clients use
>   [myInstance valueForKey: @“myDatumList”]
> instead of
>   myInstance.myDatumList
> Yuck.
> 
> I first tried to work around this by wrapping that in a property:
>   - (NSArray*) myDatumList {
>   return [self valueForKey: @“myDatumList”];
>   }
> As you might guess, this causes an infinite regress and crashes. :(
> 
> Then I tried to get clever and give the public property a different name from 
> the one with the -countOf and …atIndex methods:
>   - (NSArray*) myPublicDatumList {
>   return [self valueForKey: @“myDatumList”];
>   }
> The problem with this is that the .myPublicDatumList property isn’t 
> KV-observable since the actual changes are happening to myDatumList instead. 
> But if you don’t need the property to be mutable, this might be good enough 
> for you…

Does this not work?

+ (NSSet *)keyPathsForValuesAffectingValueForMyPublicDatumList {
return [NSSet setWithObject:@“myDatumList”];
}

Charles

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Making an array property without a backing store. How?

2015-04-07 Thread Jens Alfke

> On Apr 7, 2015, at 11:55 AM, Quincey Morris 
>  wrote:
> 
> The problem is that this approach doesn’t actually work, not in this form. 
> There’s a little bit of Doing It Wrong™, but mostly this is pretty badly 
> broken in Cocoa.

What Quincey said. I banged my head against this a lot back in 2005 or so and 
gave up on this approach. It sounds lovely — I can expose this property as an 
NSArray in my class’s public API even though it’s not really implemented as an 
NSArray! — but it just doesn’t work, not unless you make all of your’ class’s 
clients use
[myInstance valueForKey: @“myDatumList”]
instead of
myInstance.myDatumList
Yuck.

I first tried to work around this by wrapping that in a property:
- (NSArray*) myDatumList {
return [self valueForKey: @“myDatumList”];
}
As you might guess, this causes an infinite regress and crashes. :(

Then I tried to get clever and give the public property a different name from 
the one with the -countOf and …atIndex methods:
- (NSArray*) myPublicDatumList {
return [self valueForKey: @“myDatumList”];
}
The problem with this is that the .myPublicDatumList property isn’t 
KV-observable since the actual changes are happening to myDatumList instead. 
But if you don’t need the property to be mutable, this might be good enough for 
you…

—Jens
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Making an array property without a backing store. How?

2015-04-07 Thread Quincey Morris
On Apr 7, 2015, at 10:58 , Daryle Walker  wrote:

> @interface MyClass : NSObject
> @property (readonly) NSArray *  myDatumList;
> @property NSArray *  myDataList;
> @end
> 
> @implementation MyClass
> 
> - (NSUInteger)countOfMyDatumList {
>return self.myDataList.count;
> }
> 
> - (id)objectInMyDatumListAtIndex:(NSUInteger)index {
>NSDictionary * const  dict = self.myDataList[index];
>return dict[@“datum”];
> }
> @end

The problem is that this approach doesn’t actually work, not in this form. 
There’s a little bit of Doing It Wrong™, but mostly this is pretty badly broken 
in Cocoa.

Your custom accessors are *never* used, except in two circumstances:

1. If you expose them publicly, and a client of MyClass invokes them directly. 
This is fine, except that clients don’t get access via all the other NSArray 
methods for free.

2. Clients access the “myDatumList” property via a *proxy* object which is 
obtained as follows:

[someMyClassInstance valueForKey: @“myDatumList”]

Using the returned proxy object as a NSArray, they can then use any of the 
standard NSArray methods (including ordinary ‘count’, ‘objectAtIndex:’ and all 
the others) on this proxy, and the calls will be translated into some 
combination of your custom accessors.

This is fine, too, except that you’ve lost the ability for clients of the class 
to write:

myClassInstance.myDatumList.count

etc. It would *seem* you could solve this by giving MyClass a simple getter:

- (NSArray*) myDatumList {
return [self valueForKey: @“myDatumList”];
}

but if you try that, you’ll find it causes an infinite loop. *By API contract* 
(look in NSKeyValueCoding.h for the gory details), under these circumstances 
‘valueForKey’ will call the ‘myDatumList’ method — hence the loop.

(In your case, without an implementation of the “myDatumList” getter, because 
you’ve declared a “myDatumList” property and declared it @dynamic, you’d just 
end up crashing, because ‘valueForKey’ would try to call a method that doesn’t 
exist. Or, if you let it be @synthesized, ‘valueForKey’ would call a method 
that always returns nil.)

There’s no direct solution to this. You either can’t use the custom NSArray 
accessors, or clients of the class have to remember to us the crummy 
‘valueForKey’ approach and you have to get rid of the “myDatumList” property. 
The indirect solution is to implement your own proxy object. It’s doable, and 
I’ve done it in the past, but it makes your head hurt.

My suggestion, therefore, is to take one of the following approaches:

— If clients of the class access “myDatumList” in limited ways, just expose the 
custom accessors directly, and don’t have the @property at all.

— Have the “myDataList” property be cleverer, and let it maintain a parallel 
array (_myDatumList). This is just an array, therefore, without custom 
accessors, so the “myDatumList” property will work fine.

Finally, if on top of all of this you want the “myDatumList” property to be KVO 
compliant — if you want to KVO observe it — there’s yet another level of 
complication because there’s a different proxy involved:

[ mutableArrayValueForKey: @“myDatumList”]

and/or you must provide KVO compliance manually when updating the backing store 
NSArrays. (And if you think this proxy avoids the looping problem of the 
non-mutable proxy — it doesn’t.)

Sorry to ruin your day. :)



___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Making an array property without a backing store. How?

2015-04-07 Thread Daryle Walker
> On Apr 6, 2015, at 10:18 PM, Graham Cox  wrote:
> 
>> 
>> On 7 Apr 2015, at 12:01 pm, Daryle Walker  wrote:
>> 
>> I have an object like:
>> 
>> @interface MyClass : NSObject
>> @property (readonly) NSArray *  myDatumList;
>> @property NSArray *  myDataList;
>> @end
>> 
>> The second member is meant to be an actual data member, an array of mutable 
>> dictionaries. The first member isn’t supposed to have a backing store; 
>> accessing a member of the first array references the corresponding member of 
>> the second, then access a dictionary attribute with a custom key.
>> 
>> I have a custom array count and array element read methods for the first 
>> member.
>> 
>> The program does nothing now. I checked with my limited debugging skills 
>> that the second member has one element, but the first member is looped 
>> though as if it has no elements. I see that BOTH “_myDatumList” and 
>> “_myDataList” internal members exist, with the former set as NIL. I’ve heard 
>> that we used to need to use the “@synthesize” command to create internal 
>> members, then a later version of the Objective-C compiler did it 
>> automatically. Now I have the reverse problem; the first member gets a 
>> backing store automatically, but I do NOT want that; I always want to use 
>> the custom array accessor methods to simulate “myDatumList” with pieces of 
>> “myDataList.”
>> 
>> So, how can I turn off auto-@synthesize? Using “@dynamic” doesn’t work; it 
>> causes a crash due to a missing [MyClass myDatumList] method. I also saw a 
>> random page saying that “self.myDatumList” causes this, but “myDatumList” 
>> wouldn’t (like the “self.” gives the unwanted backing store have priority 
>> over the array accessor methods). Is that accurate?
> 
> 
> 
> You need to write your own getter method for myDatumList, and write the code 
> in there to do what you want it to do. Since it's a readonly property, the 
> compiler will be satisfied that you've done everything necessary to implement 
> the property and won't give it any backing store at all.
> 
> As a general comment, I personally find the automatic creation of backing 
> store by default leads to bugs and confusion - how it was originally with the 
> requirement to either use @synthesise and/or write your own methods was 
> cleaner and less bug-prone, even if it did add a tiny bit of extra work. 
> C'est la vie…

I already have:

@implementation MyClass
// @synthesize myDataList = _myDataList;  // Not really needed due to 
auto-@synthesize

- (NSUInteger)countOfMyDatumList {
return self.myDataList.count;
}

- (id)objectInMyDatumListAtIndex:(NSUInteger)index {
NSDictionary * const  dict = self.myDataList[index];
return dict[@“datum”];
}
@end

Do I have to switch to a “- (NSArray *)myDatumList” method? If so, do I also 
use “@dynamic myDatumList”?

Looking at the debugger again, I noticed that my BOOL property with a custom 
getter (which simulates its result based on other member data) also has a 
“_myFlag” backing store. Is there some setting we’re missing, or do I have to 
file a bug to add no-auto-@synthesize?

…

OK, using @dynamic suppresses the creation of backing ivars, for both the BOOL 
and the NSArray. I got around the crash for the simulated array property by 
using a direct getter. So the bug is that @dynamic always looks for a direct 
getter, even for collection-based properties. (It never considers a countOf… / 
objectIn…AtIndex pair.)

Radar #20451913

— 
Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com 

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Making an array property without a backing store. How?

2015-04-06 Thread Graham Cox

> On 7 Apr 2015, at 12:01 pm, Daryle Walker  wrote:
> 
> I have an object like:
> 
> @interface MyClass : NSObject
> @property (readonly) NSArray *  myDatumList;
> @property NSArray *  myDataList;
> @end
> 
> The second member is meant to be an actual data member, an array of mutable 
> dictionaries. The first member isn’t supposed to have a backing store; 
> accessing a member of the first array references the corresponding member of 
> the second, then access a dictionary attribute with a custom key.
> 
> I have a custom array count and array element read methods for the first 
> member.
> 
> The program does nothing now. I checked with my limited debugging skills that 
> the second member has one element, but the first member is looped though as 
> if it has no elements. I see that BOTH “_myDatumList” and “_myDataList” 
> internal members exist, with the former set as NIL. I’ve heard that we used 
> to need to use the “@synthesize” command to create internal members, then a 
> later version of the Objective-C compiler did it automatically. Now I have 
> the reverse problem; the first member gets a backing store automatically, but 
> I do NOT want that; I always want to use the custom array accessor methods to 
> simulate “myDatumList” with pieces of “myDataList.”
> 
> So, how can I turn off auto-@synthesize? Using “@dynamic” doesn’t work; it 
> causes a crash due to a missing [MyClass myDatumList] method. I also saw a 
> random page saying that “self.myDatumList” causes this, but “myDatumList” 
> wouldn’t (like the “self.” gives the unwanted backing store have priority 
> over the array accessor methods). Is that accurate?



You need to write your own getter method for myDatumList, and write the code in 
there to do what you want it to do. Since it's a readonly property, the 
compiler will be satisfied that you've done everything necessary to implement 
the property and won't give it any backing store at all.

As a general comment, I personally find the automatic creation of backing store 
by default leads to bugs and confusion - how it was originally with the 
requirement to either use @synthesise and/or write your own methods was cleaner 
and less bug-prone, even if it did add a tiny bit of extra work. C'est la vie...

--Graham



___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Making an array property without a backing store. How?

2015-04-06 Thread Daryle Walker
I have an object like:

@interface MyClass : NSObject
@property (readonly) NSArray *  myDatumList;
@property NSArray *  myDataList;
@end

The second member is meant to be an actual data member, an array of mutable 
dictionaries. The first member isn’t supposed to have a backing store; 
accessing a member of the first array references the corresponding member of 
the second, then access a dictionary attribute with a custom key.

I have a custom array count and array element read methods for the first member.

The program does nothing now. I checked with my limited debugging skills that 
the second member has one element, but the first member is looped though as if 
it has no elements. I see that BOTH “_myDatumList” and “_myDataList” internal 
members exist, with the former set as NIL. I’ve heard that we used to need to 
use the “@synthesize” command to create internal members, then a later version 
of the Objective-C compiler did it automatically. Now I have the reverse 
problem; the first member gets a backing store automatically, but I do NOT want 
that; I always want to use the custom array accessor methods to simulate 
“myDatumList” with pieces of “myDataList.”

So, how can I turn off auto-@synthesize? Using “@dynamic” doesn’t work; it 
causes a crash due to a missing [MyClass myDatumList] method. I also saw a 
random page saying that “self.myDatumList” causes this, but “myDatumList” 
wouldn’t (like the “self.” gives the unwanted backing store have priority over 
the array accessor methods). Is that accurate?

— 
Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com 

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com