I initialize a variable (userMarkerLayer) in viewDidLoad. When I use it in that method access is ok. but I set a button and I want to access to it when that button tapped.
#implementation MapViewController
{
NTVectorElementLayer *userMarkerLayer;
}
(void)viewDidLoad {
[super viewDidLoad];
userMarkerLayer = [NTNeshanServices createVectorElementLayer];
[userMarkerLayer addMarker: marker]; // This is ok
}
-(IBAction)showList:(id)sender {
[userMarkerLayer addMarker: marker];
// error:
// Thread 1: EXC_BAD_ACCESS (code=1, address=0x150543c30)
}
cannot access the userMarkerLayer!
When I enable ARC, this problem not happens. but I have to set it no.
Since you're using MRC (ARC disabled), please, read Memory Management Policy.
In your code, you have an ivar (instance variable) ...
#implementation MapViewController {
NTVectorElementLayer *userMarkerLayer;
}
... and you're assigning an object to it ...
userMarkerLayer = [NTNeshanServices createVectorElementLayer];
... but the problem here is that you do not own this object (no alloc, new, copy or mutableCopy in the createVectorElementLayer method name).
It means that you're storing a reference to this object, but this object can go away anytime. You have to use retain to take an ownership of this object.
To fix this, take an ownership ...
userMarkerLayer = [[NTNeshanServices createVectorElementLayer] retain];
... and add dealloc method ...
- (void)dealloc {
[userMarkerLayer release];
[super dealloc];
}
Or declare it as a property with retain ...
#interface MapViewController ()
#property (nonatomic, retain) NTVectorElementLayer *userMarkerLayer;
#end
... and use self.userMarkerLayer instead of just userMarkerLayer ...
self.userMarkerLayer = [NTNeshanServices createVectorElementLayer];
Related
Who can tell me why the following code causes different results when the test parameter and the testMember variable point to the same address
#interface TestClass : NSObject
#property (nonatomic, copy) void (^testBlock)(TestClass *test);
#end
#implementation TestClass
- (void)viewDidLoad {
[super viewDidLoad];
self.testBlock(self);
}
#end
#implementation OtherClass
- (void)viewDidLoad {
[super viewDidLoad];
TestClass *testMember = [[TestClass alloc] init];
// Case 1
testMember.testBlock = ^(TestClass *test) {
NSLog(#"%#",test); // This does not create circular references
};
// Case 2
testMember.testBlock = ^(TestClass *test) {
NSLog(#"%#",testMember); // This creates circular references
};
}
#end
For the case 1, the test variable is just a local function parameter, the block didn't retain any other variables from outside.
For the case 2, the testMember object comes from block outside when defining the block, the block retains its reference count. Since the testBlock is defined as a property of class TestClass, so the testMember owns the testBlock. So, the testMember and testBlock retain each other, that's the circular references.
To fix the circular retain issue of case 2, use this:
__weak TestObject *weakMember = testMember;
testMember.testBlock = ^(TestClass *test) {
__strong TestObject *strongMember = weakMember;
NSLog(#"%#", strongMember);
};
Because the weakMember variable won't increase the reference count of real testMember object, so did the testBlock. When starting executing the testBlock, the strongMember try to retain the weakMember object (which may get nil value depends on your logic design) and get decreased of reference count when finishing the block.
I have some abbreviated iOS Objective-C sample code (simplified from a larger project) that causes a crash in NSUndoManager that I can't explain.
Namely, when an object that is only held onto by the NSUndoManager deallocs (because it's beyond the levels of undo), and, according to the docs calls removeAllActionsWithTarget:self, I get an EXC_BAD_ACCESS.
// SimpleViewController.m
#interface ViewController ()
#property (nonatomic, strong) NSUndoManager *undoManager;
#end
#implementation ViewController
#synthesize undoManager;
// called from a simple button
- (IBAction)doItTapped:(id)sender
{
CoolObject *object = [CoolObject new];
object.undoManager = self.undoManager;
// according to docs, object will be retained by NSUndoManager here
// but target will not (which should be okay)
[self.undoManager registerUndoWithTarget:self selector:#selector(notCool:) object:object];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.undoManager = [NSUndoManager new];
self.undoManager.levelsOfUndo = 3;
}
and
// CoolObject.m
#implementation CoolObject
- (void)dealloc
{
[self.undoManager removeAllActionsWithTarget:self];
}
#end
After the 4th tap of the button (levelsOfUndo + 1), it crashes.
If I swap NSUndoManager with GCUndoManager, no crash.
Tested in iOS 10.2 sim and devices.
Thanks for any ideas!
Their are chances that you might be getting this error because self.undoManager is not retained at that point where you are using it. When the object is already deallocated and you try to access it, you will get bad access exception.
Try to change your code from this:
CoolObject *object = [CoolObject new];
to this:
#interface ViewController (){
CoolObject *object;
}
#property (nonatomic, strong) NSUndoManager *undoManager;
#end
#implementation ViewController
- (IBAction)doItTapped:(id)sender
{
object = [CoolObject new];
object.undoManager = self.undoManager;
// according to docs, object will be retained by NSUndoManager here
// but target will not (which should be okay)
[self.undoManager registerUndoWithTarget:self selector:#selector(notCool:) object:object];
}
#end
Hope this will help.
Just like me, you seem to have misinterpreted the admittedly inaccurately written documentation. The docs talk about "target", "object" and "target object" as if they were different things when they really mean exactly one and the same: the (id)target parameter of -removeAllActionsWithTarget:
In other words, in my opinion you should not need to call -removeAllActionsWithTarget: inside of CoolObject at all because CoolObject has been specified as the object of -registerUndoWithTarget:selector:object: whereas the target is your ViewController.
You may have to call -removeAllActionsWithTarget: in your NSViewController's -dealloc but even that is unnecessary in your example because your NSViewController owns the NSUndoManager and thus ViewController won't go away before undoManager does.
I have a problem understanding the Objective-C and the ARC.
As I understood the strong pointers will be dealloced automatically for you, so you don't have to think about it (dealloced in dealloc method, or after the last time the object was used ?).
So I wrote a little app, with 2 viewControllers and a NavigationController, which enters one view and then goes back.
The dealloc method was called, but the property, which I set at viewDidLoad method, wasn't deallocated, it is still pointing to some object.
Code:
The first viewController has a button, which by pressing it, performs a segue to another viewController. No code there.
SecondViewController.m
#interface SecondViewController ()
#property (nonatomic, strong) NSString *myString;
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", _myString);
_myString = #"it works";
}
- (void)dealloc {
NSLog(#"%#", _myString);
// and now it is deallocating the _myString property ???
}
#end
Then, I tried to do another thing.
The idea was to create a weak pointer, which points to the same memory address, as the strong pointer. I though, that the weak pointer should be nil in any case.
Since the dealloc method is called, all weak pointers should be niled
Since the strong pointer was used only in viewDidLoad, it should be deallocated way before the dealloc method.
The problem is, it is not deallocated.
Why ?
Code for secondViewController:
#interface SecondViewController ()
#property (nonatomic, strong) NSString *myString;
#property (nonatomic, weak) NSString *test;
#end
#implementation SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", _myString);
_myString = #"it works";
_test = _myString;
}
- (void)dealloc
{
NSLog(#"%#", _test);
}
#end
Deallocation of the properties happens at the end of the dealloc method. If you overwrite the dealloc method, the properties won't yet be deallocated inside that method.
You could test this by creating a weak property in your first view controller, assign the strong property of the second view controller, then log the value of it when the application returns to the first view controller.
The simplest way to illustrate weak references is with the following example...
Given the following two classes:
#interface ObjectWithStrongRef : NSObject
#property (strong) NSString *ref;
#end
#interface ObjectWithWeakRef : NSObject
#property (weak) NSString *ref;
#end
We will create an instance of ObjectWithWeakRef with a scope larger than that of ObjectWithStrongRef, assign the latter's ref property a value, then have the former's ref point to this same object, then we will check ref in both scopes.
int main(int argc, const char * argv[]) {
ObjectWithWeakRef *weak = [[ObjectWithWeakRef alloc] init];
#autoreleasepool {
ObjectWithStrongRef *strong = [[ObjectWithStrongRef alloc] init];
strong.ref = [NSString stringWithFormat:#"Hello %#", #"World"];
weak.ref = strong.ref;
NSLog(#"Weak.Ref = %#", weak.ref);
}
NSLog(#"Weak.Ref = %#", weak.ref);
}
Note, that we can't simply assign ref to a literal string. Objective-C tends to keep these around in memory so it can do some memory optimizations, but when we use stringWithFormat:, it'll create an autoreleasing string.
At the first NSLog statement, strong.ref maintains a strong reference to the string object, so when we log weak.ref, the object is not yet deallocated, so it correctly logs "Hello World".
Between the first and second NSLog call, we've exited the #autoreleasepool, within which the strong object was scoped (if we put an NSLog message in ObjectWithStrongRef's dealloc, we'd see it called here). Because strong has deallocated as we exit the #autoreleasepool, there are no longer any strong references to the string object we have references to--we only have weak's weak reference to the memory, so the string object also deallocates (just after strong has deallocated).
So in the second NSLog call, we'll see Weak.Ref = (null) printed.
Apple's Developer Reference mentions that a an object is deallocated if there are no strong reference to it. Can this happen if an instance method called from a weak reference is in the middle of execution?
For example, consider the below snippet -
#interface ExampleObject
- doSomething;
#end
#interface StrongCaller
#property ExampleObject *strong;
#end
#implementation StrongCaller
- initWithExampleInstance:(ExampleObject *) example
{
_strong = example;
}
- doSomething
{
....
[strong doSomething];
....
strong = nil;
....
}
#end
#interface WeakCaller
#property (weak) ExampleObject *weak;
#end
#implementation WeakCaller
- initWithExampleInstance:(ExampleObject *) example
{
_weak = example;
}
- doSomething
{
....
[weak doSomething];
....
}
#end
Now, In main thread,
ExampleObject *object = [[ExampleObject alloc] init];
In Thread 1,
[[StrongCaller initWithExampleInstance:object] doSomething];
In Thread2,
[[WeakCaller initWithExampleInstance:object] doSomething];
Assuming that the main thread no longer holds a reference to object, what would happen if strong is set to nil, when [weak doSomething] is executing? Is the object GC'ed in this case?
Normally this problem happens during asynchronously blocks execution where there is impossible to avoid this problem by changing logic.
But if you are sure that you do not want to change logic you can use the same solution in your case. You should modify your method this way
- (void) doSomething
{
Your_Class *pointer = self; //Now this local variable keeps strong reference to self
if(pointer != nil){ // self is not deallocated
... your code here
}
//Here pointer will be deleted and strong reference will be released automatically
}
I am developing an ARC enabled project. From a view controller I am pushing MyClass,
- (void)pushMyClass {
MyClass *myClass = [[MyClass alloc] init];
[self.navigationController pushViewController:myClass animated:YES];
}
After doing some operations I am popping MyClass. The problem here is that MyClass is not getting deallocated. Following is how the classes look.
/* MyHelperClassDelegate */
#protocol MyHelperClassDelegate <NSObject>
- (void)helperDidFinishHelping:(MyHelperClass *)helper;
#end
/* MyHelperClass Interface */
#interface MyHelperClass : NSObject {
__weak id <MyHelperDelegate> delegate;
}
#property(nonatomic, weak) id<MyHelperDelegate> delegate;
- (void)startHelping;
#end
/* MyHelperClass Implementation */
#implementation MyHelperClass
#synthesize delegate;
- (void)dealloc {
delegate = nil;
}
/* MyClass */
#interface MyClass : UIViewController <MyHelperClassDelegate> {
MyHelperClass *helper;
}
#implementation MyClass {
- (void)dealloc {
helper.delegate = nil;
}
- (void)getHelp {
helper = [MyHelperClass new];
helper.delegate = self;
[helper startHelping];
}
- (void)helperDidFinishHelping:(MyHelperClass *)helper {
}
}
MyHelperClass calls a web service using NSMutalbleURLRequest & NSURLConnection to fetch some data and saves it to user defaults.
One thing to notice here is, if I comment the line helper.delegate = self;, then MyClass gets deallocated.
What to do to make MyClass get deallocated when it is popped out of navigation controller?
Thanks.
Your delegate code looks correct (except your use of an ivar, you don't show a #synthesize so you may have _delegate and delegate both). Its quite likely that something else is retaining MyClass. What I suggest you do is add a NSLog to your MyClass dealloc. Then push it, and immediately hit the back button and see if its dealloc'd or not. If not, then take a hard look at what you do in viewDidLoad et al and start commenting out sections of that code until you can get the dealloc.
Also, I assume you don't keep a strong reference in the class that pushes the MyClass object.
I agree with Chuck that one cannot say much from the code provided. But one reason why the MyClass object is not deallocated might be that it is retained by your helper object since delegate is declared as strong, and the MyClass object has the property helper also declared as strong. In this case you had a retain cycle, and none of them can be released.
The trick could possibly lie within the fact that you use NSURLConnection. It is not specified how you use this class with the code that you've provided, but please note the special considerations referenced in the NSURLConnection class reference:
Special Considerations: During the download the connection maintains a
strong reference to the delegate. It releases that strong reference
when the connection finishes loading, fails, or is canceled.