[65823 Closed] Nougat - - ObjC Categories (class helper)

Hi,
what’s the Oxygene syntax for class helpers (ObjC Categories)?

For example for NSString?

    type
MyNSString = class helper for (NSString)
public
    method toEmptyString:String; 
end;

implenemtation

method MyNSString.toEmptyString:String;
begin
  self :='';
  exit (self);
end;

Just define an extension method on that type:


extension method NSString.toEmptyString: String;
..

extension method NSString.toEmptyString: String;
begin
  self := '';
  exit self;
end;

Ah.
I had seen the article about “extension” in the wiki, but have not recognized the relation between extension method and the related “base” class.

It has become clear now.
Thank you.

Has a Oxygene mechanism to avoid naming conflicts (for example if Apple implements [NSString toEmptyString] by default in iOS 8.x)? Am I then protected from conflicts by the Namespaces or is it better to provide all extension methods with a short prefix?

"toEmptyString" is of cause a unusabe example, but things like [[NSDate date] startOfTheDay] might be… quite times provided by Apple.

Hello,

If the extension method with the same name is already defined somewhere in another class or in your references the compiler shows you ambigous error like “(E110) Ambiguous call to overloaded method “startOfTheDay””.

Best regards.

Cocoa has no provision to handle name conflicts for this. A random method will win, probably yours. Apple recommends using __ as prefix for custom methods — bit I don’t follow that either, too ugly ;). FWIW, if expect “emptyString” to the the cocoa-style name for the method in question :wink:

Addendum re vika’s reply: yes, at compile time you’ll get the error (ie if you rebuild your app against ios 8). At runtime (your existing exe, running on iOS 8), you’ll get the undefined behavior)

OK, this naming part got clear now.
For now, I use a prefix for all extension method names.
That looks a little bit strange, but shoud avoid conflicts at runtime.

I’ve two more questions regarding extensions. Sorry …

I’ve created a set of extension methods for NSDate in that way.

namespace jwa.classhelpers.NSDate;
interface
   ....
  extension method NSDate.jwaInitWithFirstDayOfMonth(month:Integer) Year(year:Integer):NSDate; 
  extension method NSDate.jwaInitWithLastDayOfMonth(month:Integer) Year(year:Integer):NSDate; 
  ...
  extension method NSDate.jwaBeginningOfDay:NSDate;
  extension method NSDate.jwaEndOfDay:NSDate;
  extension method NSDate.jwaBeginningOfWeek:NSDate;
  extension method NSDate.jwaEndOfWeek:NSDate;

implementation 
...
  extension method NSDate.jwaBeginningOfDay:NSDate;
  begin
       ...
  end:

All extensions are currently defined as instance methods, but in fact the first both are “initializers” and should be class methods instead.

I tried a lot yesterday, but I haven’t found a way to extend a class with a class method.

In fact I must call the initializers in that way:

var lastDay := NSDate.alloc.jwaInitWithLastDayOfMonth(2) Year(2014);

This works, but it’s bad, because I create an unneeded NSDate instance and perhaps a memory leak too.

Is there already an existing way to create class method extensions resp. can you add this feature?

On the Wiki I’ve read that extensions are always classless.
That’s curse and blessing at the same time.
Is it possible to create class based extensions or make the code completion list more intelligent?

After I’ve added my “NSDate classhelper namespace” to the uses list I see my NSDate extensions in the code completion list of each class. It’s a bit confusing if I see the method “jwaAddDays()” on a UIView type (for example).

Hello,

  1. I logged a feature request (65823: Support of class modifier for extension method).
  2. Could you please provide us with a simple testcase? This is wrong and the NSDate extension method shouldn’t show in the CC list for UIView or another type except NSDate instance. Thanks in advance.

Best regards.

Hi,

I’ve attached a small testcase.

test case - class extensions.zip (503.9 KB)

This appears on my PC …

“jwaEndOfYear()” is an extension method for “NSDate”.
Can you reproduce that?

For me, the code formatting also doesn’t work in nougat projects.
I get the message: “The Shortcut (” CTRL + K, CTRL + D “) is bound to the command (” Format.Document “) that’s currently unavailable.”

Is something wrong with my VS2012 / Oxygene installation?

Hello,

We tried to reproduce the issue on several machines and couldn’t.
The code formatting doesn’t available at the moment but we are planning to implement that feature.

Best regards.

But what is the difference? I see all class extensions on any class.
Perhaps an issue that only occures in iOS Projects, because all classes are internally mapped to “id”?
Or a problem that only appears with VS2012? (Don’t know if you’re working with VS2012 or VS2013 …)

Perfect. That’s also applies to Objective-C Oxydizer.
The Oxydizer translates Obj-C categories correctly as “extension class method …”), but Oxygene can’t compile this afterwards, because of the class modifier.

What version of iOS are you building for? On iOS 6 and below, init* returns id, so the compiler treats btn as “var btn: id”. On iOS 7 init* returns instancetype, so the car should be strongly typed, like one would naïve expect.

I building against the iOS 7 SDK, but I had not set the “deployment target version” and the “target sdk” in my project options.
I have just changed this settings to iOS 7, but that didn’t solve my problem.

Hmm, ok, then this might be an unrelated issue. This only affects CC? Or does the compiler also accept the wrong extension methods?

The Compiler doesn’t accept wrong extension methods.
(E44) No member “jwaAddDays” on type “RootViewController”

But I have found a new aspect.

I have created a complete new project, added my existing “NSDate extension unit” to that project and added the namespace in “RootViewController.pas”.

Then I’ve written “self.jwa” and there where no NSDate Extension methods in the CC List of the class “RootViewController”. (All my NSDate Extension methods are prefixed with “jwa”).

Then I have used the NSDate extension for first time on a NSDate Variable.

var D:NSDate := NSDate.date;
title := D.jwaToFormattedStringDateStyle(NSDateFormatterStyle.NSDa....);

After using the extension initially with an variable of class NSDate, I have the extensions again in the CC of all other classes.

self.jwa.. // all NSDate extensions in CC List for class "RootViewController"

PS: All iOS “init methods” I’ve tried return rtl.id as result.
initWithNibName >> returns “rtl.id

Hello,

I reproduced the bug in CC here. Thank you for the report. The issue is logged for further review (65908: Extension method is shown on the wrong type).

Best regards.

Hi Viktoria,
can you say, if or when 65823 will be implemented?
Specially Nougat projects often work with categories (extensions) and class methods (custom initializers and so on)

it’s currently scheduled for the next release (after February, which as been locked down and will only see minimal/critical fixes between now and RTM next week).

bugs://65908 got closed as fixed

@jensw_2000: The latest compilers support “extension types”, which now allow you to do what you want, something like:

type
  MyDateExtensions = public extension class(NSDate)
  public
    class method jwaInitWithLastDayOfMonth(...)
  end;

this works on all 3 platforms

1 Like