How does the Xcode breakpoint system interact with memory management? - objective-c

Here's a little experiment:
#interface Model : NSObject
#property (copy) NSString *value;
-(instancetype)initWith:(NSString *)value;
#end
#implementation Model
-(instancetype)initWith:(NSString *)value {
self = [super init];
self.value = value;
return self;
}
#end
#import <Foundation/Foundation.h>
#import "Model.h"
void experiment(Model *m);
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
Model *ma = [[Model alloc] initWith:[[NSUUID UUID] UUIDString]];
experiment(ma);
NSLog(#"yyy %#", [ma value]);
}
return 0;
}
void experiment(ModelA *m) {
NSString *testValue = nil;
testValue = [m value];
NSLog(#"xxx %#", testValue);
}
When run, this produces the following:
Hello, World!
xxx 6005A7B0-F71C-4755-B1BF-792D6296B716
yyy 6005A7B0-F71C-4755-B1BF-792D6296B716
Program ended with exit code: 0
But suppose I make this line:
testValue = [m value];
part of a breakpoint:
And this changes everything:
Hello, World!
(__NSCFString *) $0 = 0x000000010071e220 #"1C0DCB39-BFBB-4E67-A041-E6B58615BDFD"
xxx 1C0DCB39-BFBB-4E67-A041-E6B58615BDFD
yyy 1C0DCB39-BFBB-4E67-A041-E6B58615BDFD
*** -[CFString release]: message sent to deallocated instance 0x10071e220
And a crash. I see what's happening--the string is released once we exit the function scope, and the second time when the Model object is destroyed, which is an overrelease. But why doesn't the breakpoint (or more precisely, the lldb expression inside the breakpoint) handle the reference count correctly?

Related

With ARC , why the dealloc not called

I have enable the ARC. but this code makes me wonder
#interface Dog : NSObject
#end
#implementation Dog
- (void)dealloc
{
printf("Dog is dealloc\n"); //the function not called
}
#end
#interface Person : NSObject
#property (nonatomic, strong) Dog *dog;
#end
#implementation Person
- (void)dealloc
{
printf("Person is dealloc\n");
_dog = nil;
}
-(Dog *)dog
{
return _dog;
}
#end
int main()
{
Person *p = [[Person alloc] init];
p.dog = [[Dog alloc]init];
Dog* d = p.dog;
d=nil;
p=nil;
printf("end\n");
return 0;
}
the result is
Person is dealloc
end
Program ended with exit code: 0
why the dog's dealloc method not called.
and then I commented out this Method, the dog's dealloc method called.
//-(Dog *)dog
//{
// return _dog;
//}
thank you very much.
You can see the memory graph to find out what exactly points to the Dog and preserve it from automatically deallocation:
Unlike Swift, In Objective-C, you need to put the main body inside an #autoreleasepool to make it ARC compatible:
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
Person *p = [[Person alloc] init];
p.dog = [[Dog alloc]init];
Dog* d = p.dog;
d = nil;
p = nil;
printf("Ended...\n");
}
return 0;
}
Then you will see this in the output:
Person is dealloc
Ended...
Dog is dealloc

Why weak property of associated object is not nilled out if I call its getter?

