accessor-Objective C - objective-c

I am new to Objective C.
I need to know how to access the instance variable using the accessors.
I could able to access integer variables but not the character variable which i have declared here.
Please correct if there is any wrong in this code below.
#define SIZE = 4096
#interface data : NSObject
{
unsigned char id[SIZE];
}
#property(readwrite)unsigned char id[SIZE];
#end
#implementation data
#synthesize unsigned char id[SIZE];
#end
main.m
someClass* classPointer = [[someClass alloc]init];
data* dt = [[data alloc]init];
[classPointer createMessage:data.id];

Why not use an instance of NSString or NSData for the instance variable instead of an array of chars? For example:
#interface Foo2 : NSObject
{
NSString *_dataId;
}
#property (nonatomic, retain) NSString *dataId;
#end
#implementation Foo2
#synthesize dataId = _dataId;
#end
Otherwise you'd have to do something along these lines:
#define DATA_ID_SIZE 4096
#interface Foo : NSObject
{
char _dataID[DATA_ID_SIZE];
}
#property (nonatomic, assign) const char *dataID;
#end
#implementation Foo
// Returns a copy of the internal array of chars.
- (const char *)dataID
{
size_t length = strlen(_dataID);
// Dynamically allocate an array of chars to return.
char *buf = malloc(length);
// Copy the values from the internal array.
memcpy(buf, _dataID, length);
return buf;
}
- (void)setDataID:(const char *)dataID
{
// Copy provided chars into the internal array.
memcpy(_dataID, dataID, DATA_ID_SIZE);
// To be on the safe side, copy null character to the last element.
_dataID[DATA_ID_SIZE - 1] = '\0';
}
By the way, id is a data type in Objective-C, so it's best not to use it as a variable name.

You haven't set id anywhere, so it's just going to be nil.
Also on a side note, you must release the objects that you alloc init.

How do you want to manage the memory is the sample above? char c[size] is an array and it will take sizeof(char) * size bytes as the class member, but the property declared in the same manner will read/write pointer, not the data! I suggest you to use NSData* (or NSMutableData) instead of C-array, it's the preferred way for Obj-C.

Related

ObjectC-Why can't I get the properties correctly using the class_copyPropertyList function?

