Objective-c readonly copy properties and ivars - objective-c

I'm try to grok properties declared as both copy and readonly in objective-c, and specifically, whether I have to do the copy myself. In my init methods. Evidence suggests I do:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData *test;
- (instancetype)initWithData:(NSData *)data;
#end
#implementation A
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
_test = data;
}
return self;
}
#end
int main (void) {
NSData *d1 = [NSMutableData dataWithBytes:"1234" length:5];
A *a = [[A alloc] initWithData:d1];
NSLog(#"%lx", (unsigned long)d1);
NSLog(#"%lx", (unsigned long)a.test);
return 0;
}
I had thought I could do self.test = data in my init method, but that is not permitted because it's readonly (not unexpectedly). Of course, self.test = [data copy] ensures two different objects.
So: Is there a way to create a readonly property in objective-c that copies the incoming value, or is it sufficiently an edge case that the combination is pointless and I have to do any copying myself manually anyway?

A #property declaration is merely shorthand for some accessor/mutator method declarations, and (in some cases) synthesized implementations for said accessor/mutator methods.
In your case, the #property(nonatomic, copy, readonly) NSData *test declaration expands to this equivalent code:
#interface A : NSObject
{
NSData* _test;
}
- (NSData*)test;
#end
#implementation A
- (NSData*)test
{
return _test;
}
#end
There is no setTest: mutator method because the property is declared as readonly, so the copy attribute has no effect.
You can implement your own mutator method:
- (void)setTest:(NSData*)newValue
{
_test = [newValue copy];
}
Or, you can have the compiler synthesize a mutator method for you by declaring a read/write property in a private class extension in your implementation file:
// A.m:
#interface A()
#property (nonatomic, copy) NSData* test;
#end
Both cases would allow you to use the test mutator method to copy a value to the _test instance variable:
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
The end result is:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData* test;
- (instancetype)initWithData:(NSData*)data;
#end
#interface A()
#property (nonatomic, copy) NSData* test;
#end
#implementation A
- (instancetype)initWithData:(NSData*)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
#end

In addition to what Darren said, the copy attribute describes what semantics the properties setter has. In your initializer, you're not using the setter, you're directly assigning to the instance variable.
It's maybe a bit hard to grok, but the instance variable is not the same thing as the property. It is used to implement the property in this case. But, assigning to the instance variable is not the same as setting the property.
If you want your initializer to also have the semantics that it copies the passed-in data, that's a separate design decision (although a good idea to go with the property's semantics). You could implement that by using a private setter as Darren suggests, but you could also just do:
_test = [data copy];
in the initializer.

Related

Obj-C: Clone object with a mutated property

I would like to a create convenience initiator by cloning an existing object with a mutation, while keeping the original object intact.
For example:
Given a Person object person1 with a name (Tom) and age (10)
I would like to clone the person1 object, but with 0 age.
I have following code in Obj-C, but not sure if there's a better way to do it:
Person.h
#interface Person : NSObject
#property (nonatomic, readonly) NSString *name;
#property (nonatomic, readonly) NSUInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
- (instancetype)cloneWithZeroAge;
#end
Person.m
#implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age
{
if (self = [super init]) {
_name = name;
_age = age;
}
return self;
}
- (instancetype)cloneWithZeroAge
{
if (self) {
// mutate age to 0
return [self initWithName:_name age:0];
}
return self;
}
#end
Let's start with the constructor. For you specific scenario it doesn't make much difference, but in order to be functionally independent, you better ensure that the name gets a copy of the data passed. You also want to specify this part as memory storage modifier of the property, so the contract is apparent to the client code:
#interface Person : NSObject
#property (copy, nonatomic, readonly) NSString *name;
...
#end
#implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age
{
if (self = [super init]) {
_name = [name copy];
_age = age;
}
return self;
}
Now for the actual "copy" method. In order to be more consistent with existing NSCopying protocol and Objective-C naming convention, and, which is more important, the memory management ownership convention, you should start the method name with the word "copy", so the calling side knows it's responsible for releasing the object. The most important part now, is that if you want to keep the original object untouched, you have to allocate and create a new object. In your implementation, however, you just change the self into the new object entirely. Here is how I would implement such a method:
- (instancetype)copyWithZeroAge {
Person *copy = [[Person alloc] initWithName:_name age:0];
return copy;
}
If you prefer to keep the name property memory modifier strong instead of copy, don't forget to copy the instance:
- (instancetype)copyWithZeroAge {
Person *copy = [[Person alloc] initWithName:[_name copy]
age:0];
return copy;
}

What's the best way to unit test a method with a parameter that's immutable and does not have an initializer?

Question
I'm trying to unit test my implementation of a protocol's method that takes an immutable object that's not in my project as a parameter. This object doesn't have any initializers exposed that set its properties. What's the best way to unit test in this scenario?
What I tried
I tried subclassing and casting the properties to readwrite, but you only have access to a super-class' ivars if they're explicitly marked as #protected. Since the class isn't within my project, I can't re-assign a new scope to the ivar.
I also can't mock a setter that doesn't exist, and ivars are not directly writable through mock objects.
Eventually, I decided to add an additional method that the method in question would call, passing only objects whose properties I can set, and I covered that additional method in unit tests.
Is there a better way to unit test in this scenario?
Specifics of my scenario
I'm trying to unit test my implementation of userContentController:didReceiveScriptMessage: from WebKit's WKScriptMessageHandler protocol and can't find a way to pass an instance of WKScriptMessage or of WKUserContentController with any properties set.
To unit test this, I created a new method that takes a parameter of type 'id' and called it from userContentController:didReceiveScriptMessage:, passing WKScriptMessage's body property.
Does this help you?
TestMessage.h
#import <WebKit/WebKit.h>
#interface TestMessage : WKScriptMessage
- (id) initWithBody:(id)body
frameInfo:(WKFrameInfo*)frameInfo
name:(NSString*)name
webView:(WKWebView*)webView;
#end
TestMessage.m
#import "TestMessage.h"
#interface TestMessage()
#property (nonatomic, strong) id messageBody;
#property (nonatomic, strong) WKFrameInfo* messageFrameInfo;
#property (nonatomic, strong) NSString* messageName;
#property (nonatomic, strong) WKWebView* messageWebView;
#end
#implementation TestMessage
- (id) initWithBody:(id)body
frameInfo:(WKFrameInfo*)frameInfo
name:(NSString*)name
webView:(WKWebView*)webView
{
self = [super init];
if (self)
{
_messageBody = body;
_messageFrameInfo = frameInfo;
_messageName = name;
_messageWebView = webView;
}
return self;
}
- (id) body
{
return self.messageBody;
}
- (WKFrameInfo *)frameInfo
{
return self.frameInfo;
}
- (NSString *)name
{
return self.messageName;
}
- (WKWebView *)webView
{
return self.messageWebView;
}
#end
So you're basically creating a subclass with matching properties and then overriding the getters for the superclass properties to return values for the ones you have full access to.
This worked for me with the simple test (I'm sure your scenario is more involved):
- (void) testMessageHandler
{
WKWebView* webView = [[WKWebView alloc] init];
WKUserContentController* controller = [[WKUserContentController alloc] init];
WKFrameInfo* info = [[WKFrameInfo alloc] init];
NSDictionary* testBody = #{#"testKey" : #"testVal" };
TestMessage* testMessage = [[TestMessage alloc] initWithBody:testBody
frameInfo:info
name:#"Message Name"
webView:webView];
// some object conforming to the WKScriptMessageHandler protocol
[self userContentController:controller
didReceiveScriptMessage:testMessage];
}
- (void)userContentController:(nonnull WKUserContentController *)userContentController
didReceiveScriptMessage:(nonnull WKScriptMessage *)message
{
NSLog(#"Received Script Message... (%#)", message.body);
}

Objective-C Where to define initial Class Properties

just wanted to ask where I define initial class properties?
From other languages I am used to define some standard properties in the head before the content of the class starts.
For example paths to files. Settings and so on.
Where I fill these initial properties with values in Objective-C?
Thanks
Generally it's something like:
MyClass.h:
extern NSString * const staticValue1;
extern NSString * const staticValue2;
#interface MyClass : NSObject
{
NSString *_strval;
int _intval;
float _fltval;
}
#property (retain, nonatomic, readwrite) NSString *strval;
#property (assign, nonatomic, readwrite) int intval;
#property (assign, nonatomic, readwrite) float fltval;
#end
MyClass.m:
NSString * const staticValue1 = #"Something";
NSString * const staticValue2 = #"Something else";
#interface MyClass
#synthesize strval = _strval;
#synthesize intval = _intval;
#synthesize fltval = _fltval;
- (id)init
{
self = [super init];
if (self != nil)
{
[self setStrval:[NSString stringWithFormat:#"This is a %#", #"string"]];
[self setIntval:10];
[self setFltval:123.45f];
}
return self;
}
- (void)dealloc
{
[self setStrval:nil];
[super dealloc];
}
#end
This demonstrates the use of synthesized properties which are being used here to manage the memory of the instance variable _strval, which requires retaining/releasing to avoid memory leaks. Note that [self setStrval] is initialised with an autoreleased object (from [NSString stringWithFormat) and will be retained by the setter method. Alternatively these methods can be called using the following syntax, if you prefer:
self.strval = [NSString stringWithFormat:#"This is a %#", #"string"];
self.intval = 10;
self.fltval = 123.45f;
Maybe some of what you are after can be implemented with class methods.
Class methods are coded with a + (instead of the instance methods' -), and can't refer to instance variables, as they are not associated with any specific instance of the class.
This is a class method to return a default string:
+ (NSString *)myDefaultString
{
return #"Some default value";
}
You call it by simply calling it with the class name at the receiver's place. Imagine you have defined the method in a class called MyClass, the you call it like this:
NSString *str = [MyClass myDefaultString];
You'll notice that there is no alloc/init calls in this.
Public property needs to be define in .h file.
#interface MyClass {
}
#property(nonatomic, reatin) NSString *a;//Define as per needs, then synthesise in .m file
#end
For private property you need define inline category in .m file-
#interface MyClass ()
#property(nonatomic, reatin) NSString *b;//Define as per needs, then synthesise in .m file
#end
#implementation MyClass
#synthesize a = _a;
#synthesize b = _b;
- (void)viewDidLoad {
//You can initialise property here or in init method
self.a = #"Demo1";
self.b = #"Demo2";
}
//Now you can have other code for this class.
#end

Help with a method that returns a value by running another object's method

I have a Class that runs the following method (a getter):
// the interface
#interface MyClass : NSObject{
NSNumber *myFloatValue;
}
- (double)myFloatValue;
- (void)setMyFloatValue:(float)floatInput;
#end
// the implementation
#implementation
- (MyClass *)init{
if (self = [super init]){
myFloatValue = [[NSNumber alloc] initWithFloat:3.14];
}
return self;
}
// I understand that NSNumbers are non-mutable objects and can't be
// used like variables.
// Hence I decided to make make the getter's implementation like this
- (double)myFloatValue{
return [myFloatValue floatValue];
}
- (void)setMyFloatValue:(float)floatInput{
if ([self myFloatValue] != floatInput){
[myFloatValue release];
myFloatValue = [[NSNumber alloc] initWithFloat:floatInput;
}
#end
When I mouse over the myFloatValue object during debugging, it does not contain a value. Instead it says: "out of scope".
I would like to be able to make this work without using #property, using something other than NSNumbers, or other major changes since I just want to understand the concepts first. Most importantly, I would like to know what mistake I've apparently made.
I can see a couple of mistakes:
The line #implementation should read #implementation MyClass
The function setMyFloatValue is missing a closing ] and } —it should read:
- (void)setMyFloatValue:(float)floatInput{
if ([self myFloatValue] != floatInput){
[myFloatValue release];
myFloatValue = [[NSNumber alloc] initWithFloat:floatInput];
}
}
I've just tested it in Xcode and it works for me with these changes.
Why not just set property in interface and synthesize accessors in implementation?
#interface MyClass : NSObject {
float *myFloat
}
#property (assign) float myFloat;
#end
#implementation MyClass
#synthesize myFloat;
#end

Why does this property need the 'retain'?

Given the following definition of a class with retain properties:
#interface FeedEntry : NSObject<NSCoding>
{
NSURL* url;
NSData* source;
}
#property (retain) NSURL* url;
#property (retain) NSData* source;
#end
#implementation FeedEntry
#synthesize url;
#synthesize source;
-(void)encodeWithCoder:(NSCoder*)coder
{
[coder encodeObject:url forKey:#"url"];
[coder encodeObject:source forKey:#"source"];
}
Why does the url property in initWithCoder method need the "retain":
-(id)initWithCoder:(NSCoder*)coder
{
url = [[coder decodeObjectForKey:#"url"] retain];
source = [coder decodeObjectForKey:#"source"];
NSLog(#"got url=%#\n", url);
return self;
}
Specifically, why doesn't the synthesized "get url" method retain the object? (I'm guessing the source property will need a retain as well).
Quick answer:
When you set:
url = [[coder decodeObjectForKey:#"url"] retain];
you are not using the #property. You are manually setting the value of the instance variable url. You must, therefore, also manually retain the value.
To set the variable using the synthesized properties, you would instead call:
[self setUrl:[coder decodeObjectForKey:#"url"]];
or
self.url = [coder decodeObjectForKey:#"url"];
Either of these forms would make use of the synthesized methods, and handle the retain automatically.
Details:
In Objective-C, the #property and #synthesize keywords automatically create the getter and setter methods for you:
#interface MyClass
{
id someValue;
}
#property (retain) id someValue;
#end
#implementation MyClass
#synthesize someValue;
#end
Is equivalent to:
#interface MyClass
{
id someValue;
}
- (id)someValue;
- (void)setSomeValue:(id)newValue;
#end
#implementation MyClass
- (id)someValue { return someValue; }
- (void)setSomeValue:(id)newValue
{
[newValue retain];
[someValue release];
someValue = newValue;
}
#end
This creates an important distinction between the "internal" member variable and the property having the same name. If you reference the member variable by name, you are bypassing the synthesized property methods.