Forward declare structs in Objective C - ios

I'm writing an iOS app in which I have a model class that is going to initialize itself with an XMLElement I give to it.
I'm using TBXML for the XML part.
The header for the model class looks like this:
#interface CatalogItem : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSManagedObject *group;
-(id)initWithXMLElement:(TBXMLElement*)element;
#end
Now instead of including the header in which TBXMLElement is defined, I'd like to forward declare it with: struct TBXMLElement before the class definition. I'm however getting an "Expected Type" error wich tells me my declaration isn't working. Is this not how I would got about this?
As I understand it, including header files in header files is bad practice. The compiler doesn't need to know the inner workings of TBXMLElement, just that it exists or will exist at compile time.

Forward declaration of structs are used all the time, but still involves importing a header. The motivation is to not allow developers to dip into the structure directly. I.e. look at CFString. It is implemented as a struct, but you can't touch the structure contents directly. Instead, there is a full API for manipulating the struct contents. This allows CFString's implementation details to change without breaking binary compatibility.
In your header (ideally the header that defines whatever API is associated with TBXMLElement*):
TBXMLElement.h:
typedef const struct TBLXMLElement *TBXMLElementRef;
extern TBXMLElementRef TBLXMLCreateElement();
... etc ...
Then, in the implementation file containing the implementation of the TBLXMLElementAPI:
TBXMElement.c (assuming it is a C file):
typedef struct __TBLXMLElement {
... struct members here ...
} TBLXMLElement;
TBXMLElementRef TBLXMLCreateElement()
{
return (TBXMLElementRef)malloc(sizeof(TBLXMLElement));
}
... etc ....

Related

Allow change in property only from one specific class

Suppose I have a class Participant which looks like this
Participant.h
#interface Participant : NSObject
#property(nonatomic, readonly) NSString *name;
#property(nonatomic, readonly) NSString *id;
#end
Here properties are readonly because I don't want anyone using this interface to change it
Besides ParticipantManager.h
What changes should I do in Participant class and how would I create ParticipantManager such that only ParticipantManager can change properties of Participant
More context
I get an event from react-native when value changes. to keep things in sync, I want my interface ParticipantManager to only change the values.
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#interface ParticipantManager : RCTEventEmitter <RCTBridgeModule>
#end
^^ Above class should only be able to change properties of Participant class
For a user to change a value, he would call changeName method, which will send an event back to react-native where react-native would change value and send back to native code
what I have tried.
So, I thought about using class extensions concept but I am getting an error.
Here is what I did
I create Participant+Private.h which implements setName method
#import " Participant.h"
interface Participant()
- (void)setName:(NSString)name
- (void)setId:(NSString)name
#end
PS: I implemented setName and setId method in Participant.h
- (void)setName:(NSString)name {
_name = name;
}
but then when I am using it in my ParticipantManager.h, it is throwing error
No visible #interface for Participantdeclares the selector
setName
I am using it like this
#import "Participant+Private.h"
NSString* value = #"varun";
[[Participant sharedInstance] setName:value];
Can someone help me in fixing error?
Slightly detailed question here:
No visible #interface for Participant declares the selector setName
Objective-C doesn't provide that sort of reasoned privacy, e.g. "I am private to everyone except one other specific class which I hereby name". (Actually, I don't know any language that behaves that way, but that's not to say that there are no such languages of course.)
If this is a framework, you can use #package privacy to confine the privacy of something to other classes in the same framework.
Otherwise, Objective-C generally solves the visibility problem by importing headers, so if you put public accessors for Participant into a header and the only class that imports that header is ParticipantManager, then only ParticipantManager sees them.

How to create a new object and assign properties in Objective-C?