macOS 11.5.2
Xcode 13.2.1
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <iostream>
int main(int argc, const char * argv[]) {
#autoreleasepool {
Class clazz = NSClassFromString(#"NSString");
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
const char* name = property_getName(properties[i]);
std::cout << name << std::endl;
}
free(properties);
}
return 0;
}
I will take some snippets of the output:
hash
superclass
description
debugDescription
hash
superclass
description
debugDescription
vertexID
sha224
NS_isSourceOver
hash
superclass
description
debugDescription
...
From the output, we can find that properties such as hash, description, superclass, etc. will appear repeatedly several times, while some properties (such as UTF8String) do not appear in the result list.
How should I get the list of properties correctly?
I would appreciate it.
The reason you're not seeing UTF8String come up as a property is that it's not declared as a property in the main declaration of NSString, but rather in a category. On macOS 12.2.1/Xcode 13.2.1, the declaration of NSString boils down to this:
#interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
#property (readonly) NSUInteger length;
- (unichar)characterAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
#end
All other properties and methods on NSString are declared in categories immediately afterwards:
#interface NSString (NSStringExtensionMethods)
#pragma mark *** Substrings ***
/* To avoid breaking up character sequences such as Emoji, you can do:
[str substringFromIndex:[str rangeOfComposedCharacterSequenceAtIndex:index].location]
[str substringToIndex:NSMaxRange([str rangeOfComposedCharacterSequenceAtIndex:index])]
[str substringWithRange:[str rangeOfComposedCharacterSequencesForRange:range]
*/
- (NSString *)substringFromIndex:(NSUInteger)from;
- (NSString *)substringToIndex:(NSUInteger)to;
// ...
#property (nullable, readonly) const char *UTF8String NS_RETURNS_INNER_POINTER; // Convenience to return null-terminated UTF8 representation
// ...
#end
When a property is declared in a category on a type like this, it doesn't get emitted as an actual Obj-C property because categories can only add methods to classes, and not instance variables. When a category declares a property on a type, it must be backed by a method and not a traditional property.
You can see this with a custom class, too — on my machine,
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface MyClass: NSObject
#property (nullable, readonly) const char *direct_UTF8String NS_RETURNS_INNER_POINTER;
#end
#interface MyClass (Extensions)
#property (nullable, readonly) const char *category_UTF8String NS_RETURNS_INNER_POINTER;
#end
#implementation MyClass
- (const char *)direct_UTF8String {
return "Hello, world!";
}
- (const char *)category_UTF8String {
return "Hi there!";
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
Class clazz = NSClassFromString(#"MyClass");
printf("%s properties:\n", class_getName(clazz));
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
printf("%s\n", property_getName(properties[i]));
}
free(properties);
puts("-----------------------------------------------");
printf("%s methods:\n", class_getName(clazz));
Method *methods = class_copyMethodList(clazz, &count);
for (uint32_t i = 0; i < count; i++) {
SEL name = method_getName(methods[i]);
printf("%s\n", sel_getName(name));
}
free(methods);
}
return 0;
}
outputs
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
category_UTF8String
If you remove the actual implementations of the *UTF8String methods from the class, the property remains declared, but the category method disappears (because it doesn't actually have a synthesized implementation because of how categories work):
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
As for how to adjust to this: it depends on what purpose you're trying to fetch properties for, and why you might need UTF8String specifically.
NSString declares in its interface it implements methods, but it does not actually implement them, that is why when you print at runtime a list of the its methods it does not print what you expect.
The methods are implemented by other private classes, and when you initialize a new instance of NSString, instead of getting an instance of NSString you get an instance of that private class that have the actual implementation.
You can see that by printing the class type of a string, the following prints NSCFString or NSTaggedPointerString, not NSString:
NSString* aString = [NSString stringWithFormat: #"something"];
NSLog(#"%#", [aString class]);
And this prints __NSCFConstantString:
NSLog(#"%#", [#"a constant string" class]);
This pattern is called a class cluster pattern.
If you modify to dump the methods of the NSCFString you will get a "redactedDescription", it seems you are prevented to query these classes.

Why does my Objective-C dot accessor behaves differently with an implicit getter?

In the following code, I get '(null)' for the second line in the output but not the fourth.
MyClass.h
#interface MyClass : NSObject
#property (readonly) NSString *foo;
#property (getter=getBar, readonly) NSString *bar;
#end
main.m
#implementation MyClass
- (NSString *)getFoo { return #"foo"; }
- (NSString *)getBar { return #"bar"; }
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyClass *myClassInstance = [MyClass new];
NSLog(#"%#", myClassInstance.getFoo);
NSLog(#"%#", myClassInstance.foo);
NSLog(#"%#", myClassInstance.getBar);
NSLog(#"%#", myClassInstance.bar);
}
return 0;
output
foo
(null)
bar
bar
Why am I seeing this?
Remember that Objective C getters are just the name of the property; foo in the foo case. In this, there's no relationship between getFoo and foo, so you access the underlying property via its normal getter. It's never been set, so it's nil, which logs as null.
In the later case, you establish the getter for bar as getBar. Thus, accessing bar the property evaluates the getter function you specified.

Property doesn't match type of instance variable?

I took some sample code from Apple's SimpleFTPSample
Which looks like this:
#interface PutController () <UITextFieldDelegate, NSStreamDelegate>
...
#property (nonatomic, assign, readonly) uint8_t *buffer;
...
#end
#implementation PutController
{
uint8_t _buffer[kSendBufferSize];
}
...
#end
But when I copy it to my code I get an error:
Type of property 'buffer' ('uint8_t *' (aka 'unsigned char *')) does not match type of instance variable '_buffer' ('unsigned char [32768]')
My code is exactly the same as their sample, and yet it won't compile. What's going on here?
Unfortunately, while arrays can decay to pointers, with clang, at least, it doesn't auto-decay into a readonly pointer when used in a property.
The simplest way to fix this is by having a second instance variable which is simply a pointer to the first element into the array, and have your property synthesize to that, like this:
#interface MyObj : NSObject
#property (readonly) uint8_t *buffer;
#end
#implementation MyObj {
uint8_t *_bufferPtr;
uint8_t _buffer[1024];
}
#synthesize buffer = _bufferPtr;
-(id) init {
if (self = [super init]) {
_bufferPtr = &_buffer[0];
}
return self;
}
#end
Alternativey, simply implement your own getter implementation to simply return the pointer to the first element of buffer. It's your call, really.
It isn't optimal, but it does appear to work the way you want.

Objective-C and NSCoding primative data type just like the old C code

I have some old C code that looks like this:
#include <stdio.h>
#include <strings.h>
main()
{
FILE *output;
struct socket_cpacket
{
char type; /* CP_SOCKET */
char version;
char udp_version; /* was pad2 */
char pad3;
unsigned socket;
};
struct socket_cpacket sockPack;
bzero(&sockPack,sizeof(sockPack));
sockPack.type = 27;
sockPack.version = 4;
sockPack.udp_version = 10;
sockPack.pad3 = 0;
sockPack.socket = 0;
output = fopen("/tmp/sockPack.bin", "wb");
fwrite(&sockPack, sizeof(sockPack), 1, output);
}
I'd like to duplicate this functionality in obj-c and I started down the path of using NSCoding protocol.
CP_Socket.h
#import <Foundation/Foundation.h>
#interface CP_Socket : NSObject <NSCoding>
{
#private
char type;
char version;
char udp_version;
char pad3;
unsigned int socket;
}
#property (readonly) char type;
#property (readonly) char version;
#property (readonly) char udp_version;
#property (readonly) char pad3;
#property unsigned int socket;
typedef enum {
mTYPE = 27,
mVERSION = 4,
mUDP_VERSION = 10,
} cpSocketEnum;
#end
And CP_Socket.m
#import "CP_Socket.h"
#implementation CP_Socket
#pragma mark ======== properties =========
#synthesize type;
#synthesize version;
#synthesize udp_version;
#synthesize pad3;
#synthesize socket;
- (id)init {
NSLog(#"init");
if( !( self = [super init] ) )
return nil;
type = mTYPE;
version = mVERSION;
udp_version = mUDP_VERSION;
pad3 = 0;
socket = 0;
return self;
}
#pragma mark ======== Archiving and unarchiving methods =========
//
// Archives and Serializations Programming Guide for Cocoa
// http://bit.ly/PAaRsV
//
// NSCoding Protocol Reference
// http://bit.ly/PAb1Rd
//
- (void)encodeWithCoder:(NSCoder *)coder
{
NSLog(#"encodeWithCoder");
[coder encodeBytes:[self type] length:1 forKey:#"type"];
//[coder encodeBytes:[self version] length:1 forKey:#"version"];
//[coder encodeBytes:[self udp_version] length:1 forKey:#"udp_version"];
//[coder encodeBytes:[self pad3] length:1 forKey:#"pad3"];
//[coder encodeInt:[self socket] forKey:#"socket"];
}
- (id)initWithCoder:(NSCoder *)coder
{
NSLog(#"initWithCoder");
}
#end
First problem, [coder encodeBytes:[self type] length:1 forKey:#"type"]; throws a warning. Incompatible integer to pointer conversion sending 'char' to parameter of type 'const uint8_t *.
How do I encode a char?
I tried [coder encodeInt:[self type] forKey:#"type"]; but char != int.
Going with the code to further understand how it work; the file the obj-c code generates is 280 bytes and looking inside the file I see what looks like name-mangled class identifiers.
I've tried NSKeyedArchiver and NSArchiver with the same results.
I don't know where to go from here. The C-code generates a 8 byte file. I'd like the obj-c code to do the same while using some of the OO stuff like the NSCoding protocol.
I feel like I'm going to have to extend the NSCoder object to make this work.
Any help would be appreciated.
The first argument to encodeBytes:length:forKey: is expected to be a pointer to the buffer you want to encode, so take the address of your ivar:
[coder encodeBytes:&type length:1 forKey:#"type"];
Or make a temp variable, put the result of the property access in that, and take its address.
Using encodeInt:forKey: should work too (with a cast), but it'll inflate the size of your data file.
If you really wanted to, you could certainly extend NSCoder with a category:
#implementation NSCoder (BTEncodeChar)
- (void)BTencodeChar: (char)c forKey: (NSString *)key
{
[self encodeBytes:&c length:1 forKey:key];
}
#end
I dont know much about NSCoding, but obj C interops with C just fine. Take your existing code put it in a function with params and call it.

How to avoid temp objects when returning NSString under ARC

I've got a class with two properties:
#interface Contact : NSObject {
NSString *lastname;
NSString *lastNameUpper;
}
I've declared lastname as a property (and synthesize it in the .m-file):
#property (nonatomic, retain) NSString *lastname;
However, I want to write my own method to access the lastNameUpper, so I declared a method:
- (NSString *) lastNameUpper;
and implemented it like this:
- (NSString *) lastNameUpper {
if (!lastNameUpper) {
lastNameUpper = [lastname uppercaseString];
}
return lastNameUpper;
}
This works all right, but as this is called quite often, a lot of temporary objects are called. Interestingly, the Instruments show a lot of "Malloc (4k)", and the number increase each time lastNameUpper is accessed. I can also see that the memory is allocated in objc_retailAutoreleaseReturnValue.
As this was working fine before I converted my project to ARC, I'm assuming that I have to make some ARC specific additions to the method signature, but I can't seem to be able to make it work.
Any suggestions?
0: you should copy your NSString properties:
#property (nonatomic, copy) NSString * lastname;
I'm guessing that returning the string is implemented by copying it.
nope. copy of an immutable string is a retain operation. just run it in the profiler to see how much this costs in time and memory. also, there's no implicit copy in this case.
Update
I tested this on Lion-64. uppercaseString may return a mutable string.
To be safe, you may consider assigning a copy of the result of uppercaseString: lastNameUpper = [[lastname uppercaseString] copy];. that may result in more or less allocations, depending on how you used the string in your implementation. if your properties copy, then a copy will be made each time you assign it. the easy generalization is to assign a copy, and the rest usually takes care of itself.
Test Program
// ARC enabled
#import <Foundation/Foundation.h>
#interface Contact : NSObject
{
NSString * lastname;
NSString * lastNameUpper;
}
#property (nonatomic, copy) NSString *lastname;
#end
#implementation Contact
#synthesize lastname;
- (NSString *) lastNameUpper {
if (!lastNameUpper) {
lastNameUpper = [lastname uppercaseString];
}
return lastNameUpper;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
int n = 0;
while (n++ < 100000) {
Contact * c = [Contact new];
c.lastname = #"skjdhskjdhaksjhadi";
NSString * lastNameUpper = c.lastNameUpper;
}
}
return 0;
}
Override the - (void)setLastname:(NSString*)aLastname method (created automatically by #synthesize lastname, and set lastNameUpper as in the existing method.
Now create a lastNameUpper property (and synthesize it):
#property (nonatomic, readonly) NSString *lastNameUpper;
Since this will return the pointer of the lastNameUpper instance variable, no copies should be made whenever this is accessed.