Can I change the type of a property in a class extension? - objective-c

I wish to expose a property with a generic type in a public header, and then change its type to a more specific one in a private header. I'm using Clang, but though I'm able to change its read/write property, it doesn't accept a different type. So far this is what I tried:
A common client would import BKSystem.h:
#interface BKSystem : NSObject
#property(nonatomic, readonly) id<XYZWorker> worker;
#end
While a client for testing has access to internals by importing BKSystem+Testing.h:
#import "BKSystem.h"
#interface BKConfigurableWorker : NSObject<XYZWorker>
#property(nonatomic) BKConfiguration *config;
#end
#pragma mark -
#interface BKSystem ()
// Attempts to change worker to be writable and with a more specific type.
#property(nonatomic, readwrite) BKConfigurableWorker *worker;
#end
But on a testing client this is what I get:
#import "BKSystem+Testing.h"
BKSystem *system = [[BKSystem alloc] init];
// I am able to write to this property.
system.worker = [[BKConfigurableWorker alloc] init];
// ERROR: Property 'config' not found on object of type 'id<XYZWorker>'
system.worker.config = [[BKConfiguration alloc] init];

To accomplish what you're trying to do, I'd use BKWorker as a subclass rather than a protocol. See my example header file below:
#interface BKConfiguration : NSObject
#end
#interface BKWorker : NSObject
#property (nonatomic, strong, readonly) BKConfiguration *config;
#end
#interface BKConfigurableWorker : BKWorker
#property (nonatomic, strong, readwrite) BKConfiguration *config;
#end
Notice that the same property is declared again in the configurable worker, but as readwrite rather than readonly.
This produces the following results when used in an implementation:
Notice how the configurable worker can be written to but the standard one can not.
Hope this helps.

Related

"property has a previous declaration" error in class extension: bug or feature?

In Objective-C you can generally re-declare a readonly property as readwrite in a class extension like this:
#interface PubliclyImmutablePrivatelyMutableClass : NSObject
#property (readonly, nonatomic) SomeStateEnum someState;
#end
// In "PubliclyImmutablePrivatelyMutableClass+Private.h"
// or "PubliclyImmutablePrivatelyMutableClass.m"
#interface PubliclyImmutablePrivatelyMutableClass()
#property (readwrite, nonatomic) SomeStateEnum someState;
#end
// In "PubliclyImmutablePrivatelyMutableClass.m"
#implementation PubliclyImmutablePrivatelyMutableClass #end
If, however, I introduce a property in a class extension as readonly and try to re-declare it as readwrite in a second one, Xcode 10’s Clang gives me a compiler error:
#interface ClassWithPrivateImmutableInternallyMutableProperty : NSObject
// any public API
#end
// In "ClassWithPrivateImmutableInternallyMutableProperty+Private.h"
#interface ClassWithPrivateImmutableInternallyMutableProperty()
#property (readonly, nonatomic) SomePrivateStateEnum somePrivateState;
#end
// In "ClassWithPrivateImmutableInternallyMutableProperty.m"
#interface ClassWithPrivateImmutableInternallyMutableProperty()
#property (readwrite, nonatomic) SomePrivateStateEnum somePrivateState; // error: property has a previous declaration
#end
#implementation ClassWithPrivateImmutableInternallyMutableProperty
// other API
#end
Now I wonder:
Is the compiler error a bug/regression in Clang or a deliberate feature?
If it’s a bug, is there another workaround than manually implementing the setter?
I believe that this is correct behavior from the compiler.
In the second example you are using two class continuation categories with the same name () to declare the same property on two occasions. It is effectively the same as declaring the same property name twice in the same extension.
Note that this differs from the first example, in which the property is declared first in the header and then re-declared in a single class continuation category named ().
If I am right, then the answer is to mark the '+private' class extension with a name like (Private) instead of ():
#interface ClassWithPrivateImmutableInternallyMutableProperty(Private)
And also if you have any implementation for the private extension:
#implementation ClassWithPrivateImmutableInternallyMutableProperty(Private)
I hope that helps!

Forward Declaration vs #import when subclassing