I'm trying to bridge an Objective C SDK with React Native and I'm having some trouble. I have a Subclass of NSObject and I'm trying to set some property values but I can't get it to work.
I have tried to change the property in the header, and in the imp file with out any difference.
PrinterSDK.h (which has libPrinterSDK.a)
#interface Printer : NSObject
#property (nonatomic, readonly) NSString* name;
#property (nonatomic, readonly) NSString* UUIDString;
#end
RNPosPrint.m
#interface Printer ()
#property (readwrite) NSString* name;
#property (readwrite) NSString* UUIDString;
#end
RCT_EXPORT_METHOD(printTestPaper:(NSString*)name:(NSString*)uuid)
{
Printer* printer = [[Printer alloc] init];
printer.name = name;
}
But I keep facing issue with the setter for some reason I can't figure out.
ExceptionsManager.js:94 Exception '-[Printer setPrinterName:]: unrecognized selector sent to instance 0x13fd25b90' was thrown while invoking printTestPaper on target RNPosPrint with params (
"Test Printer",
"XXX-XXX-XXX"
)
You do not report the names of your .h and .m files or what else is in the .m – e.g. #implementation of Printer? The class printTestPaper belongs to? Without details like this it is difficult for anyone to help you, you need to help people help you.
That said some points that may help you:
The #interface Printer () where you open up the properties to be writeable should be in the your Printer.m file – in general do not try to open up access to a type's properties from outside the type's implementation, it is both bad design and may not work as you hope (as you just found out).
The code to support a #property is generated by the compiler when it compiles the #implementation, #interface's themselves produce no executable code – they describe the accessible parts of the #implementation.
setter=<name> provides a different name for the auto-created property setter function. While a method <name> will be created to set the property using dot syntax the properties name is still used, e.g. in your case printer.name = ... is still used even with the setter=setPrinterName:. You can call the auto-created method using standard method syntax, that failed in your case for the reasons above.
Using setter=<name> or getter=<name> are really advanced features and you probably will never need to use them – when you do need to use them you will know! Just avoid them till then.
If you wish to provide a method which creates the object and sets properties then do this in the type's implementation. The usual way of doing this is to provide an init method that does this, e.g. in this case it might be - initWithName:(NSString *)printerName { ... }, or an equivalent class method which does the allocation and sets the parameters, e.g. in this case it might be + newWithName:(NSString *)printerName { ... }.
HTH
Since it's an interface from statically linked library it is simply not possible to extend or manipulate. Not without tempering with the compiler.

Objective C Singleton method not available in Swift Interface

I've been looking for documents of reasons of why a singleton method definition in Objective-C class is not available on the Swift interface on Xcode.
The Objective-C class is defined like this
/**
* A general accessor across the sample App to remove the dependency of QPSLibraryManager from resusable components.
*/
#interface QPSSharedAccessor : NSObject
/**
* Required by QPSLibraryManager and some UI components.
*/
#property (nonatomic, strong) QPSApplicationConfiguration *qpsConfiguration;
/**
* Provides Commands to app
*/
#property (nonatomic, strong) id<QPSAppController> appController;;
/**
* Shared singleton.
*/
+ (instancetype)sharedAccessor;
/**
* Returns access to a configuration manager
*/
- (QPSConfigurationManager *)configurationManager;
#end
On Swift Interface, its defined like this
/**
* A general accessor across the sample App to remove the dependency of QPSLibraryManager from resusable components.
*/
open class QPSSharedAccessor : NSObject {
/**
* Required by QPSLibraryManager and some UI components.
*/
open var qpsConfiguration: QPSApplicationConfiguration!
/**
* Provides Commands to app
*/
open var appController: QPSAppController!
/**
* Returns access to a configuration manager
*/
open func configurationManager() -> QPSConfigurationManager!
}
I'm expected to see the sharedAccessor() singleton method on Swift but it is missing as you can see. Calling the said method on a separate swift file results in a compiler error, saying that the sharedAccessor() method doesn't exist. Converting everything to Swift is not viable btw. Advice on fixing this problem?
After some experimentation, I see that sharedAccessor() appears to have some special meaning. Using a different name for it, e.g. sharedAccessor1() or sharedInstance(), worked for me just fine. One related observation is that trying to call sharedAccessor() when it is NOT part of the interface results in this error: Type QPSSharedAccessor has no member sharedAccessor, which makes sense. However, adding sharedAccessor() to the Objective-C code results in:
'sharedAccessor()' is unavailable: use object construction 'QPSSharedAccessor()'
Opening the error details sheds more light on this:
'sharedAccessor()' has been explicitly marked unavailable here (__ObjC.QPSSharedAccessor)
Now, renaming the QPSSharedAccessor type to something else makes sharedAccessor() acceptable, but if the new type name is, for example, QPSMyClass, then naming the method myClass() becomes a problem!
To work around this strange problem, evidently having to do with compiler internals, obscure method naming conventions, or a bug, you can simply rename the sharedAccessor() method to something else. Alternatively, you can write a wrapper method in C or Objective-C and make it available to Swift via the bridging header. For example:
QPSSharedAccessor * getGlobalQPSSharedAccessor()
{
return [QPSSharedAccessor sharedAccessor];
}
You could also add a category to QPSSharedAccessor with a method having a different name and delegating to sharedAccessor().
Also, please see if these references are useful:
Can't build in Xcode with error "[method] has been explicitly marked unavailable"
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html (see the Singleton section).

