Why does a readonly property still allow writing with KVC - objective-c

I'm working through the "Key Value Coding" chapter in "Programming for Mac OS X". I've built an interface with a slider and a label, both bound to fido, an int. If I set the property for fido to readonly, moving the slider still causes the label to change it's value. I had assumed that I'd get some sort of error for this. If the property is readonly, how come the slider can still write to the property? I thought that it would have no setters created, and KVC wouldn't work. Thanks.
Here's the code I'm using:
#import <Cocoa/Cocoa.h>
#interface AppController : NSObject
{
int fido;
}
#property (readonly, assign) int fido;
#end
#import "AppController.h"
#implementation AppController
#synthesize fido;
- (id)init
{
[super init];
[self setValue:[NSNumber numberWithInt:5] forKey:#"fido"];
NSNumber *n = [self valueForKey:#"fido"];
NSLog(#"fido = %#", n);
return self;
}
#end
alt text http://idisk.me.com/nevan/Public/Pictures/Skitch/Window-20091001-174352.png

AppController.h:
#interface AppController : NSObject
{
int fido;
}
#property (readonly, assign) int fido;
#end
import "AppController.h"
#implementation AppController
#synthesize fido;
...
#end
At this point, you have declared that AppController has a -fido method and you have synthesized that method. There is no -setFido: method. So, why does the following "work"?
- (id)init
{
if (self=[super init]) {
[self setValue:[NSNumber numberWithInt:5] forKey:#"fido"];
NSNumber *n = [self valueForKey:#"fido"];
NSLog(#"fido = %#", n);
}
return self;
}
(BTW: I fixed your -init to implement the correct pattern)
This works because KVC follows a heuristic to set or get the value. The call to -setValue:forKey: first looks for -setFoo:. If not found, it then looks for the instance variable foo and sets it directly.
Note that if you change the instance variable fido to _fido, the set will work, but the valueForKey will return 0 as it calls the synthesized method (since I'm on 64 bit, the #synthesize synthesizes a fido instance variable. Confusing, I know.).
If you were to change the name of your ivar to bar and then use #synthesize foo=bar;, the code would fail at runtime.
You'll see:
2009-10-01 08:59:58.081 dfkjdfkjfjkfd[24099:903] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppController 0x20000e700> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key fido.'
*** Call stack at first throw:
(
0 CoreFoundation 0x00007fff85b055a4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff85c5a0f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff85b5caf9 -[NSException raise] + 9
3 Foundation 0x00007fff814e14f5 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 434
(
0 CoreFoundation 0x00007fff85b055a4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff85c5a0f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff85b5caf9 -[NSException raise] + 9
3 Foundation 0x00007fff814e14f5 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 434
4 dfkjdfkjfjkfd 0x0000000100000d96 -[AppController init] + 130

Having readonly property means that compiler won't generate you setter for that property. It's still legal to write to it via KVO/KVC.

The compiler directives #property and #synthesize are just shorthand ways to create the methods to get and set the variable in question.
The setter method created is named setFido:, and the getter method is just named fido.
When you specify readonly, I believe that simply tells the compiler not to create the setter method, but only the getter. It doesn't put any sort of barrier in the way of setting the variable by other means.
(Hope I've got all that right. Good luck!)

Related

Does "#synthesize" every "#property" necessary?

I'm following one of the iOS tutorials from Ray Wenderlich (Scarybugs part 1). But I notice for each property in the model, he always "#synthesize" it in the implementation.
Here is the example of the models:
#import <Foundation/Foundation.h>
#interface RWTScaryBugData : NSObject
#property (strong) NSString *title;
#property (assign) float rating;
- (id)initWithTitle:(NSString*)title rating:(float)rating;
#end
--
#import "RWTScaryBugData.h"
#implementation RWTScaryBugData
#synthesize title = _title;
#synthesize rating = _rating;
- (id)initWithTitle:(NSString*)title rating:(float)rating {
if ((self = [super init])) {
self.title = title;
self.rating = rating;
}
return self;
}
#end
--
#import <Foundation/Foundation.h>
#class RWTScaryBugData;
#interface RWTScaryBugDoc : NSObject
#property (strong) RWTScaryBugData *data;
#property (strong) UIImage *thumbImage;
#property (strong) UIImage *fullImage;
- (id)initWithTitle:(NSString*)title rating:(float)rating thumbImage:(UIImage *)thumbImage fullImage:(UIImage *)fullImage;
#end
--
#import "RWTScaryBugDoc.h"
#import "RWTScaryBugData.h"
#implementation RWTScaryBugDoc
#synthesize data = _data;
#synthesize thumbImage = _thumbImage;
#synthesize fullImage = _fullImage;
- (id)initWithTitle:(NSString*)title rating:(float)rating thumbImage:(UIImage *)thumbImage fullImage:(UIImage *)fullImage {
if ((self = [super init])) {
self.data = [[RWTScaryBugData alloc] initWithTitle:title rating:rating];
self.thumbImage = thumbImage;
self.fullImage = fullImage;
}
return self;
}
#end
I know "#synthesize" is basically to allocate an instance variable for a property, but it has been taken care of by default for every "#property" in ".h file" (although not visible).
My questions is: is it necessary to "#synthesize" every "#property" we have in our public API? (I tried deleting all the "#synthesize" in the implementation, and it still worked)
#synthesize is no longer needed. The compiler will synthesize the getter and setter as required with an instance variable named as _<propertyName> automatically. It creates the instance variable but more importantly it creates the getter and setter methods (for readwrite properties).
If you've manually provided the getter/setter for a property, then an instance variable won't be automatically synthesized, and you'll need to add the #synthesize statement. From the docs:
Note: The compiler will automatically synthesize an instance variable in all situations where it’s also synthesizing at least one accessor method. If you implement both a getter and a setter for a readwrite property, or a getter for a readonly property, the compiler will assume that you are taking control over the property implementation and won’t synthesize an instance variable automatically.
If you still need an instance variable, you’ll need to request that one be synthesized:
#synthesize property = _property;
As noted in the Objective-C Feature Availability Index, automatic synthesis of property instance variables was introduced with Xcode 4.4 (LLVM Compiler 4.0) and requires the modern runtime (all code on iOS, 64-bit code on OS X).
So, the tutorial is a bit dated, that's all.
hope this will help little more.
#property(nonatomic) NSString *name;
the #property is an Objective-C directive which declares the property
-> The "`nonatomic`" in the parenthesis specifies that the property is non-atomic in nature.
-> and then we define the type and name of our property.
-> prototyping of getter and setter method
now go to .m file
previously we have synthesis this property by using #synthesis , now it also NOT required , it automatically done by IDE.
-> this #synthesis now generate the getter and setter(if not readonly) methods.
and Then why we even write #synthesis in our code if it always done by IDE .
one of the basic use is :-
what our IDE do internally
#synthesis name=_name;
we use _name to access particular property but now you want synthesis by some other way like
firstname you can do it like
#synthesis name= firstname
or just by name
#synthesis name=name
So from it you can access this property as you want.

Unrecognized selector sent to instance (multiple view controllers)

There are two view controllers that I access using an NSTabView: ViewController1 and ViewController2. In my AppDelegate, I have a variable that I wish to share between the two:
AppDelegate.h:
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
NSMutableString *myString;
}
#property (assign) NSMutableString *myString;
ViewController.h:
-(void)doStuff
{
AppDelegate *del = (AppDelegate *)[[NSApplication sharedApplication] delegate];
[del.myString setString:#"This is a test"];
}
This is the error I get when doStuff() is called:
2014-06-10 16:29:09.240 MyApp[32297:303] -[ViewController2 myString]:
unrecognized selector sent to instance 0x6100001a7700
2014-06-10 16:29:09.240 MyApp[32297:303] An uncaught exception was raised
2014-06-10 16:29:09.240 MyApp[32297:303] -[ViewController2 myString]:
unrecognized selector sent to instance 0x6100001a7700
2014-06-10 16:29:09.241 MyApp[32297:303] (
0 CoreFoundation 0x00007fff8d52d25c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff93f16e75 objc_exception_throw + 43
2 CoreFoundation 0x00007fff8d53012d -[NSObject(NSObject)
doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00007fff8d48b322 ___forwarding___ + 1010
4 CoreFoundation 0x00007fff8d48aea8 _CF_forwarding_prep_0 + 120
5 Recorder 0x0000000100006b55 -[ViewController1 doStuff:] + 549
Why is there an error being thrown in the other view controller?
Because at some point you have reset the app delegate so that it is a ViewController2. That happened in code you have not shown, so I don't know how and when you did that. But the point is, your message myString is never arriving at the AppDelegate. It is arriving at the ViewController2.
In other words, you are saying
(AppDelegate *)[[NSApplication sharedApplication] delegate]
but in fact that object has somehow been repointed at the ViewController2. Thus, even though you are casting to an AppDelegate, it isn't an AppDelegate at all. The compiler allows you to say myString to this object because you said (falsely) that it is an AppDelegate, but the reality causes the crash at runtime when the myString message arrives at the ViewController2 object.
Look for code where you say setDelegate: or .delegate = ... to work out when you repointed the app delegate.
A few things:
1) The error "unrecognized selector" means you're trying to call some function ("doStuff" or "setMyString") on an object that doesn't contain a public method with that name. It crashes because it doesn't know how to respond. Most often you'll see this when you forget to hook up a storyboard properly, have a typo in a method call, or if you send a message to the wrong object.
2) You're trying to access myString on AppDelegate but it's actually being called on ViewController2. The error is because ViewController2 doesn't have a #property called myString. As the response above stated, doublecheck how you're actually assigning the appDelegate.
3) You're declaring both an instance variable myString AND the #property myString. This is redundant and really outdated (< iOS4) practice. Instead, remove the {NSMutableString *myString} portion in curly braces & only use #property (nonatomic, strong) NSMutableString *myString;
4) Unless you specifically created a setString method in your AppDelegate.m, the default way to access an #property named "myString" would probably be [del setMyString] or del.myString = ....
Calling [del.myString setString] is also a redundant mixture of dot syntax & brackets. It may throw a separate error after you fix the first one. Let the #property handle creating getter/setter methods.
Call [del setMyString:#"whatever value"]; or alternatively del.myString = #"whatever value";
5) In Obj-C this sort of crash is often avoided by first asking whether or not the object will respond:
if ([someObject respondsToSelector:#selector(doStuff:)] {//then do something
}
6) This sort of error is one good reason why Apple's new language Swift has moved away from the pattern above. Swift discourages asking about selectors & instead requires more specific/optional data types so you're less likely to run into this problem.

Overriding a readonly property in subclass

There is a class that looks like this (I'm omitting the imports for brevity):
Base.h:
#interface Base : NSObject
#property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
#end
Base.m:
#implementation Base
- (id)initWithSomething:(NSString *)something {
self = [super init];
if (self) _something = something;
return self;
}
#end
As you see, the 'something' property is readonly. Now I want to create a subclass that overrides that property to be writable as well:
Sub.h:
#interface Sub : Base
#property (strong) NSString *something;
#end
Sub.m:
#implementation Sub
#end
And the code:
main.c:
int main(int argc, const char * argv[]) {
#autoreleasepool {
Sub *o = [Sub new];
o.something = #"foo";
NSLog(#"%#", o.something);
}
return 0;
}
This code results in:
2013-09-07 13:58:36.970 ClilTest[3094:303] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: '-[Sub setSomething:]: unrecognized
selector sent to instance 0x100109ff0'
Why is that? Why doesn't it find the setSelector?
When I do this in the subclass instead:
Sub.m:
#implementation Sub
#synthesize something = _something;
#end
it all works. Does this mean the subclass' property is not synthesized by default even though it is defined as #property in the #interface? Does the compile somehow 'see' the automatically generated getter from Base and doesn't generate the setter? And why, I think the setter should be generated as it doesn't exist yet. I'm using Xcode 4.6.2 and the project is a Cli Tool (type Foundation), but the same happens in my actual project which is an iPhone app.
Background: I have a heavy object (instance of Base) that requires a Bluetooth connection to some equipment and I am supposed to create a view controller for some functionality. For easy testing I don't want to be connected to BT (actually, I would need a physical device and test the code on it), I would like to be able to test it in the simulator.
What I came up with is that I simply create a subclass (Sub) that stubs a few methods / properties and use it instead, and when the code is ready I just remove the code for the subclass, replace its instance with the correct one, test in with a device, commit and push. It actually works fine, except for the weird thing with #property above.
Could somebody tell me what is going on with property overriding?
For a readonly property, only a getter method is synthesized, but no setter method.
And when compiling the subclass, the compiler does not know how the property is realized
in the base class (it could be a custom getter instead of a backing instance variable).
So it cannot just create a setter method in the subclass.
If you want to have write access to the same instance variable from the subclass,
you have to declare it as #protected in the base class
(so that it is accessible in the subclass), re-declare the property
as read-write in the subclass, and provide a setter method:
Base.h:
#interface Base : NSObject {
#protected
NSString *_something;
}
#property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
#end
Sub.h:
#interface Sub : Base
#property (strong, readwrite) NSString *something;
#end
Sub.m:
#implementation Sub
-(void)setSomething:(NSString *)something
{
_something = something;
}
#end
Your solution
#synthesize something = _something;
generates getter and setter method in the subclass, using a separate instance
variable _something in the subclass (which is different
from _something in the base class).
This works as well, you just should be aware that self.something refers to
different instance variables in the base class and in the subclass. To make that
more obvious, you could use a different instance variable in the subclass:
#synthesize something = _somethingElse;
The given answer works perfectly fine. This is an alternative answer, that apparently Apple likes a bit more.
You can define a private extension of your class, a Base+Protected.h file, which needs to be included in Base.m and Sub.m.
Then, in this new file, you redefine the property as readwrite.
#interface Base ()
#property (strong, readwrite) NSString *something;
#end
This alternative allows you to use the accessor self.something rathern than the ivar _something.
Note: you still need to keep the definition of something in your Base.h as is.
I guess that the backing variables are the same when the property is not synthesized in the subclass. So at runtime the programm tries to call the setSomething in the superclass. But since it doesnt exist there an Exception is thrown.

Weak property is set to nil in dealloc but property's ivar is not nil

I noticed the following in Objective-C with ARC enabled:
Let's have simple class A and autosynthesized weak property
#interface A
#property (nonatomic, weak) id refObject;
#end
#implementation A
#end
And second class B with dealloc implemented
#interface B
#end
#implementation B
-(void) dealloc
{
NSLog(#"In dealloc");
}
#end
And finally somewhere in class A have the following:
#implementation A
...
-(void) foo
{
B* b = [B new];
self.refObject = b;
// Just use b after the weak assignment
// in order to not dealloc 'b' before assignement
NSLog(#"%#", b);
}
...
#end
If I set a breakpoint in [B dealloc] and inspect [A refObject] property I can see that a.refObject is nil but a->_refObject is not nil and points to 'b'
Any ideas why that happens?
Short answer: The instance variable a->_refObject is not (yet) nil in -[B dealloc],
but each access to that weak pointer is done through a ARC runtime function
that returns nil if the deallocation has already begun.
Long answer: By setting a watchpoint you can see that a->_refObject is set to nil at the end of the
deallocation process. The stack backtrace (when the watchpoint is hit) looks like this:
frame #0: 0x00007fff8ab9f0f8 libobjc.A.dylib`arr_clear_deallocating + 83
frame #1: 0x00007fff8ab889ee libobjc.A.dylib`objc_clear_deallocating + 151
frame #2: 0x00007fff8ab88940 libobjc.A.dylib`objc_destructInstance + 121
frame #3: 0x00007fff8ab88fa0 libobjc.A.dylib`object_dispose + 22
frame #4: 0x0000000100000b27 weakdealloc`-[B dealloc](self=0x000000010010a640, _cmd=0x00007fff887f807b) + 151 at main.m:28
frame #5: 0x0000000100000bbc weakdealloc`-[A foo](self=0x0000000100108290, _cmd=0x0000000100000e6f) + 140 at main.m:41
frame #6: 0x0000000100000cf5 weakdealloc`main(argc=1, argv=0x00007fff5fbff968) + 117 at main.m:52
frame #7: 0x00007fff8c0987e1 libdyld.dylib`start + 1
and object_dispose() is called from -[NSObject dealloc] (as can be seen in
http://www.opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm).
Therefore in -[B dealloc], a->_refObject is not nil before the (compiler generated) [super dealloc] is called.
So the question remains: Why does a.refObject return nil at that point?
The reason is that for each access to a weak pointer the ARC compiler generates
a call to objc_loadWeak() or objc_loadWeakRetained(). From the documentation:
id objc_loadWeakRetained(id *object)
If object is registered as a __weak object, and the last value stored into object has not > yet been deallocated or begun deallocation, retains that value and returns it. Otherwise > returns null.
So even if a->refObject is not nil at that point, accessing the weak pointer
via objc_loadWeakRetained() (as done by the property accessor method) returns nil,
because the deallocation of the B object has already begun.
The debugger accesses a->refObject directly and does not call objc_loadWeak().

When using Objection, implementation of custom protocol crashes with unrecognized selector

I am defining a custom protocol:
#protocol NGSAuthProvider <NSObject>
- (BOOL)isReady;
- (BOOL)isSessionValid;
- (void)login;
- (void)logout;
- (NSString *)accessToken;
- (BOOL)handleOpenURL:(NSURL *)url;
#end
I want to have different providers. So one is a Facebook provider:
#interface NGSFacebookAuthProvider : NSObject <NGSAuthProvider>
#end
#interface NGSFacebookAuthProvider () <FBSessionDelegate>
#property BOOL ready;
#property(nonatomic, retain) Facebook *facebook;
#property(nonatomic, retain) NSArray *permissions;
#end
#implementation NGSFacebookAuthProvider
//Implementation of fbLogin, fbLogout and the methods in NGSAuthProvider that forward calls to self.facebook
- (NSString *)accessToken
{
return [self.facebook accessToken];
}
#end
I setup Objection to bind from my class to the protocol.
#interface NGSObjectionModule : ObjectionModule
#end
#implementation NGSObjectionModule
- (void)configure
{
self bind:[NGSFacebookAuthProvider class] toProtocol:#protocol(NGSAuthProvider)];
}
#end
I setup the Global Injector:
#implementation NGSAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
ObjectionModule *module = [[NGSObjectionModule alloc] init];
ObjectionInjector *injector = [Objection createInjector:module];
[module release];
[Objection setGlobalInjector:injector];
}
I am using this in my RootViewController like this:
#interface RootViewController : UITableViewController
#end
#interface RootViewController ()
#property(nonatomic, retain) id<NGSAuthProvider> authProvider;
#end
#implementation RootViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.authProvider = [[Objection globalInjector] getObject:#protocol(NGSAuthProvider)];
}
- (void)processConfig {
NSString *token = [self.authProvider accessToken];
// use the access token
}
#end
When I run this, I get the following error:
2011-07-26 21:46:10.544 ngs[6133:b603] +[NGSFacebookAuthProvider accessToken]: unrecognized selector sent to class 0x30c7c
2011-07-26 21:46:10.546 ngs[6133:b603] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NGSFacebookAuthProvider accessToken]: unrecognized selector sent to class 0x30c7c'
*** Call stack at first throw:
(
0 CoreFoundation 0x00e825a9 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x00fd6313 objc_exception_throw + 44
2 CoreFoundation 0x00e8417b +[NSObject(NSObject) doesNotRecognizeSelector:] + 187
3 CoreFoundation 0x00df3966 ___forwarding___ + 966
4 CoreFoundation 0x00df3522 _CF_forwarding_prep_0 + 50
5 ngs 0x0000324b -[RootViewController processConfig] + 731
6 ngs 0x000041a2 __33-[RootViewController viewDidLoad]_block_invoke_0 + 50
So my class implements the protocol. It successfully is assigned to id<NGSAuthProvider>. I tried contructing [[NGSFacebookAuthProvider alloc] init] explicitly instead of using Objection and it still crashed.
I tried looping through the selectors using objc/runtime.h methods to see which selectors are there but the only thing it finds is initialize:
- (void)logSelectors:(id)obj
{
int i=0;
unsigned int mc = 0;
Method * mlist = class_copyMethodList(object_getClass([obj class]), &mc);
NSLog(#"%d methods", mc);
for(i=0;i<mc;i++)
NSLog(#"Method no #%d: %s", i, sel_getName(method_getName(mlist[i])));
free(mlist);
}
This has to be something simple that I am missing. I use protocols defined by Cocoa and don't have this issue. I have defined custom protocols for UIViewController-based delegates without issue.
I am stumped as to why Obj-C runtime can't find my methods! If I change id<NGSAuthProvider> to NGSFacebookAuthProvider and construct it explicitly then it all works.
SOLUTION:
The problem was I misunderstood how to bind to a protocol. One way that works is:
#implementation NGSObjectionModule
- (void)configure
{
[self bind:[[[NGSFacebookAuthProvider alloc] init] autorelease] toProtocol:#protocol(NGSAuthProvider)];
}
#end
What I would like to do is bind a class to a protocol, but Objection probably wouldn't know the initializer to call?
The issue is you're trying to use the static class method (denoted because you've got a +) instead of a method run on an instance of your object (which is what you've written it as, with a -)
Chris,
You could use Objection's meta class bindings that allows you to bind a meta class to a protocol and invoke class methods against the meta class instance.
For example,
[self bindMetaClass:[NGSFacebookAuthProvider class] toProtocol:#protocol(NGSAuthProvider)];
But only if you want to use class methods. Otherwise you can use the protocol bindings against a share instance.
I also faced the same issue as I was using
[self bind:[MyClass class] toProtocol:#protocol(MyProtocol)];
The right way for it to work is
[self bindClass:[MyClass class] toProtocol:#protocol(MyProtocol)];