What can I do if two methods call each other and I don't want to make one of them public in the header file? - objective-c

I have two methods -a and -b.
-a calls sometimes -b, and -b sometimes calls -a. Both methods are intended to be private, and not called from outside.
But I had to make one of them public in the .h file, because otherwise the compiler would go crazy and give a warning for either one of them.
Is there any valid and good-practise solution for that problem?

Traditionally, what you'd do is define a category (something like #interface MyClass (MyClass_Private) inside the implementation file that declares the private methods. Apple recently introduced a feature called a class extension that is intended for this exact case. It's basically a specialization of a category, but the class has to implement the methods when it's first defined. It looks like:
#interface MyObject ()
- (void)setNumber:(NSNumber *)newNumber;
#end

Implement a protocol.
or
Write a second header file with a category.

If you really want the functions to be private, you need to declare them as static. To eliminate the cyclic dependency, one should be declared before the other is defined. Here's a simple example:
static void b(); /* forward declaration */
static void a()
{
if (foo)
b(); /* forward-declared, so we're ok */
}
static void b()
{
if (bar)
a(); /* already defined, so we're ok */
}
This is all valid C, and so based on the OP's comment I assume this is valid ObjC as well.

Related

Testing private methods in Raku

Is there a way to test private methods in Raku?
I understand that one should ideally define their tests targeting the public methods, but is there a way to do it "the wrong way"? :)
I initially thought about defining a subclass for the Testing that inherited from the class I wanted to test and do the tests there, but it seems that private methods are not inherited.
Then I saw the 'trusts' routine, but I wouldn't want to reference a Testing class on any of the classes of the code.
Is there something like changing the 'private' property of a method via introspection?
What would be the best way to call/test a private method?
This can be done using introspection.
Consider this is the class you want to test:
class SomeClass {
has Int $!attribute;
method set-value(Int $value) returns Nil {
$!attribute = $value;
return;
}
method get-value returns Int {
return $!attribute;
}
# Private method
method !increase-value-by(Int $extra) returns Nil {
$!attribute += $extra;
return;
}
}
You may create a test like this:
use Test;
use SomeClass;
plan 3;
my SomeClass $some-class = SomeClass.new;
my Method:D $increase-value = $some-class.^find_private_method: 'increase-value-by';
$some-class.set-value: 1;
$increase-value($some-class, 4);
is $some-class.get-value, 5, '1+4 = 5';
$increase-value($some-class, 5);
is $some-class.get-value, 10, '5+5 = 10';
my SomeClass $a-new-class = SomeClass.new;
$a-new-class.set-value: 0;
$increase-value($a-new-class, -1);
is $a-new-class.get-value, -1, '0+(-1) = -1; The method can be used on a new class';
done-testing;
You first create an instance of the class and the use ^find_private_method to get its private Method. Then you can call that Method by passing an instance of a class as the first parameter.
There's a more complete explanation on this answer:
How do you access private methods or attributes from outside the type they belong to?
A fresh cup of tea and #Julio's and #JJ's answers inspired the following:
class SomeClass { method !private ($foo) { say $foo } }
use MONKEY-TYPING; augment class SomeClass { trusts GLOBAL }
my SomeClass $some-class = SomeClass.new;
$some-class!SomeClass::private(42); # 42
My solution tweaks the class using monkey typing. Monkey typing is a generally dodgy thing to do (hence the LOUD pragma). But it seems tailor made for a case just like this. Augment the class with a trusts GLOBAL and Bob's your Uncle.
Raku requires the SomeClass:: qualification for this to work. (Perhaps when RakuAST macros arrive there'll be a tidy way to get around that.) My inclination is to think that having to write a class qualification is OK, and the above solution is much better than the following, but YMMV...
Perhaps, instead:
use MONKEY-TYPING;
augment class SomeClass {
multi method FALLBACK ($name where .starts-with('!!!'), |args) {
.(self, |args) with $?CLASS.^find_private_method: $name.substr: 3
}
}
and then:
$some-class.'!!!private'(42); # 42
I've used:
A multi for the FALLBACK, and have required that the method name string starts with !!!;
A regular method call (. not !);
Calling the method by a string version of its name.
The multi and !!! is in case the class being tested already has one or more FALLBACK methods declared.
A convention of prepending !!! seems more or less guaranteed to ensure that the testing code will never interfere with how the class is supposed to work. (In particular, if there were some call to a private method that didn't exist, and there was existing FALLBACK handling, it would handle that case without this monkey FALLBACK getting involved.)
It should also alert anyone reading the test code that something odd is going on, in the incredibly unlikely case that something weird did start happening, either because I'm missing something that I just can't see, or because some FALLBACK code within a class just so happened to use the same convention.
Besides using introspection, you can try and use a external helper role to access all private methods and call them directly. For instance:
role Privateer {
method test-private-method ( $method-name, |c ) {
self!"$method-name"(|c);
}
}
class Privateed does Privateer {
method !private() { return "⌣" }
}
my $obj = Privateed.new;
say $obj.test-private-method( "private" );
The key here is to call a method by name, which you can do with public and private methods, although for private methods you need to use their special syntax self!.

Extending a Swift class with Objective-C category

Im in a situation where I need to use Objective-C category to extend a Swift class. I've done something as follows:
In "SomeClass.swift":
class SomeClass: NSObject {
}
In "SomeClass+Extension.h":
#import "Project-Swift.h"
#interface SomeClass (Extension)
-(void)someMethod();
#end
This has worked well. And if I try to use the SomeClass extension in my Objective C code, it is fine.
The problem is, if I want to use someMethod() in a another Swift class, I will need to put the SomeClass+Extension.h file into my ObjC-BridgingHeader.h file.
But doing this will cause a circular dependency, because SomeClass+Extension.h also imports Project-Swift.h.
Does anyone have a good way to get around this?
Please note that simply forward declaring the class in the category header will not work, as categories cannot use forward declarations for it's own implementation as so:
#class SomeClass without importing Project-Swift.h will give a compile error.
The Bad
i too have been fighting this issue a bunch. unfortunately the documentation pretty explicitly states that this pattern is not allowed:
To avoid cyclical references, don’t import Swift code into an
Objective-C header (.h) file. Instead, you can forward declare a Swift
class or protocol to reference it in an Objective-C interface.
Forward declarations of Swift classes and protocols can only be used
as types for method and property declarations.
also throughout the the linked page you will notice it keeps mentioning to import the generated header specifically into the .m file:
To import Swift code into Objective-C from the same target
Import the Swift code from that target into any Objective-C .m file
within that target
The Good
one solution that may work for you is to create a swift extension that redefines each method you need in the category. it is fragile and ugly, but arguably the cleanest solution.
/**
Add category methods from objc here (since circular references prohibit the ObjC extension file)
*/
extension SomeClass {
#nonobjc func someMethod() {
self.performSelector(Selector("someMethod"))
}
}
adding the #noobjc to the front allows the
same method signature to be used w/o overriding the ObjC implementation
now the import "SomeClass+Extension.h" from the bridging
header can be removed
if support for more than two input params is needed, or tighter type coupling is desired i would recommend using the runtime to call the underlying function. a great description is here.
From the Interoperability guide, we cannot directly access the subclassed / categorized / extensioned Objc-objects for the .swift [SomeClass] class.
But as a turn-around, we can do this:
For Variables , we can do this:
extension Class {
private struct AssociatedKeys {
static var DescriptiveName = "sh_DescriptiveName"
}
var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
}
For Methods, we can use method_swizzling which is not recommended.
As one simple solution, you can move the extension to your Swift code. Then you won't have any dependency problems.

How can I exclude a method inside a class with doxygen

I am using Objective-C language. And I have 3 classes: Chicken, Dove, Dog. Each of class has run method.
Chicken.h
-(void)run;
Dove.h
-(void)run;
Dog.h
-(void)run;
I want to exclude run method inside Dove class. I found that EXCLUDE_SYMBOLS can do that. But if I set configuration : EXCLUDE_SYMBOLS = save, all save methods in 3 classes are excluded.
Am I able to exclude save method of Dove class only?
I do not know objective-c, but for C++ the only way I know to do this would be to pre-process it out. To do this set the configuration values:
ENABLE_PREPROCESSING = YES
PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS
Then wrap the method you want excluded inside an ifndef block. Here is a C++ example:
/// doc comment for class Dove
class Dove {
public:
/// doc comment for foo
void foo();
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// the bar method will not be seen by doxygen, so you should not
// see it in any documentation
void bar();
#endif
};
This is a general strategy you may use. Anything inside the #ifndef blocks will be pre-processed away and will not be visible to doxygen.

using static c variables in Objective C classes

i have helper C functions in some Objective C classes.
Just found out that the values of global, static C variables which i use in these functions are shared between instances of the class (duh), which is not what i want.
Is there a way to declare these variables local to instances of the class, so that they are visible by the helper functions without passing them explicitly?
Is there a way to declare these variables local to instances of the class
Sure, make them instance variables.
But:
so that they are visible by the helper functions without passing them explicitly?
You can pass the object into the function. If you have appropriate accessors, the function can get them. And if you have mutators, it can modify them, too.
But if you're doing that, you might as well just create a method, and automatically have access to the instance variables.
want to avoid method calls where necessary
logically separate it so your low level code is in c or c++, then add the required data to your objc class:
/* c example */
typedef struct t_generator {
UInt32 a;
} t_generator;
static void Generate(t_generator* const gen) {
/.../
}
#interface MONObjCGeneratorContainer : NSObject
{
t_generator generator;
NSString * name;
UInt32 b;
}
#end
if the data interface is as simple you can just access them from the instance:
- (void)method { GenerateB(&b); }
that should meet all the requirements you have posted (so far).

class variable scope issue with objc / cocoa?

Compiling in XCode 3.1.3 using GCC 4, under Leopard 10.5.8, with 10.5 as my target...
I have an interface, thus:
#interface testThing : NSObject { classContaininghooHa *ttv; }
#end
And an implementation, thus:
#implementation: testThing
- (void) instanceMethodMine
{
[ttv hooHa]; // works perfectly, compiles, links, hooHa is invoked.
}
// void cFunctionMine()
// {
// [ttv hooHa]; // compiler: 'ttv' undeclared (first use in this function) "
// }
void stupidCFunctionMine((testThing *)whom) // whom is passed class 'self' when invoked
{
[whom instanceMethodMine]; // compiles, links, works. :/
}
#end
Now, my understanding -- clearly flawed -- was that if you declared a variable, class ID or otherwise, it was private to the class, but within the class, is performed essentially as a global, stored in the allocated class instance for the duration of its existence.
That's how it acts for objc methods.
But in the c function above, also written within the class, the variable appears to be invisible. The doesn't make sense to me, but there it is.
Can someone explain to me what is going on?
While you're at it, how can I declare this as an instance variable so I can use the method within a c function declared within the class scope as shown above, as well as within methods?
Insight much appreciated.
It doesn't make any difference where you are declaring/defining your "normal" c functions. They are not part of the class, they are just plain old c functions. No connection to the class whatsoever. Passing the instance they work on is a workaround if you really don't want to make this function a true objective-c method.
interface methods have full access to it's member variables. And the C function is not part of the class and so it cannot access any class variables unless it takes an class instance as the argument.
void cFunctionMine()
{
[ttv hooHa]; // compiler: 'ttv' undeclared (first use in this function)
}
Clearly cFunctionMine is not part of the interface. So it does not what ttv is to send the message hooHa.
While you're at it, how can I declare this as an instance variable so I can use the method within a c function declared within the class scope as shown above, as well as within methods?
void cFunctionMine()
{
// 1. Create an instance using alloc and init
testThing *ttv = [ [testThing alloc] init ] ;
[ttv hooHa] ;
// Now the above statement is valid. We have a valid instance to which
// message can be passed to.
// .....
[ ttv release ] ;
// release the resources once you are done to prevent memory leaks.
}