Static const definitions in .h vs external const in .m

I've find it frustrating to define constants in the .h as externals and then assign the constants in the .m file. Seems so redundant. Is there any reason not to just define the constants in the header file?
Typical implementation would be:
// Constants.h
#interface Constants : NSObject
extern NSString *const kPCFavorites;
#end
The implementation would then be:
// Constants.m
#implementation Constants
NSString *const kPCFavorites = #"PCFavorites";
#end
However, I can just do this:
// Constants.h
static NSString *const kPCFavorites = #"PCFavorites";
#interface Constants : NSObject
#end
Obviously, this last definition doesn't even need an interface or implementation so both could be left out and become:
// Constants.h
static NSString *const kPCFavorites = #"PCFavorites";
with no .m file at all.
This seems much cleaner to me. Why wouldn't we implement constants this way? I've defined them both ways and I get no compile or runtime errors in XCode 5.
Because
static NSString * const kPCFavorites = #"PCFavorites";
declares a variable, not a constant. C doesn’t actually have a way to declare a symbolic constant (besides enum, which only works for integers).
As a result, if you use this method, every file that #includes your header will have its own variable called kPCFavorites. Historically, that would have meant that your program would increase in size because of all of the copies of kPCFavourites and the string #"PCFavourites", although more modern linkers may manage to get rid of some or all of the duplication (certainly I’d expect the linker to leave you with only one copy of the string itself; whether or not it can currently get rid of the extra pointer variables I’m not sure — but it’s easy to test).
static variables have file scope. If you compile a file which includes a header file with a static variable, that variable will exist in the compiled file. If you compile another file with the same header file, you have a second static variable and so on. If you include the header from 1000 source files, you get 1000 static variables, all with the same name.

valueForKey: on packed struct?

Given the following packed struct:
typedef struct __attribute__((packed)) {
BOOL flag;
int x;
} Foo;
And the following class:
#interface Obj : NSObject
#property (nonatomic) Foo foo;
#end
#implementation Obj
#end
Trying to call valueForKey: on a property which has type of packed struct:
Obj *obj = [Obj new];
id boo = [obj valueForKey:#"foo"];
causes a crash inside valueForKey: (actually it's crashing not inside valueForKey: but in random places depending on moon magnitude, I guess it's memory corruption).
If I remove __attribute__((packed)) it works fine.
Any possibility to get struct's data without a crash? Is it Apple's bug?
PS. I do need to do it at runtime, i.e. I can't just call .foo directly, I only have #"foo" string at runtime. (What I'm trying to achieve actually is to recursively print object contents).
I don't know if this is possible with properties, but if it is I don't think you're using the right syntax.
Have you tried changing
id boo = [obj valueForKey:#"foo"];
to read
Foo boo = obj.foo;
?
Foo is not nor never will be an id. valueForKey: returns id, and the runtime might be barfing trying to squeeze struct Foo into an NSValue.
If you need to use valueForKey: for some reason, your accesses need to look like.
Foo myFoo = FooFactory();
Object *myObj = [Object new];
[myObj setValue:#( myFoo ) forKey:#"foo"];
Foo myFooOut;
[[myObj valueForKey:#"foo"] getValue:&myFooOut];
//I bet `getValue:` is where things are barfing.
In this case, if NSValue's machinery indeed can't handle the packed struct, you just have to write the accessors the old fashioned way: -<key> and -set:`.
PS: Never name a class "Object", there actually is an Object in some SDKs that NSObject inherits from. I assume that's just in your example.
Avoid KVO for your usage case, and stick to the handy dandy <objc/runtime.h> header. You can re-implement a basic part of KVO using this kind of introspection. Struct packing is used primarily to make sure the compiler doesn't align internal fields properly so the CPU does not need to handle it at the hardware level during execution. In this day and age, it's better to let the compiler take care of things for you.
If you'd really prefer this struct packing (as you said, in a network transmission scenario), I'll need further information to determine where the issue lies. Perhaps attempt to change #property (nonatomic) Foo foo; to #property (nonatomic) NSValue *foo; and then box it and unbox it yourself? This way, the exception/error will be in your application's domain.

Resources