I have a very simple class:
#interface WORef : NSObject
#property(nonatomic,weak) NSObject * object;
#end
Instances are stored in an NSArray and from time to time (only on the main thread) this array is iterated and I access the "object" property.
All works fine when testing or debugging, but in the production version of my app on the store I sometimes get crash reports when dereferencing the "object" property (the stack trace actually shows the line number of the property definition).
Here is an example of such a call stack:
Thread : Crashed: com.apple.main-thread
0 libsystem_platform.dylib 0x35180518 _os_lock_recursive_abort + 18446744073709552000
1 libsystem_platform.dylib 0x3518050f _os_lock_handoff_lock_slow + 90
2 libobjc.A.dylib 0x34adac3f objc_object::sidetable_release_slow((anonymous namespace)::SideTable*, bool) + 22
3 libobjc.A.dylib 0x34adad2f objc_object::sidetable_release(bool) + 118
4 Foundation 0x27f56f01 -[NSOperationQueue dealloc] + 72
5 libobjc.A.dylib 0x34adad5f objc_object::sidetable_release(bool) + 166
6 libobjc.A.dylib 0x34ac14d9 _class_initialize + 536
7 libobjc.A.dylib 0x34ac705f lookUpImpOrForward + 254
8 libobjc.A.dylib 0x34ac6f1b lookUpImpOrNil + 26
9 libobjc.A.dylib 0x34abfab3 class_getMethodImplementation + 34
10 libobjc.A.dylib 0x34ada583 weak_read_no_lock + 58
11 libobjc.A.dylib 0x34ada871 objc_loadWeakRetained + 92
12 MyApp 0x000c5983 -[WORef object] (WeakRef.m:12)
Anyone know what might be causing this?
// consider this method
-(void)populateArray {
WORef *ref = [WORef new];
ref.object = [YourObject new];
return #[ref];
// once you get out of this scope, the memory space occupied by [YourObject new]
// can be re-allocate to something else and your ref.object would still point at
// that memory space. You would get random crashes depending on when this memory space get re-allocated.
// The reason being is that ref.object hold a weak reference to [YourObject new]
// so it reference count doesn't get incremented.
// Change your property declaration to (nonatomic,strong) instead
}
Related
Fabric found that the NSMutableArray found crash when calling removeAllObjects.
Most of the crash happened in iOS9. This is my code, crash in [self.recommentGoodsArray removeAllObjects]:
- (void)clickColorWithIndex:(NSUInteger)index {
[self.recommentGoodsArray removeAllObjects];
[self.tableView reloadData];
GoodsInfo *gInfo = [self.goodsInfo.relatedGoodsArray objectAt:index];
self.goods_id = gInfo.goods_id;
[self loadGoodsDetail];
}
Fabric Latest Session
Crashed: com.apple.main-thread
0 libobjc.A.dylib 0x22d2a94e realizeClass(objc_class*) + 25
1 libobjc.A.dylib 0x22d2aa15 realizeClass(objc_class*) + 224
2 libobjc.A.dylib 0x22d2aa15 realizeClass(objc_class*) + 224
3 libobjc.A.dylib 0x22d2d91b lookUpImpOrForward + 158
4 libobjc.A.dylib 0x22d2d873 _class_lookupMethodAndLoadCache3 + 34
5 libobjc.A.dylib 0x22d33cfb _objc_msgSend_uncached + 26
6 CoreFoundation 0x2357e523 -[__NSArrayM removeAllObjects] + 266
7 ZZKKO 0x19f781 -[GoodsDetailVC clickColorWithIndex:] (GoodsDetailVC.m:825)
You have a crash in _objc_msgSend_uncached. It's likely that you are addressing deallocated object. It cat either be recommentGoodsArray, or self.
You need to check following:
Is recommentGoodsArray declared strong or weak? It has to be
strong.
Is it possible for this method to be called after VC was destroyed (example: from a timer or in a callback from an UIAlertView). If so, you have to retain self until the moment your function is done working with self.
You can try if self.recommentGoodsArray has objects before removing it.
if ([self.recommentGoodsArray count]){
[self.recommentGoodsArray removeAllObjects];
}
I have a class with a property
#property (nonatomic, copy) MyObject *currentObject;
and a function that looks like this:
- (void)handleExternalChange {
#synchronized (self) {
MyObject *newObject = [self.externalStore getObject];
//Business logic...
self.currentObject = newObject;
}
}
I am seeing a crash objc_msgSend() selector name: copyWithZone: occasionally when i hit the self.currentObject = newObject line.
I presume this is because the externalStore released the object returned by getObject on another thread and by the time it got down to the setter it was gone.
Does this conclusion seem right? If so is there a recommended way to fix this?
UPDATE:
Here's some of the stack trace
Application Specific Information:
objc_msgSend() selector name: copyWithZone:
0 libobjc.A.dylib 0x000000018149ef30 objc_msgSend + 16
1 libobjc.A.dylib 0x000000018149c2d4 objc_setProperty_nonatomic_copy + 44
2 MyApp 0x0000000100115cb4 -[MyClass handleExternalChange] (MyClass.m:117)
3 CoreFoundation 0x00000001829ee22c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 16
4 CoreFoundation 0x00000001829ed930 _CFXRegistrationPost + 396
5 CoreFoundation 0x00000001829ed6ac ___CFXNotificationPost_block_invoke + 56
6 CoreFoundation 0x0000000182a5cb9c -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1500
7 CoreFoundation 0x000000018292fbf4 _CFXNotificationPost + 372
8 Foundation 0x00000001834366bc -[NSNotificationCenter postNotificationName:object:userInfo:] + 64
MyObject *newObject = [self.externalStore getObject];
That method shouldn't be named get* anything, but that isn't likely to be the cause of the issue. Nor is it likely to be a threading issue (at least not directly).
This sounds more like there is some state related to the copyWithZone: that isn't handled correctly. How is the copy method implemented? Specifically, is it correctly copying all state and bumping reference counts or does it try to cheat by doing some kind of a byte copy?
I recently received a crash report that looks like this:
0 libobjc.A.dylib 0x0000000193dfea88 object_cxxDestructFromClass(objc_object*, objc_class*) + 0
1 libobjc.A.dylib 0x0000000193e0bf34 objc_destructInstance + 88
2 libobjc.A.dylib 0x0000000193e0bf8c object_dispose + 24
3 My App 0x00000001000d88fc -[CCAction dealloc] + 44
4 My App 0x00000001000e6bf8 -[CCActionSequence .cxx_destruct] + 36
5 libobjc.A.dylib 0x0000000193dfeb18 object_cxxDestructFromClass(objc_object*, objc_class*) + 144
6 libobjc.A.dylib 0x0000000193e0bf34 objc_destructInstance + 88
7 libobjc.A.dylib 0x0000000193e0bf8c object_dispose + 24
8 My App 0x00000001000d88fc -[CCAction dealloc] + 44
9 My App 0x00000001000e6bf8 -[CCActionSequence .cxx_destruct] + 36
10 libobjc.A.dylib 0x0000000193dfeb18 object_cxxDestructFromClass(objc_object*, objc_class*) + 144
11 libobjc.A.dylib 0x0000000193e0bf34 objc_destructInstance + 88
12 libobjc.A.dylib 0x0000000193e0bf8c object_dispose + 24
13 My App 0x00000001000d88fc -[CCAction dealloc] + 44
14 My App 0x00000001000e6bf8 -[CCActionSequence .cxx_destruct] + 36
...and it continues on like this forever. Some kind of infinite recursion happening in dealloc that is causing a stack overflow.
There are only 2 method calls that are visible to me and one of them is .cxx_destruct which is an internal private method used by ARC. The other is the dealloc method of CCAction which only has a log statement in it:
-(void) dealloc {
CCLOGINFO(#"cocos2d: deallocing %#", self);
}
The crash is really hard to reproduce; I've only seen it once. However my app has not been released to the public yet so I'd really like to figure out what is causing it. Any help debugging this would be greatly appreciated.
I am using Cocos2D 3.3.0 and iOS 8.1
If you are using ARC, sending messages to self in dealloc doesn't work like how it used to in manual memory management. ARC does all of the cleanup/disposing in .cxx_destruct BEFORE calling dealloc, so sending messages to self in dealloc will cause undefined behavior - most probably crashes. Unlike in manual memory management where you can send messages to self in dealloc before calling [super dealloc].
From your code, it looks like CCLogInfo is sending a message to self, most probably [self description] (assuming CCLogInfo parameters are just a format string with arguments). This will cause a crash, but I'm not sure why it is causing recursion - can you share the code to CCLogInfo?
In my ChatMessage class I have the weak reference to Chat class
#interface ChatMessage : NSObject
#property (nonatomic, weak) Chat *chat;
I make the following initialization within Chat implementation on the global queue
ChatMessage *chatMessage = [[ChatMessage alloc] initWithDictionary:dictionary];
chatMessage.chat = self;
and get very strange error at the second line
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x4c008be5
Triggered by Thread: 17
Thread 17 Crashed:
0 libobjc.A.dylib 0x39f535d2 cache_getImp + 18
1 libobjc.A.dylib 0x39f4e9a8 lookUpImpOrForward + 28
2 libobjc.A.dylib 0x39f4e942 lookUpImpOrNil + 22
3 libobjc.A.dylib 0x39f48aca class_getMethodImplementation + 30
4 libobjc.A.dylib 0x39f5833a weak_register_no_lock + 38
5 libobjc.A.dylib 0x39f586fa objc_storeWeak + 106
6 MyMessenger 0x000d366c -[ChatMessage setChat:]
7 MyMessenger 0x001173bc -[Chat getMessagesForPage:]
8 libdispatch.dylib 0x3a432d50 _dispatch_call_block_and_release + 8
9 libdispatch.dylib 0x3a438684 _dispatch_root_queue_drain + 224
10 libdispatch.dylib 0x3a4388d8 _dispatch_worker_thread2 + 52
11 libsystem_pthread.dylib 0x3a563c14 _pthread_wqthread + 296
12 libsystem_pthread.dylib 0x3a563ad8 start_wqthread + 4
Did someone come across such problems with setting weak properties?
It appears that the problem is in simultaneous access to this weak property from different threads.
Weakness of the property doesn't allow us to neglect multithread assigning.
The safe rule to work with datasources is to change it synchronously within one selected thread.
I'm trying to debug some ARC code, and it'd be really helpful if i could find out when an object of a certain class is added to the autorelease pool (not when it is actually autoreleased down the track).
Is this possible, eg with a breakpoint? Or by overwriting the 'autorelease' method and putting a breakpoint in it? Any suggestions?
-- edit --
The problem is that i've got an infrequent crash occurring where a custom subclass of UIView is autoreleased on a background thread, which crashes because UIView's cannot be dealloc'd on a background thread. The trace looks like below:
0 libsystem_kernel.dylib __pthread_kill + 8
1 libsystem_c.dylib pthread_kill + 54
2 libsystem_c.dylib abort + 94
3 libc++abi.dylib abort_message + 46
4 libc++abi.dylib default_terminate() + 24
5 libobjc.A.dylib _objc_terminate + 146
6 libc++abi.dylib safe_handler_caller(void (*)()) + 76
7 libc++abi.dylib operator delete(void*)
8 libc++abi.dylib __cxa_throw + 122
9 libobjc.A.dylib objc_exception_throw + 94
10 CoreFoundation +[NSException raise:format:]
11 Foundation -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 90
12 MYAPP MySuperclass.m line 156 -[MySuperclass dealloc]
13 MYAPP MyClass.m line 41 -[MyClass dealloc]
14 ... libobjc.A.dylib _objc_rootRelease + 36
15 libobjc.A.dylib objc_release + 38
16 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 224
17 libobjc.A.dylib _objc_autoreleasePoolPop + 12
18 CoreFoundation _CFAutoreleasePoolPop + 18
19 libdispatch.dylib _dispatch_worker_thread2 + 338
20 libsystem_c.dylib _pthread_wqthread + 294
This might not help with your problem, but I think it answers your original question:
You can add a symbolic breakpoint on [NSObject autorelease] and then set a condition to match your class. If your running on a device $r0 should hold the pointer to the receiving object. You need to do some casting to make the condition work: (BOOL)[(id)$r0 isKindOfClass:[NSArray class]] breaks whenever an NSArray is added to the autoreleasepool. Note that everything will be running very slow as the debugger has to break on every autorelease and check the condition.