Doubts about __bridge, _bridge_retain and _bridge_transfer - ios

I have read about __bridge, _bridge_retain and _bridge_transfer and did some experiments. However the output does not coincide with what I was expecting. In particular, I have the following code:
#interface ViewController ()
#property (nonatomic, strong) NSString *test;
#end
#implementation ViewController
CFStringRef cfString;
- (void)viewDidLoad
{
[super viewDidLoad];
self.test = #"123";
cfString = (__bridge CFStringRef)self.test;
self.test = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"%#", cfString);
NSLog(#"%#", self.test);
}
I expect the program to crash, based on the following reasoning: _bridge does not transfer ownership, so while casting self.test to cfString, there is no retainCount increment. Once self.test is set to nil, ARC will step in and dealloc the string. So that portion of memory is freed, but cfString is still pointing there, resulting in a pointer bad access exception. In contrast to my reasoning, the output is 123 and null, and of course the program does not crash.
Moreover, if I replace
self.test = nil;
with
CFRelease(cfString);
I expect the program to crash as well due to a similar reasoning. Even stranger is that the output is now 123 and 123.
Can anyone kindly elaborate why? Btw, the term ownership always troubles me, some explanation will be greatly appreciated.

Your problem is that you're using a constant string. This is put straight into the programs memory so the reference is unexpectedly remaining valid despite the fact that it shouldn't. Use something less constant than a constant string and your program will brake like you think.

The problem is that you base your example on a literal NSString value.
In objective-C, constant NSString (constant values known at compile time) are never released. In fact, their main memory managment methods are like:
+ (id)allocWithZone:(NSZone *)zone {
id _uniqueInstance = [self _singletonInstanceOfClass];
if( _uniqueInstance == nil )
_uniqueInstance = [super allocWithZone:zone];
return _uniqueInstance;
}
- (id)copyWithZone:(NSZone *)zone {
(void)zone;
return self;
}
- (id)retain {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax; // denotes an object that cannot be released
}
- (oneway void)release {
//do nothing
return;
}
- (id)autorelease {
return self;
}
As you can see, releasing them is not possible.

Related

When is the autorelease object released?