I have MyClassA which has a property of type MyClassB
//
// MyClassA.h
//
#interface MyClassA : NSObject
#property (strong, nonatomic, readonly) MyClassB *myClassB;
#end
MyClassB has a property myString.
//
// MyClassB.h
//
#interface MyClassB : NSObject
#property (copy, nonatomic, readonly) NSString *myString;
#end
I have MyClassC which needs to access myString in it's implementation.
Should I -
a) Forward Declare MyClassB in MyClassA.h and #import "MyClassB.h" in MyClassC.m
or
b) #import MyClassB.h in MyClassA.h
In general, you should forward declare with #class where possible in your header files. The only time you probably wouldn't want to do it is when you're inheriting from a super class or declaring protocol conformance, because the compiler needs to know what is going on in that class or protocol.
For this instance, I would use #class for all your property declarations in your header files, and #import MyClassB.h in your MyClassC.m file. That will allow MyClassC to know about all the properties on MyClassB.
Looking at this from a slightly different angle ... you need to decide if you want the world to really know about myClassB being a property of MyClassA. For example, if you may only want to advertise that myString that can be obtained through MyClassA. This insulates other classes from knowing the underlying implementation of myString. Unless you have a need to expose MyClassB you should hide it from the "rest of the world".
In this case you would change MyClassA.h as follows:
//
// MyClassA.h
//
#interface MyClassA : NSObject
#property (strong, nonatomic, readonly) NSString *myString;
#end
In MyClassA.m, you would do the following.
//
// MyClassA.m
//
#import "MyClassA.h"
#import "MyClassB.h"
#interface MyClassA()
#property (strong, nonatomic) MyClassB *myClassB;;
#end
#implementation MyClassA
// Other meaningful code omitted
- (NSString *)myString {
return self.myClassB.myString;
}
#end
Note that what I've done here is use an anonymous category to internally define property for myClassB.
The key thing here is whether or not it makes sense to not expose MyClassB to others. The main advantage of this approach is your code is more malleable. Let's say myString gets derived a different way. From a different class or different method altogether. The code which needs to consume myString is immunized.
If you need to expose MyClassB, then you can either use #class as recommended by Tyler above or #import MyClassB.h from MyClassA.h. Best practices prescribe forward declaring #class. But at times the convenience of not having to remember to import a lot of files within the implementation file can win out. It's your code-base, so you can pick which one works the best for you. I generally use a combination of the two.

Objective-C equivalent of internal(set) in Swift

The internal(set) access modifier in Swift allows a property to be changed within the same module, but not from the outside. I'm curious about whether it has an Objective-C equivalent, and how I can implement it.
AFAIK, there is no equivalent in Objective-C.
But you can hide the setter outside from the module (Framework). For example:
MyObject.h: as Public header
#import <Foundation/Foundation.h>
#interface MyObject : NSObject
// `readonly` for public
#property (strong, nonatomic, readonly) NSString *myProp;
#end
MyObject-Internal.h: as Project header
#import "MyObject.h"
#interface MyObject ()
// NOT `readonly` for internal
#property (strong, nonatomic) NSString *myProp;
#end
Then, you can use MyObject-Internal.h in .m codes inside the module.

NSObject subclass as a property

I want to use my class as a property in my project. The idea is that i have a class which contains all list ellements. The basic idea i show below in graph:
So i have a myContainerClass object, and i want to do in some other class:
#property (strong,nonatomic) MyContainerClass *obj;
and here i have error! I figure out that i can only use Foundations type as a #property. But Why? What is replacement for doing that (passing an object)?
No, you can use any class you like as a property
#property (nonatomic, strong) MyContainerClass* obj;
is perfectly legal provided that the compiler knows that MyContainerClass is a class. To do that in the header file, the best way is to use an #class forward declaration:
#class MyContainerClass;
#interface SomeOtherClass : NSObject
// method an property declarations
#property (nonatomic, strong) MyContainerClass* obj;
#end
And then include the header file in the implementation:
#import "MyContainerClass.h"
#implementation SomeOtherClass
#synthesize obj;
// other stuff
#end
What is the error you are getting? May be you are not importing MyContainerClass to where you want to use it.
#import "MyContainerClass.h"
Declare a category for an object that you want to add your property to:
#interface NSObject (MyContainerClassAdditions)
#property (nonatomic, strong) MyContainerClass *myContainerClass
#end
Then implement the setter and getter methods using objective c associated object trick:
#import <objc/runtime.h>
#implementation NSObject (MyContainerClassAdditions)
- (void)setMyContainerClass:(MyContainerClass *)myContainerClass {
objc_setAssociatedObject(self, "myContainerClass", myContainerClass, OBJC_ASSOCIATION_ASSIGN);
}
- (MyContainerClass *)myContainerClass {
return objc_getAssociatedObject(self, "myContainerClass");
}
#end

Can I remove the #private generated by Core Data?

What is the #private for in the file generated by Core Data below? I know what #private means in Objective-C, but there are not instance variables listed after it, so can't I just take it out?
//
// Event.h
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Event : NSManagedObject {
#private
}
#property (nonatomic, retain) NSDate * timestamp;
#end
//
// Event.m
//
#import "Event.h"
#implementation Event
#dynamic id;
#end
You can safely take it out, it won't change the semantics of your class. If you're not statisfied with what XCode generates for you (though it's a reasonable default), I'd suggest you take a look at https://github.com/rentzsch/mogenerator.
You can, but it doesn't hurt. If you generate the model again it will just put it back.
Xcode now defaults to generating classes with #private for instance variables, which you are supposed to declare in case you need them. You can safely remove that #private since, as you’ve already noticed, there are no instance variables. In fact, that class declaration is equivalent to
#interface Event : NSManagedObject
#property (nonatomic, retain) NSDate * timestamp;
#end