Recently i noticed strange behaviour with Objective C memory management in Xcode.
Here is the code:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *firstString = [[NSString alloc] initWithString: #"Hello"];
NSString *secondString = firstString;
[firstString release];
NSLog(#"%#", secondString);
}
return 0;
}
I think, that secondString points to nil after releasing firstString and NSLog should produce error.
But this code does not produce any errors and successfully prints "Hello" string.
I manually compiled and run code with such command and does not noticed any errors too:
% clang -framework Foundation -fno-objc-arc main.m && ./a.out
I tried to compile this code with online objective-c compiler (GCC) (http://rextester.com/l/objectivec_online_compiler) and error was occurred.
What am i doing wrong?
ARC support is turned off in Xcode.
Thanks in advance.
If you perform a static analysis (shift+command+B in Xcode, or "Analyze" on Xcode's "Product" menu), it will warn you that you're trying to reference an object after it is released:
The issue is that in manual reference counting code, your references to deallocated objects are not set to nil automatically. So you can end up with dangling pointers to an object that was previously deallocated unless you manually nil those pointers.
The static analyzer is remarkably good at identifying these sorts of issues, amongst others. I would advise making sure you have a clean bill of health from the static analyzer before moving on.
The second line of defense in situations like this is to enable the zombies runtime debugging option. This will report any attempts to interact with objects after they've been deallocated. Zombies can be enabled in the "Diagnostics" section when you edit your Xcode target's scheme.
Unfortunately, you are using NSString, which doesn't follow the typical memory management rules (it can keep its own references to strings, so they're not always deallocated when you'd otherwise expect them to be).
Consider an example similar to yours, except with a custom class:
#import <Foundation/Foundation.h>
#interface MyObject: NSObject
#end
#implementation MyObject
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyObject *firstObject = [[MyObject alloc] init];
MyObject *secondObject = firstObject;
[firstObject release];
NSLog(#"%#", secondObject);
}
return 0;
}
If you run this with zombies on, you'll get a corresponding error message indicating that you're trying to interact with a deallocated instance:
2017-05-27 08:19:18.154033-0700 MyApp[36888:7215135] *** -[MyObject isProxy]: message sent to deallocated instance 0x100303620
But you may not get this warning from NSString. Bottom line, one should avoid drawing any broader memory management conclusions from NSString behavior. Instead, rely upon Xcode's static analyzer and zombies. (Note, remember to turn off zombies when you're done debugging your app.)
Related
On iOS 11, when I intentionally create objects which would turn out to be tagged pointers, they start with 0xB, instead of the 0x0000000000000026, 0x000000000000001c, 0x000000000000005a values that are showing up in my crash reports as invalid addresses. I think these are likely tagged pointers, but they aren't formatted like tagged pointers that I see in the debugger.
What about 0x0000000000000010, 0x0000000000000020, 0x0000000000000030 ? They all have a trailing 0, but they sure look suspiciously small to be real pointers.
The implementation details of tagged pointers changes from release to release and architecture to architecture. With that said, those really don't look like tagged pointers.
What is most likely is that some piece of code is dereferencing into a struct or object that is unexpectedly NULL or nil.
Run this code:
struct bob {
void *a;
void *b;
void *c;
char d[42];
};
int main(int argc, const char * argv[]) {
struct bob *fred = NULL;
fred->d[2] = 'q';
return 0;
}
You'll get this crash (on x86_64): Thread 1: EXC_BAD_ACCESS (code=1, address=0x1a)
That is, it is trying to dereference through 0x0. So, more likely than not, you have a struct/object reference that is NULL and your code is trying to dereference an element or instance variable that is offset by the hex #s you listed from the beginning.
I heard that Objective-C is influenced by the "message passing mechanism" of SmallTalk.
Objective-C, like Smalltalk, can use dynamic typing: an object can be
sent a message that is not specified in its interface. This can allow
for increased flexibility, as it allows an object to "capture" a
message and send the message to a different object that can respond to
the message appropriately, or likewise send the message on to another
object.
And I felt for codes like [anObject someMethod], the binding of someMethod to the machine code may happen at run-time..
Therefore, I write a demo like this:
#import <Foundation/Foundation.h>
#interface Person : NSObject {
#private char *name;
}
#property (readwrite, assign) char *name;
- (void)sayHello;
#end
#implementation Person
#synthesize name;
- (void)sayHello {
printf("Hello, my name is %s!\n", [self name]);
}
#end
int main() {
Person *brad = [Person new];
brad.name = "Brad Cox";
[brad sayHello];
[brad sayHelloTest];
}
I tried [brad sayHelloTest] to send brad a message sayHelloTest which brad doesn't know how to handle with.. I expect the error will NOT happen at compile-time..
However, the compiler still throws an error:
main.m:24:11: error: instance method '-sayHelloTest' not found (return type defaults to 'id') [-Werror,-Wobjc-method-access]
[brad sayHelloTest];
^~~~~~~~~~~~
main.m:3:12: note: receiver is instance of class declared here
#interface Person : NSObject {
^
Change [(id)brad sayHelloTest] to [(id)brad sayHelloTest]; doesn't work either.. (The compiling command is clang -Wall -Werror -g -v main.m -lobjc -framework Foundation -o main)
In Objective-C, does the binding of method really happen at "run-time"? If so, why will there be a compiler error like this?
If the binding doesn't happen at "run-time", why was "Objective-C" called "dynamic typing language"?
Does anyone have any ideas about this?
One job of a compiler is to catch as many errors at compile time as possible. If it can tell that the call will fail at runtime, you generally want it to complain.
You can suppress this via casting to show that runtime resolution is happening:
[(id)brad sayHelloTest];
Because the IDE can infer the obvious error from the context.
When you write if (a = 1),you will get a warning. A good IDE should help you find mistakes as early as possible.
I figured out the reason finally..
It throw errors during compiling because -Werror flag is included, which will turn warning into error..
http://clang.llvm.org/docs/UsersManual.html#cmdoption-Werror
After I delete -Werror flag, everything works as expected and the error only happens at run-time.
It has become a compiler error only within the last five years for there to be no known declaration of a method. It has to do with Automatic Reference Counting. Under ARC, the compiler is now responsible for the reference-counting-based memory management that Cocoa uses.
Given that responsibilty, it must be able to see the declarations of methods for any messages before they are sent, so that it knows what retains and releases are appropriate.
The method resolution (the lookup of the method on the class) does still happen at runtime, and -- particularly if you disable ARC -- you can still take advantage of message forwarding.
One way around ARC's requirement was given by Marcelo Cantos -- cast the receiver to id. Another is to use performSelector:. A third -- though I can't recommend it -- is to use objc_msgSend() directly.
Note that the "binding" of the method does, and always did, happen at compile time. Methods are associated with classes, when the classes are defined. Messages are distinct from methods, and it is they that resolve at runtime to a method.
The following method crashes due to an early ARC release of inputLower, even though there is still a strong ptr in scope. This is using XCode 6.4 to build for ARM64, and only crashes when -Os optimization is used. Is this an ARC bug, or am I making some kind of ARC logic error?
void crasher(NSString * input) {
NSString * inputLower = [input lowercaseString]; // should be strong ptr
NSString * inputLower2 = inputLower; // should be a 2nd, independent strong ptr
int i = 1;
while (i < 10) {
inputLower2 = [NSString stringWithFormat:#"%#%d", inputLower, i++];
// ERROR: inputLower is released here, so the next iteration will crash
NSLog(#"%#", inputLower2);
}
}
The crash can be avoided simply by adding a copy, but I've read the ARC rules in detail, and I don't believe this should be required:
NSString * inputLower2 = [inputLower copy];
If this isn't my fault, I'll file a bug with Apple.
I've seen this sort of thing before. It's not an ARC matter so much as an optimization matter, and NSLog in particular elicits it. In this instance, it's the NSLog inside the while loop that causes the problem; something is being optimized away. If you move the NSLog to after the while loop you'll see that the loop worked.
Moreover, I can't reproduce the crash in Xcode 7 (that's why it took me so long to reproduce it; I had to switch specifically to Xcode 6.4). This suggests the Apple folks do know about this.
The WWDC 2013 lecture slides contain a section that in some cases the explicit bridging casts can be omitted. I wrote this in Xcode 5 (using 10.8 though, not 10.9), and the compiler complains I need a bridging cast. Am I just totally not understanding the concept?
#import <Foundation/Foundation.h>
CF_IMPLICIT_BRIDGING_ENABLED
CFStringRef MyCreateStringFromNothing();
CF_IMPLICIT_BRIDGING_DISABLED
void SomeFunction() {
// compiler requires bridging cast here...
NSString* x = MyCreateStringFromNothing();
}
The reason I ask is I wanted to write:
NSString* s = CFUUIDCreateString(NULL, uuid);
and thought that the new work on implicit bridging should simply enable me to write this. But this also needs a bridging cast.
This appears to be sort of black magic, but from my experiments functions with "Create" in them don't work with implicit bridging.
When I compiled this:
CF_IMPLICIT_BRIDGING_ENABLED
CFStringRef MyCreateStringFromNothing();
CFStringRef MyGetStringFromNothing();
CF_IMPLICIT_BRIDGING_DISABLED
void SomeFunction() {
NSString *const fails1 = MyCreateStringFromNothing();
NSString *const fails2 = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("FOO"));
NSString *const works = MyGetStringFromNothing();
}
The first two fail but the third one "works."
Note that implicit bridging apparently STILL does NOT actually keep the compiler from warning you about converting CFStringRef to NSString *, it just stops requiring you to insert a __bridge as well.
You'll notice that in slide at WWDC2013 in session 404 at 50:30, he uses CFDictionaryGetValue(), which returns a "void *", so it's one of the few functions that won't raise a warning. Any of the CF functions that return an actual type (e.g. CFStringRef, or CFDictionary) are still going to throw warnings, so, meh.
Check the compiler settings on the project and/or target. If this is an older project, you might have a stray option there causing you to use an older compiler. (This tripped me up a few times when I moved to Xcode 5 and wanted to use the latest Obj-C newness.)
These days, I always choose "Default compiler" in my build options.
I'm developing one logger class for my application.NSLog will be printed only in debug mode. I have customized the NSLog where Name of the source file,Source code line number,Name of the class and method where NSLog() was called is printed.
That is , my current NSLOG looks like
(ClassName MethodName) (SourceFileName:LineNumber) NSLog output
Now, I want to log, parameter values of the methodname. How to get those parameter values in NSLog???
I want the output as
(ClassName MethodName) (SourceFileName:LineNumber) (Parameter Values) NSLog output
Something like this should work
#define InstanceLog(fmt, ...) NSLog(#"(%#.%#)(%s:%d) " fmt, NSStringFromClass(self.class), NSStringFromSelector(_cmd), __FILE__, __LINE__, ##__VA_ARGS__)
You may use it as NSLog within Objective-C methods
InstanceLog(#"simple string");
InstanceLog(#"%# %#", #"hello", #"world");
There isn't a way to automatically introspect the values passed to a method. Even in DEBUG builds (where the optimizer is out of the way), any attempts to write code to introspect said iVars is going to be incredibly complex (you'll have to dive into the symbol tables, extract offsets, etc, and then try and find the arguments that were likely destroyed in the attempt to grab them).
So, no, no way to really automate that.
In general, though, any such logging mechanism would generate such an atrociously huge amount of output that you are far better off creating (potentially debugging only) logging that is both configurable and highly tuned to your application.
You can pass args like this (thanks to #hoha for the simpler version).
#import <Foundation/Foundation.h>
#define FooLog(fmt, ...) NSLog(#"(%s): %#", __PRETTY_FUNCTION__, ## __VA_ARGS__)
#interface Bob:NSObject
#end
#implementation Bob
- (void)yourUncle
{
FooLog(#"%#", self);
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSString *w = #"World";
FooLog(#"Hello, %#!", w);
[[Bob new] yourUncle];
}
}
Output:
2013-09-02 10:51:49.447 Untitled[60967:507] (int main(int, char **)): Hello, World!
2013-09-02 10:51:49.453 Untitled[60967:507] (-[Bob yourUncle]): <Bob: 0x7fde8840a490>
for handle custom logging you can use or refer following link.
You can customise Lumberjack as per you needs.
Lumberjack error logging for ios
Lumberjack error logging tutorial