Is there a reliable way of overriding CellClass in classes derived from NSControl? - swift

This question seems to have been asked several times, but I have yet to find an definitive answer. I'm totally aware of the fact that NSCell is "being gradually phased out". This is great (and long overdue!), but it doesn't help in the here and now. Suppose I want to create a subclass of NSImageView which (e.g.) clips an image with an arbitraily shaped mask. I might define it like this:
class MaskedImageView : NSImageView {
public override class var cellClass: AnyClass? {
get { MaskedImageCell.self }
set {}
}
MaskedImageCell, of course, inherits from NSImageCell. This code compiles just fine under Swift 5.x, but the cell class is never changed. Adding the statement super.cellClass = newValue to the above property setter likewise makes no difference. (In fact, this isn't how the underlying AppKit machinery establishes the cell class. If I add a print statement inside the property getter, I can see that AppKit is interrogating the cell class, as I'd expect, but it never uses it.)
Like I said, I'm aware that NSCell is on the way out. But there are surely multiple use cases where it would be useful to change the cellclass to add specifics required by a particular application. If this isn't possible NOW, then is one supposed to try and replicate the entire functionality of (eg) NSImageCell. I surely wouldn't want to do that!
All constructive comments gratefully received. And please do bear in mind that I'm not actually interested in adding weird-shaped masks to NSImageViews. 😄 What I'm after is a definitive solution to the problem of introducing one's own cell class into the mix. We were told 7 years ago that NSCell was being deprecated, but while it's still with us, we should have a mechanism for introducing custom cell classes.

Related

Pros/cons of implementing a particular set of delegate methods versus not implementing them and the effects of implementing one but not another?

In regards to UITableView & UICollectionView & their respective protocols, UITableViewDataSource & UITableViewDelegate + UICollectionViewDelegate & UICollectionViewDataSource -- are there any pros and cons to implementing one method, but not the other or a particular set of methods and not the rest of the methods or a unique combination of specific methods?
For example, if I am to implement heightForFooter but not viewForFooter - what happens or can happen? Does this negative impact performance or the scroll of the table view?
Is there a guide which shows that which if any methods are implemented, others should be combined and dually implemented alongside them?
Re: correctness
Is there a guide which shows that which if any methods are implemented, others should be combined and dually implemented alongside them?
Not from what I've seen, though sometimes the documentation mentions it. E.g. from the docs of NSTableViewDataSource:
If you’re not using Cocoa bindings to provide data to the table view, the following methods are required:
numberOfRows(in:)
tableView(_:objectValueFor:row:)
tableView(_:setObjectValue:for:row:) (cell-based tables only)
In general, if a method is optional, but required from the context, then there will be a runtime error telling you to implment it, e.g.:
Illegal NSTableView data source (Foo). Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:
Either that, or stuff will silently fail (e.g. if you don't implement NSTableViewDelegate.tableView(_:viewFor:row:), no drawing will happen, and all your cells will just be blank views.
Re: performance
There shouldn't be any real performance difference with not implementing optional methods.
Each of these methods is likely called as if you had this in Swift (most of these frameworks are still implemented in Objective C):
let result: Result? = yourTarget.someMethod?(someArg)
which is really just shorthand for:
let result: Result? = yourTarget.responds(to: #selector(YourTargetsClass.someMethod))
? target.method(someArg)
: nil
That is, it's one dynamic method lookup to check if your object responds to the message, and one dynamic message send to actually invoke the method (for the case where it does respond).
There's a teeny tiny optimization that one could squeeze out here: if your method implementation is empty, then you're better off not having it at all. That will prevent a needless message send. Though it's obviously not something to worry about until you're sure it's a hotspot in a profiler.
Of course, nothing stops a library author from writing:
if yourObject.responds(to: #selector(someMessage)) {
doSomethingVeryExpensiveForNoGoodReason()
}
... but that's unlikely.

Can't subclass DispatchGroup - "only visible via the Objective-C runtime"?

It's not possible to subclass DispatchGroup, how to do so?
Note:
this has finally been fixed in iOS 10+
Example, carry a stateful package with a group,
class PushDispatchGroup: DispatchGroup {
var sentIds: [(tableName: String, rowId: String)] = []
}
thanks to shallowThought for pointing out this is fixed in iOS10.
how to inherit from a class which is 'only visible via the Objective-C runtime'?
You don't.
This is not wrapped around a proper "object" the way you're thinking about it. It's wrapped around a dispatch_group, which is a C struct. This class is not designed to be subclassed and does not bridge in a normal way to ObjC, so you can't subclass it there either.
"Objects" that bridge directly to low level C types often have very unusual structures that parts of the system are hard-coded to know how to deal with (this happens all the time with toll-free bridging like NSString and CFString). In many cases, the types are designed to be identical in memory layout through careful choice of structure (I haven't picked apart DispatchGroup, but it looks like one from this group). When that's true, you can't add any storage because then the memory layout will be different and you break the bridging.
As various commenters have said, you also shouldn't be doing this, which is why there is no easy answer for how to do this. Classes that are not explicitly designed for subclassing are intentionally difficult or impossible to subclass in Swift (this comes up regularly around less tricky types than DispatchGroup, and the answer is the same: it's intentional; don't do it).

Obj-c runtime: find what class another class is a property of

Suppose I have this:
#interface ThingManager : NSObject
#property Thing *aThing;
#end
Is there a way for aThing to know at runtime that it is a property of ThingManager?
You start with the question:
Is there a way for aThing to know at runtime that it is a property of ThingManager?
Though not completely clear the answers so far have assumed you want aThing to be able to determine whether an instance of ThingManger is managing it.
So at this point we know that the ThingManger class knows about the Thing class, and you now appear to want the reverse - you ask can a Thing find its ThingManager?
However when someone answers that you comment
So Thing and ThingManager need to know about each other? Mmmm... Bad for loose coupling.
Hmmm... How can a Thing ask what its ThingManager is if you don't want it to know about ThingManagers?
Is everybody's interpretation of your question wrong?
Maybe you are concerned not about loose coupling but about strong reference cycles? If the two know about each other, and create a strong cycle in doing so, then there is a risk they will keep each other alive long after the need for either by your application - is that the concern?
But that is addressed by #DrummerB - you use a weak property on Thing which references its managing ThingManager. So it would appear that strong reference cycles are not your concern either...
Then you comment:
Isn't there a way to see who's referencing an object at runtime?
As stated generally that is a very different question. The short answer to this is no[*].
Are you asking whether it is possible to write a method which a Thing can call to find the ThingManager that is managing it without any references being maintained between Thing instances and ThingManager instances?
If so here is an outline algorithm:
Have the ThingManager class keep a collection of all create and alive instances of itself. The alive requirement requires this collection be some kind of weak collection (design your own or do a search for weak collections).
Add a class method to ThingManager which finds the manager for a Thing, e.g. something like + (ThingManager *) managerFor:(Thing *)thing.
Have your Thing instances call [ThingManager managerFor:self] when they need to know their manager.
That of course doesn't address your concern over loose coupling - the two classes must still know of each others existence...
Which brings you back to the direct solution of having Thing have a manager property. If you want to reduce the coupling you can type this property as id, so its any old object, or id<SomeMinimalManagerProtocol> so its any old object provide it implements some minimal set of management methods you've define in the protocol SomeMinimalManagerProtocol.
Take a look at NSWindow and NSWindowController - they know about each other.
HTH
[*] The long answer is that (Objective-)C(++) are not designed to ensure all references can be found. This is why you hear phrases such as "conservative garbage collection". There was a garbage collector for just Objective-C objects, which was supported by internal changes to the runtime, but that is now deprecated and its machinery wasn't easily accessible to applications to apply to other uses.
Not unless you tell it, that it is.
You could add a ThingManager weak property to Thing and implement the setter in ThingManager like this:
- (void)setAThing:(Thing *)thing {
if (_aThing != thing) {
_aThing = thing;
_aThing.manager = self; // this is the relevant line
}
}
Not unless you have a custom setter for your aThing property that sets something on the Thing object to tell it that it is. You could have the aThing setter method set a property that you add to Thing that tells it who its manager is.

Best way to do init without repeating code?

Each view class has multiple init methods - ones already included as part of UIView, and then additional ones - and each of them set up the same elements in the same way. I therefore usually have them all running a [self initialSetup]; method, which includes the setting up of all of these elements.
The problem i've ran into is that if a subclass also has an initialSetup method, it would override the superclass initialSetup method, and thus the superclass would have to have the method be public in order to still function. This causes problems with organisation, as the method should never be called other than from init, so has no reason to be public.
You've hit upon a problem that there's no perfect fix for. What you'd ideally have is a method that can't be subclassed in the normal sense, that's accessible only to instances of that exact type of class.
Where this is a risk, the normal practice seems to be to incorporate the class name into the setup method. So instead of initialSetup you'd have something like myViewSubclassInitialSetup.
You can also add something like this at the top of your method:
NSAssert([self isMemberOfClass:[ThisClass class]],
#"IniitalSetup called by sub- or superclass")
Then your debug builds will raise an exception if a subclass or superclass ends up calling your init method. That'll give you a place for a breakpoint and a stacktrace that should allow you to find the problem very quickly.
It won't add any code to your release builds.
Change the name of initialSetup to something like initialSetupClassName - subclasses, even if they accidentally used the same pattern, would not use the same name as they had a different class name.
You can also use an "_" prefix for private methods you would rather not be called, but the subclasser may do that also.
It sounds like you are missing a designated initializer. Designate one initializer as the official one that actually performs the setup, and have all the others just call that with some degree of customization. Usually the designated initializer will be the one with the most detail — for example, if you have init, initWithName:, initWithName:age: and initAsFiveYearOldNamed:, the designated initializer will be initWithName:age: and the other initializers would just call that method with the arguments filled in appropriately.
Unfortunatly Objective C doesn't provide a way to achieve that in a "clean" way. The ideal solution would be a protected method. But that's not possible in Objective C
Apple had this problem, when they were creating the UIGestureRecognizer. There were some methods they really didn't want to get called by somebody, but which had to be overwritten by subclasses. The way they chose to deal with this, was to create a seperate header file (UIGestureRecognizerSubclass.h), that contains a category to the original UIGestureRecognizer with those "protected" methods. The additional header is only to be imported by subclasses (i.e. for subclassing purposes). See UIGestureRecognizer Class Reference for some details.
Of course that doesn't prevent anybody from misusing the additional header file, but at least it clearly states your intention and keeps your code well structured. Also you won't be "bothered" by autocompletion for the additional methods, when just using the class.
Personally I only use an additional header, if it is extremely important that nobody calls it directly. In most cases I think it's ok to use public methods and make a note for what it's inteded. The iOS Framework also has many of these cases. F.e. many methods of UIViewController's viewDidLoad etc.

Is an object in objective-c EVER created without going through alloc?

I know that there are functions in the objective-c runtime that allow you to create objects directly, such as class_createInstance. What I would like to know is if anything actually uses these functions other than the root classes' (NSObject) alloc method. I think things like KVC bindings might, but those aren't present on the iPhone OS (to my knowledge, correct me if I'm wrong), so is there anything that would do this?
In case you're wondering/it matters, I'm looking to allocate the size of an instance in a way that circumvents the objc runtime by declaring no ivars on a class, but overriding the +alloc method and calling class_createInstance(self, numberofbytesofmyivars).
Thanks
EDIT
I think I need to be more specific. I am adding classes to the runtime at runtime, and possibly unload and reload an altered version of the same class. I've worked around most of the issues so far, due to things like class_addMethod, but there's no equivalent for ivars after the class has been registered. The two solutions I can think of are having no actual ivars as far as the runtime is concerned, but overriding alloc to make sure I have enough room for them through extraBytes, or alternatively declaring an ivar which is a pointer to all of my actual ivars, which I can then obviously do whatever I want with. I would prefer to use the former strategy but there are a number of things that can go wrong, like if something allocates an instance of my object without going through my overloaded alloc method. Does anyone know of one of these things?
I'm not sure if you're trying to change the behavior of existing classes, which is not safe, or trying to do something for custom classes you own that are direct subclasses of NSObject, which probably is.
Almost all NSStrings you see in practice are instances of a private subclass, and that subclass allocates space for the string inline with the object. Like, instead of containing a pointer to a char*, the character data comes right after the ivars in the object. The extraBytes parameter in NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone) is there for purposes such as this.
So on the one hand, yes, you can pull tricks like that. On the other, you need to know you're doing it with your stuff. If you try to do something like that with the private subclass of NSString (which is private, so you're only going to interact with through runtime introspection), you're probably going to conflict.
There are a few public cocoa classes that also do stuff like this, so you're best off if your classes inherit directly from NSObject. NSLock is one. The layout in memory for a custom subclass of NSLock looks like { isa, <ivars of NSLock> <ivars of subclass of NSLock> <more NSLock stuff, space reserved using the extraBytes parameter> }.
Also, just for the heck of it, note that +alloc calls +allocWithZone:, and +allocWithZone: is the more common override point.
I'm not sure why you'd want to do what you're suggesting--I don't see any reason you couldn't do it, but according to this post, there's usually no reason to use class_createInstance directly (I don't know of anything that uses it specifically). class_createInstance also doesn't take into account memory zones or other possible optimizations used by alloc. If you're just trying to hide your ivars, there are better ways.
EDIT: I think you're looking for the class_addIvar function, which (as the name suggests) dynamically adds an ivar to a class. It only works with the new runtime, so it won't work on the simulator, but it will work on the iPhone.
EDIT 2: Just to be totally clear (in case it wasn't already), you can definitely rely on allocWithZone always being called. Fundamental Cocoa classes, such as NSString and NSArray, override allocWithZone. class_createInstance is almost never used except at the runtime level, so you don't have to worry about any parts of Cocoa using it on your classes. So the answer to the original question is "no" (or more specifically, objects are sometimes created without alloc, but not without allocWithZone, at least as far as I know).
Well there is nothing technically to stop you from overriding alloc. Just create a method in your class called +alloc. I just can't imagine any reason why you would need to.
Sounds like you are trying too hard to manage memory. Let the OS dynamically allocate memory when you create an object. If you are using too much, the OS will send a notification that you are getting close to the limit. At that point you can dealloc stuff you don't need anymore.
If you need so much memory that you have to use tricks, your implementation may need rethinking at the core level instead of trying to fit your square design into the round hole of the iPhone OS.
Just my opinion based on the info you provided.