I'm wondering how it is possible to create a class in Objective-C that cannot be allocated (or init)?
I'm trying to replicate the SKDownload Apple class (StoreKit framework) and I noticed that the documentation doesn't mention any alloc or init methods.
This may be missing in the documentation but present in the code ? (some .h declaration missing to prevent us from using this method?). I've tried to +alloc-init this class and either alloc and init return null.
What I'm trying to achieve is a class B that possesses only getters, that can only be created by a class A (just like factory methods would do - only A can create and return B instances, but the user cannot create B directly).
Example:
#interface A : NSObject
// Only A "+createB" class method can create B instances
+ (Second *)createBWithValue:(int)value;
#end
#interface B : NSObject
- (id)init; // return nil
+ (id)alloc; // return nil
- (int)value; // this returns the value passed from A
#end
Question 1: How would you proceed to create such classes? Is is possible ?
Question 2: How can - (int)value on B return the value passed in the class method in A? (knowing that +alloc, -init and/or other memory allocation methods may be nullified since users cannot create B classes directly - also custom initWithValue methods in B may be unavailable too)
I'm a little confused on how the Apple engineers do that..are there hidden methods that Apple doesn't want me to use?
Thanks you.
I noticed that the documentation doesn't mention any alloc or init methods
From the documentation:
Your app never directly creates a SKDownload object. Instead, after a payment is processed, your app reads the transaction object’s
downloads property to retrieve an array of SKDownload objects
associated with the transaction.
are there hidden methods that Apple doesn't want me to use?
Yes. There are a great many hidden methods that Apple doesn't want you to use.
So, the idea is that you're not supposed to create instances of SKDownload yourself, and for that reason the initialization method isn't part of the public API. That doesn't mean that there isn't a way to create SKDownload instances, just that there's not a public way for you to create them.
It's pretty common for classes to limit allocation or initialization. Singleton classes, for example, will often override +allocWithZone: such that it only allocates a new object if one hasn't already been created; if one exists, it returns that object instead (and the initializer basically does nothing).
Hide the initializer.
If you want to create a class like that yourself, override the inherited initialization method(s) (probably just -init) so that they return nil or maybe even throw an exception. Implement your own initializer, but keep it out of the public interface for the class:
// B.h
#interface B : NSObject
#property(readonly) int value;
#end;
// B.m
#interface B ()
#property(readwrite) int value;
-(id)secretInitializerWithValue:(int)v;
#end
#implementation B
-(id)secretInitializerWithValue:(int)v
{
if ((self == [super init])) { // not really necessary since NSObject's -init doesn't do anything
_value = v;
}
}
-(id)init
{
#throw [NSException exceptionWithName:#"Don't do that!"
reason:#"You shouldn't instantiate B yourself."
userInfo:nil];
}
#end
Private mutable subclass.
Another option is to create an immutable class with the read-only accessors you want, and a private mutable subclass that has read-write accessors. You can implement -copyWithZone: in the mutable subclass so that you get an immutable object when you make a copy. This all amounts to about the same thing I described above -- either way, you're hiding the creation mechanism from the client.
Related
EDIT: This question applies to normal declared properties as well (not only to class properties)!
Original Post:
Lets say I have the public class method sharedInstance which is currently implemented as a getter method:
#interface MyClass
+ (instancetype)sharedInstance;
- (void)doSomething;
#end
#implementation MyClass
+ (instancetype)sharedInstance {
static MyClass *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[MyClass alloc] init];
});
return shared;
}
#end
Accessing this method in Swift 3.0 would look like this: MyClass.shared().doSomething()
So to make it more swifty we should change the class method to a class property (new in Xcode 8. but actually I can't find it in the Apple Docu, only in WWDC 2016 video)
#interface MyClass
#property (class, nonatomic, readonly) MyClass *sharedInstance;
- (void)doSomething;
#end
// implementation stays the same
Now in Swift code: MyClass.shared.doSomething()
So does nonatomic/atomic property modifier (don't know the exact term) even makes sense for a getter method which I implement myself in objc?
The atomic/nonatomic modifiers have no effect in your case, for multiple reasons.
The main reason is that atomicity keywords affect only generated code (i.e. synthesized accessor methods). When you declare a #property in your interface and then implement it with a method (or method pair) in your implementation, the compiler isn't generating code, so your atomicity keyword is ignored.
There are a few ways to get to this situation, and you're triggering a couple of them:
First, you have a class property. The compiler can't synthesize accessors or storage for class properties — which means no code generation, so atomicity doesn't apply.
Second, in most common uses of readonly properties, the #property declaration is backed by a manually implemented getter method — which means there's no code generation and thus atomicity doesn't apply.
(Note you can also have instance properties declared as readonly in a public interface and synthesized due to a private readwrite redeclaration in your implementation. In that case, not only does atomicity apply, you have to make the atomicity keywords match between your public and private declarations. You can also synthesize just a getter and work directly with the backing ivar in your implementation.)
Because specifying either atomic or nonatomic for this property does nothing either way, you're free to just leave atomicity keywords out of your declaration entirely. (The compiler will assume atomic, but as noted that assumption has no effect.)
It makes perfect sense. The declaration of a property gives the user of the class information. An user of the class is allowed to expect from the – synthesized or manual – implementation, what you tell in the declaration. The user cannot even know, whether it is synthesized or not.
If you implement the getter (or whatever accessor) yourself, you should reflect the atomicity of your implementation in the declaration of the property. If you have a non-atomic implementation, you should add that to the declared property.
Description + sample + explanation: (You can skip to the question section)
I'd like to make an object instance, which can be implemented by different implementations, depend on a condition (the internet status).
Simple declaration
#interface LoginController : NSObject
/** The currently logged-in User. Nil if not logged-in yet. */
#property (strong, nonatomic) User *currentUser;
// Singleton object
+ (instancetype)shareInstance;
/** Abstract methods, will do nothing if call directly. Use inheritance implements (Online/Offline) instead. */
- (User *)loginByEmail:(NSString *)email password:(NSString *)pwd;
#end
#interface LoginControllerOnline : LoginController
// Login will call request to server.
#end
#interface LoginControllerOffline : LoginController
// Login will check data in coredata.
#end
The LoginController's login method actually do nothing (return nil). Instead, the inherited class (Online/Offline) overwrite the parent login's method, with different implementations (as in comments)
And then, I have a manager to define which class should be in use:
#implement InternetManager
+ (LoginController *)loginController
{
return [self hasInternet] ? [LoginControllerOnline shareInstance] : [LoginControllerOffline shareInstance];
}
+ (BOOL)hasInternet
{
// Check with Reachability.
}
#end
This work. But it's not the mechanism I'd like to achieve.
This mean I have 2 instances of inherited LoginController instead of 1.
When internetStatus change from offline to online, I'd like to re-login online (to get session/oauthToken...). But, I'll have to do many things (copy user, change instance, check retained...) before I can actually call from login online
QUESTION:
Is there a way for me to create only one instance of LoginController, which hold the same properties (User), but can has different (dynamic) implementations (Online/Offline)?
Update question:
Quote from Apple's Dynamic typing:
The isa Pointer:
Every object has an isa instance variable that
identifies the object's class. The runtime uses this pointer to
determine the actual class of the object when it needs to.
So, is there a way for me to change this isa pointer of an object instance?
It sounds like the real problem is that you've given these things direct primary ownership of state that you actually don't want them to own — factor it out. There's no copying, just give each an instance of the thing that marshals sate at -init and allow them to talk to it.
Then just do the normal programming thing when you want to do either one thing or another based on a condition: use an if statement.
So, I don't think use of the dynamic runtime is appropriate. However, academically, supposing an interest:
If you really must, use object_setClass, which "[s]ets the class of an object", answering your actual question. Obviously you need the storage to be compatible, so probably your subclasses shouldn't declare any properties or instance variables.
A commonly-discussed alternative for this general area is not changing the class of an existing instance but changing the methods that are a member of the class. So you'd have two alternative implementations of -loginByEmail:password: and set which was the one that actually responded to that selector dynamically. But there's really no advantage over just using an if if you have access to the source code and a bunch of disadvantages around its generally indirect, opaque nature. The whole thing is usually known as swizzling. class_replaceMethod is the key component but just search for swizzling.
Wherever I've read, it's written to never use an object without calling it's initializer function. And initializer functions always "have to" start with "init".
Is there a reason behind this naming (Does naming a method starting with init do something special)? What happens if I do not call the initializer function i.e. if I just do [MyClass alloc] and start using the object?
Alloc is called to allocate space in memory for the data type you are specifying. Whether it is NSString or NSNumber, calling Alloc will reserve the most efficient space in memory as possible for that data type (small or large).
Init is called to instantiate the class and superclass's important variables. These variables could include the Rect to recalculate a certain size in order to layout subviews, or perhaps instantiate with a delegate of some kind to perform some protocol upon creation. If it all becomes too much for you Objective-C does allow you to instantiate objects like MyClass *myObject = [MyClass new];
It may all seem redundant and a waste of time, but fortunately Swift has cut down tremendous amounts of redundancies like this in the new programming language. Now all you have to do is var myObject = MyClass() and if there are any custom initializers they would likely be writen like so var myObject = MyClass(frame: CGRectZero)
Happy coding!
I recommend reading the Apple Documentation on Object Initialization, and Initialization.
Is there a reason behind this naming (Does naming a method starting with init do something special)?
It is the convention, beginning a method name with init does not do anything special.
There are some minor quirks, such as if a method beginning with init returns type id, the compiler will convert the return type to instancetype, but these are barely worth mentioning.
What happens if I do not call the initializer function i.e. if I just do [MyClass alloc] and start using the object?
If you're using a standard SDK class, you'll likely run into a crash / exception. Initialization is used to set up the initial state of the instance variables, and without doing this can lead to undefined behaviour.
For a small example, if you called [[MyObject alloc] vegetable]; with the following class, you'd get nil returned because _vegetable hasn't been assigned.
#interface MyObject : NSObject
-(NSString*)vegetable;
#end
#implementation MyObject {
NSString *_vegetable;
}
-(instancetype)init {
self = [super init];
if (self) {
_vegetable = #"Cabbage";
}
return self;
}
-(NSString*)vegetable {
return _vegetable;
}
#end
It is the way you create objects in Objective-C. It is a hard requirement of the language. Creating an object in Objective-C is a 2 step process: alloc and init.
Under the covers, the reason you must call init is, wait for it, initialization.
The call to alloc creates a block of memory for your object and zeros it out. Calling init allows the object and it's ancestors to set things up so the object is ready to function. It initializes the object's memory and does other housekeeping that set the object up.
Further, every object's init method needs to call super init, so the initialization goes all the way up the object chain, all the way to NSObject. All the ancestor classes are designed to assume that their init method is called.
+ (id)alloc;
and
- (id)init;
are methods from NSObject.h
The alloc does
+ (id)alloc {
return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
#if 0 && __OBJC2__
// Skip over the +allocWithZone: call if the class doesn't override it.
// fixme not - this breaks ObjectAlloc
if (! ((class_t *)cls)->isa->hasCustomAWZ()) {
return class_createInstance(cls, 0);
}
#endif
return [cls allocWithZone: nil];
}
It does memory allocation, and return a class Instance.
But when I came to the init method, this is the implementation
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
It only return self object (NSObject) without doing any initialization.
The documentation also says the same thing.
"The init method defined in the NSObject class does no initialization; it simply returns self."
If that is the case, alloc method alone is sufficient.
Init method is only required for overridding.
Any explanation here?
This is the implementation source NSObject.mm
http://www.opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm
alloc is to do with memory allocation while init (or initX etc., the init family) is to do with configuring that allocated memory as needed when an object is created - whether any particular class, now or in the future following some revision, needs to do any work in init is dependent on the semantics of that class. However as you don't know for any arbitrary class whether it's init needs to do any work you must call it, and as any arbitrary class does not know whether its superclass needs to do any initialisation to must call its superclasses init within its own init. For this chain to work NSObject must have an init, it so happens that it (currently, who knows in the future) does no work. NSObject's init is the end of the chain and the only one that does not need to call another init.
Confusion arises for some as many languages combine the two operations, allocation and initialisation, into one indivisible operation, e.g. new in Java. Indeed Cocoa has a new as well which is defined as alloc followed by init.
And Apple should really have written:
The init method defined in the NSObject class currently does no initialization; it simply returns self.
or simply said nothing.
HTH
CRD explained it pretty well.
I will state it more strongly, however.
The way you create a new object in Objective C is to use alloc/init.
You should always initialize every object, and custom init methods should always call [super init]. Consider failure to call init on ALL objects an error, and also consider calling [super init] in your custom init methods an error.
I've implemented +resolveInstanceMethod on a class that subclasses NSDictionary. I will dynamically add methods to my class for certain cases, but I want standard NSDictionary methods to 'just work'.
I thought this would be the case if I just call [super resolveInstanceMethod:sel]; at the end of my method but it doesn't work.
+ (BOOL) resolveInstanceMethod:(SEL)sel
{
BOOL value = [super resolveInstanceMethod:sel]; // this is always NO!?
return value;
}
Why is this? How do I get my class to behave 'normally' for existing methods on the superclass?
What leads you to expect that -resolveInstanceMethod: would return YES for already existing methods? The release notes which introduced the new method say that the default implementation simply returns NO:
The NSObject class implements these methods with a default
implementation that returns NO today, though you should still invoke
it even if you're just directly subclassing NSObject.
In any case, it should never even be called for methods which are already present on the class. It's only called when you message an object with a selector which isn't already "resolved" to an implementation.