Though it's kind of stupid in 2020 that I'm still asking question about ObjC, please be patient and considerate...
I'm reading the source code of BloksKit and ran into a weird situation.
#import <objc/runtime.h>
#interface _WeakAssociatedObjectWrapper : NSObject
#property (nonatomic, weak) id object;
#end
#implementation _WeakAssociatedObjectWrapper
#end
#interface NSObject (AddWeak)
#end
#implementation NSObject (AddWeak)
- (void)setWeakProp:(id)weakProp {
_WeakAssociatedObjectWrapper *wrapper = objc_getAssociatedObject(self, #selector(weakProp));
if (!wrapper) {
wrapper = [[_WeakAssociatedObjectWrapper alloc] init];
objc_setAssociatedObject(self, #selector(weakProp), wrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
wrapper.object = weakProp;
}
- (id)weakProp {
id value = objc_getAssociatedObject(self, _cmd);
if ([value isKindOfClass:_WeakAssociatedObjectWrapper.class]) {
return [(_WeakAssociatedObjectWrapper *)value object];
}
return value;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
{
NSObject *prop = [[NSObject alloc] init];
[obj setWeakProp:prop];
[obj weakProp]; // *Weird!!
}
NSLog(#"Now obj.weakProp = %#", [obj weakProp]);
}
return 0;
}
This code is adding a weak associated object for category.(BlocksKit does so)
Note the *Weird!! line. If this line is commented out, then it prints (null), which is reasonable since prop is deallocated outside the {} scope. On the other side, if not commented out, it prints <NSObject: 0xxxxx>, which indicates that prop is somehow retained by someone(Or any other reason?).
What is happening here??! (BlocksKit behaves the same!)
Environment: XCode 10.3
This is a feature. For the case (and any similar)
[obj weakProp];
by properties/accessors naming convention ARC returns autoreleased instance, so in your case #autoreleasepool holds it and testing as below can show this.
int main(int argc, const char * argv[]) {
NSObject *obj = [[NSObject alloc] init];
#autoreleasepool {
{
NSObject *prop = [[NSObject alloc] init];
[obj setWeakProp:prop];
[obj weakProp]; // *Weird!!
}
NSLog(#"Now obj.weakProp = %#", [obj weakProp]);
}
NSLog(#"After autoreleased >> obj.weakProp = %#", [obj weakProp]);
return 0;
}

"Missing context for method declaration" for overridden description method

Am getting a "Missing context for method declaration" for my overridden description method. Can you tell what's wrong with the code?
#import <Foundation/Foundation.h>
#import "BNRItem.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
// Create a mutable array object, store its address in items variable
NSMutableArray *items = [[NSMutableArray alloc]init];
BNRItem *p = [[BNRItem alloc]init];
NSLog(#"%# %# %# %d", [p itemName], [p dateCreated], [p serialNumber], [p valueInDollars]);
// This creates a new NSString, "Red Sofa" and gives it to the BNRItem
[p setItemName:#"Red Sofa"];
// This creates a new NSString, "A1B2C" and gives it to the BNRItem
[p setSerialNumber:#"A1B2C"];
// We send the value 100 to be used as the valueInDollars of this BNRItem
[p setValueInDollars:100];
// Destroy the array pointed to by items
items = nil;
}
return 0;
}
-(NSString *)description // Missing context for method declaration
{
NSString *descriptionString =
[[NSString alloc]initWithFormat:#"%# (%#): Worth $%d, recorded on %#",
itemName;
serialNumber;
valueInDollars;
dateCreated];
return descriptionString;
}
BNRItem.m
#import "BNRItem.h"
#implementation BNRItem
-(void)setItemName:(NSString *)str {
itemName = str;
}
-(NSString *)itemName {
return itemName;
}
-(void)setSerialNumber:(NSString *)str {
serialNumber = str;
}
-(NSString *)serialNumber {
return serialNumber;
}
-(void)setValueInDollars:(int)i {
valueInDollars = i;
}
-(int)valueInDollars {
return valueInDollars;
}
-(NSDate *)dateCreated {
return dateCreated;
}
-(NSString *)description
{
NSString *descriptionString =
[[NSString alloc]initWithFormat:#"%# (%#): Worth $%d, recorded on %#",
itemName,
serialNumber; // Expected "]"
valueInDollars, // Expression result unused
dateCreated]; //Extraneous "]" before ";"
return descriptionString;
}
#end
Your method appears to be free floating inside main.m. An instance method needs to be placed inside the implementation section of a class. (between #implementation and #end).
My guess is that you should move that code into BNRItem.m.
If you have something like a char at the veryfirst beginning of your Header oder .m file, its also likely that this error accures.
^//
// EMServices.m
// MyController
//
// Created by EMart on 09.01.14.
// Copyright (c) 2014 EMart. All rights reserved.
//

How to return an NSString * in Objective C (keep getting invalid summary)

So I presume this is a memory issue, here's the code:
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = #"a string";
return s;
}
And the calling code:
NSString *aString;
aString = [self giveMeAStringGoddammit];
However after this call, aString has an invalid summary when debugged and crashes when run.
I suspect I'm missing a retain or something, can someone help? Thanks.
What you've got works just fine:
#import <Foundation/Foundation.h>
#interface Test : NSObject
- (NSString *)giveMeAStringGoddammit;
#end
#implementation Test
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = #"a string";
return s;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
Test *t = [[Test alloc] init];
NSLog(#"t says: %#", [t giveMeAStringGoddammit]);
}
return 0;
}
The output of this program is:
t says: a string
To make this a little more realistic, let's change it to use a property:
#import <Foundation/Foundation.h>
#interface Test : NSObject
#property(copy, nonatomic) NSString *string;
- (NSString *)giveMeAStringGoddammit;
#end
#implementation Test
#synthesize string;
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = self.string;
return s;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
Test *t = [[Test alloc] init];
t.string = #"Hello world!";
NSLog(#"t says: %#", [t giveMeAStringGoddammit]);
}
return 0;
}
This does what you'd expect:
t says: Hello world!
You have created a pointer object and it's expected to increment it's retain count whenever you referring them, for increasing retain count the string should be initiated and allocated with memory else you could use [NSString stringwithString:[self giveMeAStringGoddammit]. You can use this definition only when you exactly need it reference locally. because whenever you try to refer it out side the auto release pool will crash the app (hence it's not retained manually). So if you need to use it out side of the function, better use [NSString alloc]init] and then load your string to the pointer object. Well the way to make your code to work is add the lines NSString *aString = [NSString stringWithString:[self giveMeAStringGoddammit]];
NSLog(#"My Str:%#",aString); Hooray now the goddammit string was given......

I am trying to use respondsToSelector but unable to get expected output can anyone find what is wrong in my code?

#import "movie.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
movie *obj = [[movie alloc]init];
[obj findinterestofnum1:(int)200 num2:(int)4 num3:(int)5];
SEL suf = #selector(findinterestofnum1: num2:num3:);
BOOL sul = [obj respondsToSelector:suf];
if(sul)
{
NSLog(#"It is implememted");
}
else
{
NSLog(#" It is not implemented");
}
NSLog(#"Hello, World!");
[pool drain];
return 0;
}
********-----
#interface movie : NSObject {
#private
}
-(void)findinterestofnum1:(int)p num2:(int)n num3:(int)r;
#end
*******-------
#import "movie.h"
#implementation movie
-(void)findinterestofnum1:(int)p num2:(int)n num3:(int)r
{
int a ;
a= (p*n*r/100);
NSLog(#"interest value is =%d",a);
}
- (void)dealloc
{
[super dealloc];
}
#end
i am trying to find whether method is implemented and if it is not it should print it is not implemented
#toddler, respondsToSelector just checks if the corresponding object can respond to that particular method. It doesn't do any checks on whether interface has that definition associated with it since it is a runtime check.
If you had removed the code from the implementation file (.m file) and not the interface, you would have got an error while executing it and you would have found that the BOOL sul is FALSE.