What the output should be there? - objective-c

The output should be
strString = değiştim
wkString = NULL
but it is not. WHY?
#import <Foundation/Foundation.h>
#interface learnARC : NSObject {
NSString *strString, __weak *wkString;
}
#property (strong) NSString *strString;
#property (weak) NSString *wkString;
-(void) yaz;
#end
#import "learnARC.h"
#implementation learnARC
#synthesize wkString, strString;
-(void) yaz {
NSString *anaString = #"anaString";
strString = anaString;
wkString = anaString;
NSLog(#"\nstrString = %#\nwkString = %#",strString,wkString);
anaString = #"değiştim";
NSLog(#"\nstrString = %#\nwkString = %#",strString,wkString);
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
learnARC *lrnarc = [[learnARC alloc]init];
[lrnarc yaz];
}
return 0;
}

WHY?
Because you're captalizing your question instead of your class names...
Seriously, the weak reference should not be NULL. You have assigned a pointer to it (a pointer to the string #"anaString"). And since string literals have static storage duration, they are never deallocated during the lifetime of the program. (I think you may be confusing variables with properties?)

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.

Access annotated property in Objective-C

I’ve searched around for this but found no information at all. Say I create a property like so:
#property (nonatomic, strong, readwrite) NSString *someString __attribute((annotate(“Try to access this text?)));
How could I access the text in the annotation part of the property? I’m guessing I’d have to utilize Objective-c runtime but I found no information there either.
First off, sales pitch - swift would make this easily transparent with optionals. Since you are using objective-c here's how I would do it:
#interface SomeModel {
static NSString * const _requiredProperties[] = {
[0] = #"first",
[1] = #"second"
};
}
#property(nonatomic, strong) NSString *first;
#property(nonatomic, strong) NSString *second;
#end
-(BOOL) doLoad:(...) {
//map properties here
Bool success = YES;
foreach(NSString property in _requiredProperties {
success &= ([self valueForKey: property] != nil);
}
return success;
}
The objective-C language doesn't contain the types of constructs you need. Keep it simple, handle missing properties elegantly.
Also if it's JSON, there's plenty of frameworks available to help. Unsure of your mapping here.
Alternatively you can query the protocol, and use the fact it is in a protocol to determine if required.
See this hello world like example that prints out the protocols in the required protocol - sadly the third param which is "YES" for required seems to include optionals for me on OSX. So I made a separate protocol for optionals. This might help you.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#protocol TestProtocolRequired
#required
#property(nonatomic, strong) NSString *firstName;
#property(nonatomic, strong) NSString *lastName;
#end
#protocol TestProtocolExtended
#property(nonatomic, strong) NSString *address;
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
unsigned int outCount, i;
objc_property_t *properties = protocol_copyPropertyList2(#protocol(TestProtocolRequired), &outCount, YES, YES);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
}
return 0;
}
It prints out
firstName T#"NSString",&,N
lastName T#"NSString",&,N

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.

What's wrong with my Objective-C class?

I am having trouble with my Objective-C code. I am trying to print out all of the details of my object created from my "Person" class, but the first and last names are not coming through in the NSLog method. They are replaced by spaces.
Person.h: http://pastebin.com/mzWurkUL
Person.m: http://pastebin.com/JNSi39aw
This is my main source file:
#import <Foundation/Foundation.h>
#import "Person.h"
int main (int argc, const char * argv[])
{
Person *bobby = [[Person alloc] init];
[bobby setFirstName:#"Bobby"];
[bobby setLastName:#"Flay"];
[bobby setAge:34];
[bobby setWeight:169];
NSLog(#"%s %s is %d years old and weighs %d pounds.",
[bobby first_name],
[bobby last_name],
[bobby age],
[bobby weight]);
return 0;
}
%s is for C style strings (a sequence of chars terminated by a null).
Use %# for NSString objects. In general, %# will invoke the description instance method of any Objective C object. In the case of NSString, this is the string itself.
See String Format Specifiers.
On an unrelated note, you should look into Declared Properties and #synthesize for your class implementation. It will save you a lot of typing as it produces all the getters and setters for you:
person.h:
#import <Cocoa/Cocoa.h>
#interface Person : NSObject
#property (nonatomic, copy) NSString *first_name, *last_name;
#property (nonatomic, strong) NSNumber *age, *weight;
#end
person.m
#import "Person.h"
#implementation Person
#synthesize first_name = _first_name, last_name = _last_name;
#synthesize age = _age, weight = _weight;
#end
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
int main (int argc, const char * argv[])
{
Person *bobby = [[Person alloc] init];
bobby.first_name = #"Bobby";
bobby.last_name = #"Flay";
bobby.age = [NSNumber numberWithInt:34]; // older Objective C compilers.
// New-ish llvm feature, see http://clang.llvm.org/docs/ObjectiveCLiterals.html
// bobby.age = #34;
bobby.weight = [NSNumber numberWithInt:164];
NSLog(#"%# %# is %# years old and weighs %# pounds.",
bobby.first_name, bobby.last_name,
bobby.age, bobby.weight);
return 0;
}

Request for member 'pData' with BOOL value TRUE is not a structure or union-Objective C

I could not use the pData[4096] to pass it to the other function from main.
data.m
------
#implementation data
static int msgID;
static char pData[4096]="\0";
+ (void)initialize
{
//some initialisations
msgID =123;
}
-(void)SwapEndian:(uint8_t*)pData withBOOLValue:(BOOL)bIsAlreadyLittleEndian
{
NSLog("%s %s",pData,bIsAlreadyLittleEndian);
}
#end
main.m
-------
[dat SwapEndian:dat.pData withBOOLValue:TRUE];
I am getting pData undeclared. As pData is declared as static inside the Data
implementation i tried with dat.pData to pass it from main.But when i do it i am getting
Request for member 'pData' with BOOL value TRUE is not a structure or union.
It is difficult to determine what the code is supposed to do, but here is how to create an Objective-C object that holds an integer identifier and a 4096-character array. Please note that this sort of thing is usually discouraged. Unless you have a really specific reason for using int and char[], the identifier should be NSInteger and the data should be an NSData or NSString object.
I have also used some of the "standard" naming conventions. If you are writing Cocoa code, it helps to drink a lot of the Kool-Aid.
Message.h:
#interface Message : NSObject
{
int identifier;
char data[4096];
}
#property (nonatomic, assign) int indentifier;
#property (nonatomic, readonly) char * data;
- (void)swapEndian:(BOOL)flag;
#end
Message.m:
#implementation Message
#synthesize identifier;
#synthesize data;
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
identifier = 0;
data[0] = '\0';
return self;
}
- (void)swapEndian:(BOOL)flag
{
NSLog(#"%s %d", data, flag);
}
#end
main.m:
#import "Message.h"
...
Message * message = [[[Message alloc] init] autorelease];
message.identifier = 123;
[message swapEndian:YES];