I tested it with the following code,and I found that the autorelease objc never release.
__weak id ref;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString stringWithFormat:#"test"]; // add autoreleasePool
ref = str;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"viewWillAppear:%#",ref); // result test
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"viewDidAppear:%#",ref); // result test
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(#"%#",ref); // If I click long after,But it has not been released
}
[NSString stringWithFormat:] doesn't promise you an object on the autoreleasepool. It promises you an object that you do not have to release. In this case, it's returning you a constant string, and a constant string is never destroyed. It is also free to return you a cached value, or a value shared with other readers, a tagged pointer, or a singleton. NSNumber has lots of optimizations like this.
To the underlying question, when the local autorelease pool is drained, one release will be sent to the object for each autorelease that was previously sent to the object on that pool. Whether this destroys the object or not depends on what other retains have been placed on it.
I would expect a value like this to behave closer to what you're expecting, but there are no promises:
[NSString stringWithFormat:#"something a little long and computed: %d", rand()]

NSNumber claims to be NSString

I am getting the warning Comparison of distinct pointer types ('NSString *' and 'NSNumber *') in this line of code:
if(beacon.beaconIdentifier == self.identifier) {
// do something
}
Both should contain an NSNumber. The object beacon is a CLBeacon. self.identifier is a NSNumber. Other possibly relevant code:
CLBeacon category
#interface CLBeacon ();
#property(nonatomic, strong) NSNumber *beaconIdentifier;
#end
static char const *const MyBeaconIdentifier = "MyBeaconIdentifier";
....
- (CLBeacon *)initWithJSON:(NSDictionary *)json {
self = [super init];
if (self) {
...
self.beaconIdentifier = json[#"id"];
}
return self;
}
- (void) setBeaconIdentifier:(NSNumber *)beaconIdentifier {
objc_setAssociatedObject(self, MyBeaconIdentifier, beaconIdentifier, OBJC_ASSOCIATION_RETAIN);
}
- (NSNumber *)beaconIdentifier {
return objc_getAssociatedObject(self, MyBeaconIdentifier);
}
json[#"id"] always only contains numbers. Besides the warning, everything runs as it should. What is causing my problem and what could possibly solve it?
Thanks to a couple great responses, I managed to solve the warning. Still, I would like to know what caused it, if that is possible.
beacon.beaconIdentifier.class logs _NSCFNumber, just as self.identifier does.
First, you are using pointer equality (“do these pointers point to the same address?”) where you want to use object equality (“are the objects pointed to by these pointers considered equal?”). Simply call -isEqual: instead of using == to fix this. [Comparing using == could work for NSNumbers since they are tagged pointers, but you shouldn’t do it anyway.]
Second, if the compiler complains of mismatched types, beaconIdentifier and self.identifier can’t really both be declared as NSNumbers, there has to be an NSString somewhere.
You say json[#"id"] contains a number, but that is not known at compile-time. Hence the warning. Its only at run-time that it will be identified as NSNumber by the compiler.
To check equality of NSNumber, try this code
if([beacon.beaconIdentifier isEqualToNumber:self.identifier]) {
// do something
}

Compilation error with OCMockito verify and NSError**

I can not compile this code:
[verify(mockedContext) deleteObject:item1];
[verify(mockedContext) deleteObject:item2];
[verify(mockedContext) save:anything()];<--compilation error for conversion id to NSError**
However I'm able to pass compilation in similar case with given macros with additional syntax:
[[given([mockedContext save:nil]) withMatcher:anything()] willReturn:nil];
Are there anything to help me pass compilation with verify?
Here is compilation error:
Implicit conversion of an Objective-C pointer to 'NSError *__autoreleasing *' is disallowed with ARC
I assume the save: method on the 'mockedContext' takes a pointer-to-pointer to NSError.
So actually, the NSError must be seen as an extra return value of the save:method. This means that you should rather setup an expectation in the first place.
I worked out a small example:
We start with the Context protocol with a simple method taking an NSError**.
#protocol Context <NSObject>
- (id)doWithError:(NSError *__autoreleasing *)err;
#end
Next is a class using this protocol, much like your SUT. I called it ContextUsingClass
#interface ContextUsingClass : NSObject
#property (nonatomic, strong) id<Context> context;
#property BOOL recordedError;
- (void)call;
#end
#implementation ContextUsingClass
- (void)call {
NSError *error;
[self.context doWithError:&error];
if (error) {
self.recordedError = YES;
}
}
#end
As you can see, when the context method doWithError: returns an error, the recordedError property is set to YES. This is something we can expect to be true or false in our test. The only problem is, how do we tell the mock to result in an error (or to succeed without error)?
The answer is fairly straight forward, and was almost part of your question: we pass an OCHamcrest matcher to the given statement, which in turn will set the error for us through a block. Bear with me, we'll get there. Let's first write the fitting matcher:
typedef void(^ErrorSettingBlock)(NSError **item);
#interface ErrorSettingBlockMatcher : HCBaseMatcher
#property (nonatomic, strong) ErrorSettingBlock errorSettingBlock;
#end
#implementation ErrorSettingBlockMatcher
- (BOOL)matches:(id)item {
if (self.errorSettingBlock) {
self.errorSettingBlock((NSError * __autoreleasing *)[item pointerValue]);
}
return YES;
}
#end
This matcher will call the errorSettingBlock if it has been set, and will always return YES as it accepts all items. The matchers sole purpose is to set the error, when the test asks as much. From OCMockito issue 22 and it's fix, we learn that pointer-to-pointers are wrapped in NSValue objects, so we should unwrap it, and cast it to our well known NSError **
Now finally, here is how the test looks:
#implementation StackOverFlowAnswersTests {
id<Context> context;
ContextUsingClass *sut;
ErrorSettingBlockMatcher *matcher;
}
- (void)setUp {
[super setUp];
context = mockProtocol(#protocol(Context));
sut = [[ContextUsingClass alloc] init];
sut.context = context;
matcher = [[ErrorSettingBlockMatcher alloc] init];
}
- (void)testContextResultsInError {
matcher.errorSettingBlock = ^(NSError **error) {
*error = [NSError errorWithDomain:#"dom" code:-100 userInfo:#{}];
};
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(sut.recordedError, is(equalToBool(YES)));
}
- (void)testContextResultsInSuccess {
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(sut.recordedError, is(equalToBool(NO)));
}
#end
Conclusion
When you call methods within your SUT which are returning errors through pointer-to-pointers, you should probably test for the different possible outcomes, rather than just verifying if the method has been called.
If your SUT is ignoring the error, then let the block you pass into the matcher keep a boolean flag to indicate that it was called like so:
- (void)testNotCaringAboutTheError {
__block BOOL called = NO;
matcher.errorSettingBlock = ^(NSError **error) {
called = YES;
};
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(called, is(equalToBool(YES)));
}
Or with simple verification:
- (void)testWithVerifyOnly {
[sut call];
[[verify(context) withMatcher:matcher] doWithError:nil];
}
PS: Ignoring errors is probably something you don't want to do...

Confusing about memory management on IOS

There's a little bit uncommon situation in my app, that is,
I have to reload some retain properties everytime when the view is going to appear,
the code looks like this:
// .h
#property (nonatomic, retain) NSArray *myData;
// .m
#synthesize myData;
- (void)viewWillAppear:(BOOL)animated {
... // get FetchRequest and so on
self.myData = [self.context executeFetchRequest:request error:&error]; // Line 1
[super viewWillAppear:animated];
}
- (void)viewDidUnload {
self.myData = nil;
[super viewDidUnload];
}
- (void)dealloc {
[myData release]; // Line 2
[super dealloc];
}
there are several points:
1st. as you see, the property "myData" is retain, so I think every I set some object for it, it would automatically retain that object?
2nd. I have to reload "myData" everytime the view will appear, just like the code of Line 1 above.
3rd. Since it is a retain property, I have to release it myself correctly.
Now, question is, do I correctly managed the memory without any leaking of "myData" using the codes above?
If the view would appear many times before it is dealloc, (like push in a further view in a UINavigationController and pop out for several times),
then myData would retain some object more than once, but I only release it in the dealloc for 1 once in Line 2, so is that ok?
But if I add this method the to viewController,which I think is more safe for avoiding memory leaks:
- (void)viewWillDisappear:(BOOL)animated {
self.myData = nil;
[myData release];
[super viewWillDisappear:animated];
}
- (void)dealloc {
// [myData release]; // don't release it here.
[super dealloc];
}
my app would crash after one or two times I push in and pop out the view,
So which one is really wrong?
Thanks a lot!
You are not only releasing it in Line 2, it will be also released in Line 1 when replaced as well as in viewDidUnload, so your code on top is just fine. The key is that
self.myData = anything;
is expanded to
[self->myData release];
self->myData = [anything retain];
so by assigning anything (including nil) you are already calling release implicitly. You could in fact replace Line 2 with self.myData = nil; to have never to call release since you don't have any explicit retain.
.h
#property (nonatomic, retain) NSArray *myData;
.m
#synthesize myData;
By including these lines in your code a setter and getter is created for your property myData. The setter generated at run time for objects looks something like this,
- (void)setMyData: (id)newValue
{
if (myData != newValue)
{
[myData release];
myData = newValue;
[myData retain];
}
}
The total effect is that whenever you access the property by appending self in front you are actually calling the setters and getters. So the following two lines are the exact same.
self.myData = nil;
[self setMyData:nil];
So your original code was already correct.

Can an object instance release itself?

I need to create some objects that will be waiting for some event. When the waited event is triggered, the object makes some things, and then has no longer any reason to live.
I don't want to have to maintain a list of created objects, so I'd like to do something like this :
main() {
Waiter* myWaiter1 = [[Waiter alloc] initAndWaitForEvent:xxxxxxxxxx];
Waiter* myWaiter2 = [[Waiter alloc] initAndWaitForEvent:xxxxxxxxxx];
Waiter* myWaiter3 = [[Waiter alloc] initAndWaitForEvent:xxxxxxxxxx];
Waiter* myWaiter4 = [[Waiter alloc] initAndWaitForEvent:xxxxxxxxxx];
....
/* myWaiterx are retained */
/* I don't release them */
}
Waiter
- (void) catchSomeEvent:(...*)theEvent {
// do what is expected
[self release]; // Release self
/* the release is there */
}
Will this work, and work fine ?
I find it better when there’s somebody to take care of the waiters, but your code is fine. Objects can do this, there is no technical obstacle that would prevent it. Some classes from the standard library already do something similar, for example UIAlertView.
I don’t think that the static analyzer will like your current API, though. It will probably complain about leaks; it would be better to tweak the interface a bit.
#interface Waiter : NSObject {}
- (id) init;
- (void) startWaitingForEvent: (id) event;
#end
#implementation Waiter
- (void) startWaitingForEvent: (id) event
{
[self retain];
…
}
- (void) eventReceived
{
…
[self release];
}
#end
Then the memory management in user code looks better:
- (void) dispatchWaiters {
Waiter w1 = [[Waiter alloc] init];
[w1 startWaitingForEvent:…];
[w1 release];
}
An object can not suicide. It can either be killed by you(the code you sent to kill it), or by the professional killer NSAutoreleasePool. If you own it, you have to kill it.
Warning: If it doesn't die on time, the population will increase and will mess up the memory.
;-)
On some occasions [self release]; is used, for example for initializers (to force variables that a required in some way), an example:
SomeClass.m:
- (id)initWithString:(NSString *)string
{
self = [super init];
if (self)
{
if (string == nil)
{
[self release];
return nil;
}
// if required values are provided, we can continue ...
}
return self;
}
- (id)init
{
return [self initWithString:nil];
}
A caller would call this like:
- (void)testInitializer
{
SomeClass *classInstance1 = [[SomeClass alloc] initWithString:#"bla"];
// classInstance1 != nil, any method calls will work as expected ...
SomeClass *classInstance2 = [[SomeClass alloc] initWithString:nil];
// classInstance2 == nil, will ignore any method calls (fail silently)
SomeClass *classInstance3 = [[SomeClass alloc] init];
// classInstance3 == nil, will ignore any method calls (fail silently)
}
I would guess since the above works fine, you shouldn't have any issues, though it doesn't seem a very clean solution.

Resources