iOS - Set Derived Property - ios

I have the following derived property:
SongWrapper.h
#property (nonatomic, strong) NSString *title;
SongWrapper.m
- (NSString *)title;
{
return self.songDocument.songAttributes.title;
}
I tried to set it like this:
self.songViewController.songWrapper.title = titleTextField.text;
Why doesn't this work and what are the best practices for setting a derived property?

You have to defined getter but not setter. You need to define setter as well.
//Getter
- (NSString *)title;
{
return self.songDocument.songAttributes.title;
}
//Setter
- (void)setTitle:(NSString *)title;
{
self.songDocument.songAttributes.title = title;
}

Related

Set the current Value of UITextField to a property when UITextField's "editingDidBegin" control event fire

How to set the current value of UITextField to a property(through a custom setter) declared in category which extends UITextField class when firing editingDidBegin control event of UITextField?
You should be able to do this using a category by taking advantage of Associative References.
From the docs:
Using associative references, you can add storage to an object without
modifying the class declaration.
Here's an example that will get you going in the right direction:
.h file:
#interface UITextField (StoredProperty)
#property (nonatomic, strong) NSString *testString;
#end
.m file:
#import <objc/runtime.h>
static void *MyStoredPropertyKey = &MyStoredPropertyKey;
#implementation UITextField (StoredProperty)
- (NSString *)testString {
return objc_getAssociatedObject(self, MyStoredPropertyKey);
}
- (void)setTestString:(NSString *)testString {
objc_setAssociatedObject(self, MyStoredPropertyKey, testString, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
Example use:
NSObject *obj = [NSObject new];
obj.testString = #"This is my test string";
NSLog(#"%#", obj.testString);

Objective-C Mutable subclass pattern?

Is there a standard pattern for implementing a mutable/immutable object class pair in Objective-C?
I currently have something like the following, which I wrote based off this link
Immutable Class:
#interface MyObject : NSObject <NSMutableCopying> {
NSString *_value;
}
#property (nonatomic, readonly, strong) NSString *value;
- (instancetype)initWithValue:(NSString *)value;
#end
#implementation MyObject
#synthesize value = _value;
- (instancetype)initWithValue:(NSString *)value {
self = [self init];
if (self) {
_value = value;
}
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}
#end
Mutable Class:
#interface MyMutableObject : MyObject
#property (nonatomic, readwrite, strong) NSString *value;
#end
#implementation MyMutableObject
#dynamic value;
- (void)setValue:(NSString *)value {
_value = value;
}
#end
This works, but it exposes the iVar. Is there a better implementation that remedies this situation?
Your solution follows a very good pattern: the mutable class does not duplicate anything from its base, and exposes an additional functionality without storing any additional state.
This works, but it exposes the iVar.
Due to the fact that instance variables are #protected by default, the exposed _value is visible only to the classes inheriting MyObject. This is a good tradeoff, because it helps you avoid data duplication without publicly exposing the data member used for storing the state of the object.
Is there a better implementation that remedies this situation?
Declare the value property in a class extension. An extension is like a category without a name, but must be part of the class implementation. In your MyMutableObject.m file, do this:
#interface MyMutableObject ()
#property(nonatomic, readwrite, strong) value
#end
Now you've declared your property, but it's only visible inside your implementation.
The answer from dasblinkenlight is correct. The pattern provided in the question is fine. I provide an alternative that differs in two ways. First, at the expense of an unused iVar in the mutable class, the property is atomic. Second, as with many foundation classes, a copy of an immutable instance simply returns self.
MyObject.h:
#interface MyObject : NSObject <NSCopying, NSMutableCopying>
#property (atomic, readonly, copy) NSString *value;
- (instancetype)initWithValue:(NSString *)value NS_DESIGNATED_INITIALIZER;
#end
MyObject.m
#import "MyObject.h"
#import "MyMutableObject.h"
#implementation MyObject
- (instancetype)init {
return [self initWithValue:nil];
}
- (instancetype)initWithValue:(NSString *)value {
self = [super init];
if (self) {
_value = [value copy];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
// Do not use the iVar here or anywhere else.
// This pattern requires always using self.value instead of _value (except in the initializer).
return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}
#end
MyMutableObject.h:
#import "MyObject.h"
#interface MyMutableObject : MyObject
#property (atomic, copy) NSString *value;
#end
MyMutableObject.m:
#import "MyMutableObject.h"
#implementation MyMutableObject
#synthesize value = _value; // This is not the same iVar as in the superclass.
- (instancetype)initWithValue:(NSString *)value {
// Pass nil in order to not use the iVar in the parent.
// This is reasonably safe because this method has been declared with NS_DESIGNATED_INITIALIZER.
self = [super initWithValue:nil];
if (self) {
_value = [value copy];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
// The mutable class really does need to copy, unlike super.
return [[MyObject allocWithZone:zone] initWithValue:self.value];
}
#end
A fragment of test code:
NSMutableString *string = [NSMutableString stringWithString:#"one"];
MyObject *object = [[MyObject alloc] initWithValue:string];
[string appendString:#" two"];
NSLog(#"object: %#", object.value);
MyObject *other = [object copy];
NSAssert(object == other, #"These should be identical.");
MyMutableObject *mutable1 = [object mutableCopy];
mutable1.value = string;
[string appendString:#" three"];
NSLog(#"object: %#", object.value);
NSLog(#"mutable: %#", mutable1.value);
Some debugging right after the last line above:
2017-12-15 21:51:20.800641-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801423-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801515-0500 MyApp[6855:2709614] mutable: one two
(lldb) po mutable1->_value
one two
(lldb) po ((MyObject *)mutable1)->_value
nil
As mentioned in the comments this requires discipline in the base class to use the getter instead of the iVar. Many would consider that a good thing, but that debate is off-topic here.
A minor difference you might notice is that I have used the copy attribute for the property. This could be made strong instead with very little change to the code.

One protocol, used by many classes

I understand how to make a class conform to several protocols, but where and how do I define a protocol that will be called by several classes, i.e.
_delegate.doStuff
could appear in several classes.
In xcode,
File-> New File-> Objective-c Protocol
#protocol myProtocolName
- (void) doStuff;
#end
then in classes you want to implement this protocol
...
#import "myProtocol.h"
#interface aClass <myProtocolName>
...
You can add this to any number of classes.
Just make a new protocol definition -- usually in a nicely #import'able .h file. In Xcode, this is under File, New, "Objective-C Protocol".
Here's a fun little example of two protocols, and some required and optional methods and properties. Note that properties on protocols must be synthesized in classes that conform to the protocol if the property is #required (#required is the default, so it can be left out if there is not #optional section).
// AnimalMinionType.h
#protocol AnimalMinionType <NSObject>
#required
#property (nonatomic, getter = isHerbivore) BOOL herbivore;
- (NSString *)genus;
- (NSString *)species;
- (NSString *)nickname;
- (void)doPet;
#optional
- (NSString *)subspecies;
#end
// IdiotType.h
#protocol IdiotType <NSObject>
#optional
- (void)pet:(id<AnimalMinionType>)pet didScratch:(BOOL)scratchy;
#end
// FluffyCat.h
#interface FluffyCat : NSObject <AnimalType>
#end
// FluffyCat.m
#implementation FluffyCat
#synthesize herbivore;
- (NSString *)genus { return #"felis"; }
- (NSString *)species { return #"catus"; }
- (NSString *)nickname { return #"damn cat"; }
- (void)doPet:(id<IdiotType>)anyoneOrAnything
{
NSLog(#"meow");
if ([anyoneOrAnything respondsToSelector:#selector(pet:didScratch:)])
[anyoneOrAnything pet:self didScratch:#YES];
}
#end
// Owner.h
#interface Owner : NSObject <IdiotType>
#property id<AnimalMinionType> housepet;
#end
// Owner.m
#implementation Owner
- (id)init
{
self = [super init];
if (self)
{
self.housepet = [FluffyCat new];
[self.housepet setHerbivore:#NO];
}
return self;
}
- (NSString *)ohACuteAnimalWhatKindIsIt
{
return [NSString stringWithFormat:#"%# %#",[self.housepet genus], [self.housepet species]];
}
- (void)haveALongDayAtWorkAndJustNeedAFriend
{
if (self.housepet) [self.housepet doPet:self];
}
- (void)pet:(id<AnimalMinionType>)pet didScratch:(BOOL)scratchy
{
if ((scratchy) && (pet == self.housepet))
{
NSLog(#"I HATE THAT %#", [[self.housepet nickname] uppercaseString]);
self.housepet = nil;
}
}
#end
I hope that helps. :-)

override property from superclass in subclass

I want to override an NSString property declared in a superclass. When I try to do it using the default ivar, which uses the the same name as the property but with an underscore, it's not recognised as a variable name. It looks something like this...
The interface of the superclass(I don't implement the getter or setter in this class):
//Animal.h
#interface Animal : NSObject
#property (strong, nonatomic) NSString *species;
#end
The implementation in the subclass:
//Human.m
#implementation
- (NSString *)species
{
//This is what I want to work but it doesn't and I don't know why
if(!_species) _species = #"Homo sapiens";
return _species;
}
#end
Only the superclass has access to the ivar _species. Your subclass should look like this:
- (NSString *)species {
NSString *value = [super species];
if (!value) {
self.species = #"Homo sapiens";
}
return [super species];
}
That sets the value to a default if it isn't currently set at all. Another option would be:
- (NSString *)species {
NSString *result = [super species];
if (!result) {
result = #"Home sapiens";
}
return result;
}
This doesn't update the value if there is no value. It simply returns a default as needed.
to access the superclass variables, they must be marked as #protected, access to such variables will be only inside the class and its heirs
#interface ObjectA : NSObject
{
#protected NSObject *_myProperty;
}
#property (nonatomic, strong, readonly) NSObject *myProperty;
#end
#interface ObjectB : ObjectA
#end
#implementation ObjectA
#synthesize myProperty = _myProperty;
#end
#implementation ObjectB
- (id)init
{
self = [super init];
if (self){
_myProperty = [NSObject new];
}
return self;
}
#end

Can I have a core data attribute decorated with readonly in the interface and then readwrite in the implementation?

In my NSManagedObject subclass I want to specify a particular attribute as readonly in the interface but readwrite in the implementation. I have done this before with other classes. But the Core Data generated subclasses use #dynamic in the implementation and it seems like I can't add the readwrite decorator to #dynamic like can be done with #property.
Is this possible?
You can declare the property in the header file as readonly and then provide your own implementation for the setter in the implementation:
(using ARC)
#interface Person : NSManagedObject
#property (nonatomic, readonly) NSString * name;
- (void)changeName;
#end
#implementation Person
#dynamic name;
- (void)changeName
{
self.name = #"Test";
}
- (void)setName:(NSString *)name
{
[self willChangeValueForKey:#"name"];
[self setPrimitiveValue:name forKey:#"name"];
[self didChangeValueForKey:#"name"];
}

Resources