Optimization: what are sidetable_release() and sidetable_retain()? - ios

In my OpenGL loop, Instruments is showing a total of 14% of my processor time in my particle processing loop going to objc_object::sidetable_release(bool) and objc_object:sidetable_retain(). This is significant, because the loop is using 100% of a CPU on an iPhone 5.
I'm wondering if there's a way I can reduce this. I don't know what causes it, and I don't see these in very many of my methods. I think they are related to doing a fast enumeration of an array of objects.
Here is what the offending method looks like:
-(void) updateWithTime:(ccTime)dt sceneHeightAboveHorizon:(CGFloat)yMax{
_elapsed = (_elapsed+dt) ;
float farTotalWidth = EQ_SCENE_WIDTH + 2*EQ_SIZE_FAR;
float farHalfWidth = farTotalWidth/2.0;
for (MyParticleData *data in self.farParticleData){
//Calculate position
float newX = data.pos.x + data.xVelocity * dt;
if (newX > 1)
newX -= 1;
float newY = data.y0 + EQ_A_FAR*sin(EQ_F_FAR*_elapsed+data.phasePosition);
data.pos = cc3v(newX,newY,0);
//Apply new position to sprites
data.sprite.position = cc3v(newX*farTotalWidth-farHalfWidth, newY*yMax, 0);
data.reflectedSprite.position = cc3v(data.sprite.position.x,-data.sprite.position.y,0);
//Calculate color
float f = MIN(14, MAX(data.pos.x*14.0, 0));
ccColor4F newColor = cycBlendColors(self.settings.eqColumnColors[(int)f], self.settings.eqColumnColors[(int)f+1], f-(int)f);
float colorAmp = MAX(0, (sin(data.frequencyColor*_elapsed+data.phaseColor)+1)/2.0);
newColor = cycScaleColor(newColor,colorAmp);
colorAmp *= colorAmp;//the alpha (white component) should be squared twice
newColor.a *= colorAmp*colorAmp;
//Apply new color to sprites
data.sprite.color4F = newColor;
data.reflectedSprite.color4F = cycScaleColor(newColor, self.settings.eqReflectionBrightness);
}
}

I'll try and psychically debug here -
1) You have ARC enabled
2) Some of the intermediate variables in your expressions (ex. data.sprite, self.settings) are Objective-C objects
3) One or more of these intermediate objects are weak or atomic (or are themselves accessing weak or atomic properties), both of which will require extra retain/release handling when accessed.
- IIRC atomic properties won't involve the side table rigamarole, just normal autoreleasing, but no guarantees on that.
I would try assigning some/all of these references to a local (on the stack) variable before enumeration, and inside your loop use the local references exclusively. This will have the added benefit of shaving off some accessor time from your loop as well.
If you know these references will remain strongly referenced through the entire enumeration 100% of the time, then you can use the __unsafe_unretained specifier on your local variables, which will (mostly) prevent any ARC shenanigans whatsoever from occurring in this method.

Related

Why does allocating a float in Metal's threadgroup address space give different results depending on the hardware?

I have recently been working on a soft-body physics simulation based on the following paper. The implementation uses points and springs and involves calculating the volume of the shape which is then used to calculate the pressure that is to be applied to each point.
On my MacBook Pro (2018, 13") I used the following code to calculate the volume for each soft-body in the simulation since all of the physics for the springs and mass points were being handled by a separate threadgroup:
// Gauss's theorem
shared_memory[threadIndexInThreadgroup] = 0.5 * fabs(x1 - x2) * fabs(nx) * (rAB);
// No memory fence is applied, and threadgroup_barrier
// acts only as an execution barrier.
threadgroup_barrier(mem_flags::mem_none);
threadgroup float volume = 0;
// Only do this calculation once on the first thread in the threadgroup.
if (threadIndexInThreadgroup == 0) {
for (uint i = 0; i < threadsPerThreadgroup; ++i) {
volume += shared_memory[i];
}
}
// mem_none is probably all that is necessary here.
threadgroup_barrier(mem_flags::mem_none);
// Do calculations that depend on volume.
With shared_memory being passed to the kernel function as a threadgroup buffer:
threadgroup float* shared_memory [[ threadgroup(0) ]]
This worked well until much later on I ran the code on an iPhone and an M1 MacBook and the simulation broke down completely resulting in the soft bodies disappearing fairly quickly after starting the application.
The solution to this problem was to store the result of the volume sum in a threadgroup buffer, threadgroup float* volume [[ threadgroup(2) ]], and do the volume calculation as follows:
// -*- Volume calculation -*-
shared_memory[threadIndexInThreadgroup] = 0.5 * fabs(x1 - x2) * fabs(nx) * (rAB);
threadgroup_barrier(mem_flags::mem_none);
if (threadIndexInThreadgroup == 0) {
auto sum = shared_memory[0];
for (uint i = 1; i < threadsPerThreadgroup; ++i) {
sum += shared_memory[i];
}
*volume = sum;
}
threadgroup_barrier(mem_flags::mem_none);
float epsilon = 0.000001;
float pressurev = rAB * pressure * divide(1.0, *volume + epsilon);
My question is why would the initial method work on my MacBook but not on other hardware and is this now the correct way of doing this? If it is wrong to allocate a float in the threadgroup address space like this then what is the point of being able to do so?
As a side note, I am using mem_flags::mem_none since it seems unnecessary to ensure the correct ordering of memory operations to threadgroup memory in this case. I just want to make sure each thread has written to shared_memory at this point but the order in which they do so shouldn't matter. Is this assumption correct?
you should use mem_flags::mem_threadgroup, but I think the main problem is metal cant initialize thread group memory to zero like that, the spec is unclear about this
try
threadgroup float volume;
// Only do this calculation once on the first thread in the threadgroup.
if (threadIndexInThreadgroup == 0) {
volume = 0;
for (uint i = 0; i < threadsPerThreadgroup; ++i) {
volume += shared_memory[i];
}
}
If you don't want to use a threadgroup buffer, the correct way to do this is the following:
// -*- Volume calculation -*-
threadgroup float volume = 0;
// Gauss's theorem
shared_memory[threadIndexInThreadgroup] = 0.5 * fabs(x1 - x2) * fabs(nx) * (rAB);
// No memory fence is applied, and threadgroup_barrier
// acts only as an execution barrier.
threadgroup_barrier(mem_flags::mem_none);
if (threadIndexInThreadgroup == 0) {
volume = shared_memory[0];
// Index memory use signed int types over unsigned.
for (int i = 1; i < int(threadsPerThreadgroup); ++i) {
volume += shared_memory[i];
}
}
threadgroup_barrier(mem_flags::mem_none);
You can use either threadgroup_barrier(mem_flags::mem_none) and threadgroup_barrier(mem_flags::mem_threadgroup), it appears to make no difference.

Crash EXC_BAD_ACCESS using malloc/free

I have a crash with some optimisation code. What I'm trying to do is to remove some points from the input array when the previous and the next point are close enough. The method works well in almost all case but crash with some specific data.
An example of input data that crash:
Value of coords : (51.55188, -0.17591), (51.55208, -0.17516), (51.55231, -0.17444)
Value of altitudes : 10000, 10000, 10000
Value of count : 3
If I skip the optimisation code and use directly the input value, then everything works correctly. It also works correctly if I simply memcpy the input values in the temp arrays.
I got a EXC_BAD_ACCESS EXC_I386_GPFLT after using this method with the input data posted. The crash doesn't happen directly in this method but after when I use the object created at the end of the method. I've already tried NSZombie and Profiling for zombies. Everything works correctly with almost all the data but crash 100% with this specific input data (At least it is easier for me to debug!).
The code of my method:
+ (instancetype) optimizedPolylineWithCoordinates:(CLLocationCoordinate2D*) coords altitudes:(RLMKAltitude*) altitudes count:(NSUInteger) count
{
CGFloat minimumDistanceBetweenPoints = [self minimumOptimizedDistanceBetweenPoints];
CLLocationCoordinate2D* tempCoords = malloc(sizeof(CLLocationCoordinate2D) * count);
RLMKAltitude* tempAltitudes = malloc(sizeof(RLMKAltitude) * count);
NSUInteger tempCoordsCount = 0;
// Always keep first point
tempCoords[0] = coords[0];
tempAltitudes[0] = altitudes[0];
++tempCoordsCount;
for (NSUInteger i = 1; i < (count - 1); i++)
{
MKMapPoint prevPoint = MKMapPointForCoordinate(coords[i - 1]);
MKMapPoint nextPoint = MKMapPointForCoordinate(coords[i + 1]);
// Get the distance between the next point and the previous point.
CLLocationDistance distance = MKMetersBetweenMapPoints(nextPoint, prevPoint);
// Keep the current point if the distance is greater than the minimum
if (distance > minimumDistanceBetweenPoints)
{
tempCoords[tempCoordsCount] = coords[i];
tempAltitudes[tempCoordsCount] = altitudes[i];
++tempCoordsCount;
}
}
// Always keep last point
tempCoords[tempCoordsCount] = coords[(count - 1)];
tempAltitudes[tempCoordsCount] = altitudes[(count - 1)];
++tempCoordsCount;
RLMKMapWay* object = [self polylineWithCoordinates:tempCoords altitudes:tempAltitudes count:tempCoordsCount];
free(tempCoords);
free(tempAltitudes);
return object;
}
Note that the polylineWithCoordinates method called with the temp data take care of making copy of all the data so the problem is likely not related with the free located after the call (I've already tried to comment both lines and the crash still happen)
When count == 1, you are writing outside the allocated memory.

CorePlot allow user drag rotate PieChart

(As for I got the solution now, it is being shared at the bottom)
Fact is I have been struggling a while about this and I believe quite a lot of discussions I found are related to older versions of CorePlot or unanswered.
Firstly, I am using CorePlot 1.5.1.
I am able to plot a PieChart already and now I would like the user to be able to rotate it by dragging on the screen ( doesn't really matter touch directly the pieChart or the host View).
Using these delegates at the moment:
#interface MyChartViewController : UIViewController<CPTPieChartDataSource,CPTPlotSpaceDelegate,CPTPieChartDelegate>
Got a hostView,
#property (strong, nonatomic) IBOutlet CPTGraphHostingView *hostView;
Made a graph, set as, self.hostView.hostedGraph = graph
and made a PieChart, put into the graph, [graph addPlot:self.mainPieChart];
(I set the pieChart with a strong property to let me refer it anytime)
So, here is my first attempt, and fact is, it is responding, (though not in a desirable way)
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *) self.hostView.hostedGraph.defaultPlotSpace;
[plotSpace setDelegate:self];
(only works by setting plotSpace delegate to self, not sure why, i guess it's about finding a way to receive user's interaction, anyway, then I overwrite these two functions)
Using this value:
static float deltaAngle;
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDownEvent:(UIEvent *)event atPoint:(CGPoint)point
{
float dx = point.x - self.mainPieChart.centerAnchor.x;
float dy = point.y - self.mainPieChart.centerAnchor.y;
deltaAngle = atan2(dy,dx);
return YES;
}
This, in order to save the first touching point
Then sense the dragging do use the difference to make the rotation
( at least I wanted so )
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDraggedEvent:(UIEvent *)event atPoint:(CGPoint)point
{
int x = self.mainPieChart.centerAnchor.x;
int y = self.mainPieChart.centerAnchor.y;
float dx = point.x - x;
float dy = point.y - y;
double a = atan2(dx,dy);
float angleDifference = deltaAngle - a;
self.mainPieChart.startAngle = -angleDifference;
return YES;
}
And here is an image about it, though i think I covered most of the details already.
http://postimg.org/image/bey0fosqj/
It is in landscape mode though.
Fact is I think this would be the most appropriate function to call, but somehow I cannot call it out (pretty sure I set self.mainPieChart delegate/ datasource to self already)
-(BOOL)pointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)interactionPoint{
(after further testing)
Interesting, after trying to print out different values, by the shouldHandlePointingDevice function (simply clicking), I think i got some ideas now.
the self.mainPieChart.centerAnchor.x / y values always return 0.5 (both)
However, point x, point y are returning values vary from 1-500+,
it seems more like I am comparing two things, though they are on top of each other, from different perspective.
Likely the PlotSpace set delegate part messed that up.
============================================================
So, as for now I still don't know how to call -(BOOL)pointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)interactionPoint{, I tried to put it into a if loop like
if([self.mainPieChart pointingDeviceDownEvent:event atPoint:self.mainPieChart.centerAnchor] == YES)
under my touched function but nothing happened, never mind.
Back to the point, my current solution works well now, even after applying padding.
float x = (self.hostView.bounds.size.width + self.hostView.hostedGraph.paddingLeft)*self.mainPieChart.centerAnchor.x;
float y = self.hostView.bounds.size.height * self.mainPieChart.centerAnchor.y;
float dx = point.x - x;
float dy = point.y - y;
double a = atan2(dx,dy);
these lines are all same for both press / drag functions, as for drag function,
float angleDifference = deltaAngle - a;
self.mainPieChart.startAngle = angleDifference;
are added before the end
However, the case is slightly different when the Pie Chart is not at the middle, or, in other words, the graph holding the Pie Chart is padded.
( my example somehow is mid centre just to make it easy)
you simply have to mortify the x y float value above, it's easier than I expected.
For example if I have,
graph.paddingLeft = -300.0f;
the value of float x in both press/drag will become
float x = (self.hostView.bounds.size.width + self.hostView.hostedGraph.paddingLeft)*self.mainPieChart.centerAnchor.x;
The pie chart centerAnchor is given as fractions of the width and height. Be sure to multiply the anchor values by the corresponding dimension of the graph before computing dx and dy.

What is wrong with this "Do.. While" loop in Swift?

I am trying to write this in Swift (I am in step 54). In a UICollectionViewLayout class I have a function setup function
func setup() {
var percentage = 0.0
for i in 0...RotationCount - 1 {
var newPercentage = 0.0
do {
newPercentage = Double((arc4random() % 220) - 110) * 0.0001
println(newPercentage)
} while (fabs(percentage - newPercentage) < 0.006)
percentage = newPercentage
var angle = 2 * M_PI * (1 + percentage)
var transform = CATransform3DMakeRotation(CGFloat(angle), 0, 0, 1)
rotations.append(transform)
}
}
Here is how the setup function is described in the tutorial
First we create a temporary mutable array that we add objects to. Then
we run through our loop, creating a rotation each time. We create a
random percentage between -1.1% and 1.1% and then use that to create a
tweaked CATransform3D. I geeked out a bit and added some logic to
ensure that the percentage of rotation we randomly generate is a least
0.6% different than the one generated beforehand. This ensures that photos in a stack don't have the misfortune of all being rotated the
same way. Once we have our transform, we add it to the temporary array
by wrapping it in an NSValue and then rinse and repeat. After all 32
rotations are added we set our private array property. Now we just
need to put it to use.
When I run the app, I get a run time error in the while (fabs(percentage - newPercentage) < 0.006) line.
the setup function is called in prepareLayout()
override func prepareLayout() {
super.prepareLayout()
setup()
...
}
Without the do..while loop, the app runs fine. So I am wondering, why?
Turns out I had to be more type safe
newPercentage = Double(Int((arc4random() % 220)) - 110) * 0.0001
This must be a Swift bug. That code should NOT crash at runtime. It should either give a compiler error on the newPercentage = expression or it should correctly promote the types as C does.

Objective C iOS - objc_object::sidetable_retain/release chewing up CPU time in my loop?

I'm working on a Spritekit Tower Defence game. ARC is enabled. (And I intend to run this code in the background, though presently it's just running on the main thread.)
In my update loop (which is running up to 60 times a second) I call a method called getTargetsForTowers. After profiling this method, I've found two items in the list that are chewing up my CPU time: objc_object::sidetable_retain/release, and I'm trying to find out what they are.
I'd like to understand more about what this is and if I can improve performance by reducing them or getting rid of them altogether.
There are 300 enemies and 446 towers in my test scenario. The majority of the CPU time is reported in the tower loop.
- (void)getTargetsForTowers {
NSArray *enemiesCopy = [enemiesOnMap copy];
for (CCUnit *enemy in enemiesCopy) {
float edte = enemy.distanceToEnd;
CGPoint enemyPos = enemy.position;
[self calculateTravelDistanceForEnemy:enemy];
if (enemy.actualHealth > 0) {
NSArray *tiles = [self getTilesForEnemy:enemy];
for (CCTileInfo *tile in tiles) {
NSArray *tileTowers = tile.towers;
for (CCSKTower *tower in tileTowers) {
BOOL hasTarget = tower.hasTarget;
BOOL passes = !hasTarget;
if (!passes) {
CCUnit *tg = tower.target;
float tdte = tg.distanceToEnd;
passes = edte < tdte;
}
if (passes) {
BOOL inRange = [self circle:tower.position withRadius:tower.attackRange collisionWithCircle:enemyPos collisionCircleRadius:1];
if (inRange) {
tower.hasTarget = YES;
tower.target = enemy;
}
}
}
}
}
}
}
Screenshots from Time Profile (after 60 seconds of running):
image one http://imageshack.com/a/img22/2258/y18v.png
image two http://imageshack.com/a/img833/7969/7fy3.png
(I've been reading about blocks, arc, strong/weak references, etc., so I tried making the variables (such as CCSKTower *tower) __weak, which did get rid of those two items, but that added a whole bunch of new items related to retaining/creating/destroying the weak variables, and I think they consumed more CPU time than before.)
I'd appreciate any input on this. Thanks.
EDIT:
There's another method that I would like to improve as well which is:
- (NSArray *)getTilesForEnemy:(CCUnit *)enemy {
NSMutableArray *tiles = [[NSMutableArray alloc] init];
float enemyWidthHalf = enemy.size.width/2;
float enemyHeightHalf = enemy.size.height/2;
float enemyX = enemy.position.x;
float enemyY = enemy.position.y;
CGVector topLeft = [self getVectorForPoint:CGPointMake(enemyX-enemyWidthHalf, enemyY+enemyHeightHalf)];
CGVector topRight = [self getVectorForPoint:CGPointMake(enemyX+enemyWidthHalf, enemyY+enemyHeightHalf)];
CGVector bottomLeft = [self getVectorForPoint:CGPointMake(enemyX-enemyWidthHalf, enemyY-enemyHeightHalf)];
CGVector bottomRight = [self getVectorForPoint:CGPointMake(enemyX+enemyWidthHalf, enemyY-enemyHeightHalf)];
CCTileInfo *tile = nil;
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
int xIndex = (int)(x+gameHalfCols);
int yIndex = (int)(y+gameHalfRows);
tile = tileGrid[xIndex][yIndex];
if (tile != nil) {
[tiles addObject:tile];
}
}
}
}
}
return tiles;
}
I've looked over it repeatedly and there's nothing I really can see. Perhaps there's nothing more that can be done.
Screenshots:
One issue is that you create a new reference to tower.target, but only use that reference once. So simply rewriting that section should improve your performance, e.g.
if (!passes) {
float tdte = tower.target.distanceToEnd;
passes = edte < tdte;
}
Based on your comment, it seems that there's no way to avoid a retain/release if you access a property on tower.target. So let's try radical surgery. Specifically, try adding a distanceToEnd property to the tower, to keep track of the distanceToEnd for the tower's current target. The resulting code would look like this.
- (void)getTargetsForTowers {
// initialization to copy 'distanceToEnd' value to each tower that has a target
for ( CCSKTower *tower in towersOnMap )
if ( tower.hasTarget )
tower.distanceToEnd = tower.target.distanceToEnd;
NSArray *enemiesCopy = [enemiesOnMap copy];
for (CCUnit *enemy in enemiesCopy) {
float edte = enemy.distanceToEnd;
CGPoint enemyPos = enemy.position;
[self calculateTravelDistanceForEnemy:enemy];
if (enemy.actualHealth > 0) {
NSArray *tiles = [self getTilesForEnemy:enemy];
for (CCTileInfo *tile in tiles) {
NSArray *tileTowers = tile.towers;
for (CCSKTower *tower in tileTowers) {
if ( !tower.hasTarget || edte < tower.distanceToEnd ) {
BOOL inRange = [self circle:tower.position withRadius:tower.attackRange collisionWithCircle:enemyPos collisionCircleRadius:1];
if (inRange) {
tower.hasTarget = YES;
tower.target = enemy;
tower.distanceToEnd = edte; // update 'distanceToEnd' on the tower to match new target
}
}
}
}
}
}
}
My impression is that there's not much to be done about the getTilesForEnemy method. Looking at the Running Time image for getTilesForEnemy it's clear that the load is fairly evenly spread among the various components of the method, with only three items above 10%. The top item getVectorForPoint isn't even in the innermost loop. The second item insertObject is apparently the result of the addObject call in the inner loop, but there's nothing to be done for that call, it's required to generate the final result.
At the next level up (see the wvry.png image), you can see that getTilesForEnemy is now 15.3% of the total time spent in getTargetsForTowers. So even if it were possible to reduce getVectorForPoint from 17.3% to 7.3% there would not be a significant reduction in running time. The savings in getTilesForEnemy would be 10%, but because getTilesForEnemy is only 15.3% of the time in getTargetsForTowers, the overall savings would only be 1.53%.
Conclusion, because the components of getTilesForEnemy are balanced and below 20%, and because getTilesForEnemy is only 15.3% of the higher level method, no significant savings will be gained by trying to optimize getTilesForEnemy.
So once again the only option is radical surgery, and this time I mean a total rewrite of the algorithm. Such action should only be taken if the app still isn't performing up to spec. You've run into the limitations of ARC and NSArray's. Both of those technologies are extremely powerful and flexible, and are perfect for high-level development. However, they both have significant overhead which limits performance. So the question becomes, "How do you write the getTargetsForTowers without using ARC and NSArray's?". The answer is to use arrays of C structs to represent the objects. The resulting top level pseudo code would be something like this
copy the enemy information into an array of C structs
copy the tower information into an array of C structs
(note that the target for a tower is just an 'int', which is the index of an enemy in the enemy array)
for ( each enemy in the enemy array )
{
create an array of C structs for the tiles
for ( each tile )
for ( each tower in the tile )
update the tower target if needed
}
copy the updated tower information back into the NSArray of tower objects
For your second method, this part seems unclear and inefficient:
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
For instance, there's no point in spinning the y loop if your x is out of bounds. You could just do this:
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
More importantly, the point of the first for loop is to start x at some minimum and increment it to some maximum, and the if statement is there to make sure x is at least some minimum and less than some maximum, so there's no reason to have both a for() and an if(). I don't know what the values might look like for topLeft.dx and gameHalfCols, so I can't tell you the best way to do this.
But, for example, if topLeft.dx is always integral, you might say:
for (float x = MAX(topLeft.dx, ceil(-(gameHalfCols+1))); x < bottomRight.dx+1 && x < gameHalfCols; x++) {
for (float y = ...
You could similarly improve the 'y' for this way. This sin't just fewer lines of code, it also prevents the loops from spinning a bunch of extra times with no effect: the 'if' statements just make the loops spin quickly to their ends, but including the logic inside the 'for's themselves makes them only loop over values that you'll actually use in computations.
To expand my comments to a complete answer:
The normal, correct Objective-C behaviour when returning an object property is to retain and then autorelease it. That's because otherwise code like this (imagine you're in the world before ARC):
TYTemporaryWorker *object = [[TYTemporaryWorker alloc] initWithSomeValue:value];
NSNumber *someResult = object.someResult;
[object release];
return someResult;
would otherwise be invalid. object has been deallocated so if someResult hasn't been retained and autoreleased then it will become a dangling pointer. ARC makes this sort of slightly less direct (the strong reference in someResult would have retained the number beyond the lifetime of object but then it would have been autoreleased for the return) but the principle remains and, in any case, whether an individual .m file has been compiled with ARC is not supposed to affect callers.
(aside: notice that weak isn't just strong without retains — is has related costs because the runtime has to establish a link from the object to the weak reference in order to know find it again and nil it if the object begins deallocation)
Supposing you wanted to create a new type of property that isn't strong and isn't unsafe_unretained but is rather defined to be that the object returned is safe for use for as long as the original owner is alive but unsafe afterwards. So it's a strong set but an unsafe_unretained get.
It's untested but I think the correct means to do that would be:
// we can't write want a synthesised getter that doesn't attempt to retain
// or autorelease, so we'd better flag up the pointer as potentially being
// unsafe to access
#property (nonatomic, unsafe_unretained) NSNumber *someResult;
...
#implementation TYWhatever
{
NSNumber *_retainedResult; // a strong reference, since
// I haven't said otherwise —
// this reference is not publicly exposed
}
- (void)setSomeResult:(NSNumber *)number
{
// set the unsafe and unretained version,
// as the default setter would have
_someResult = number;
// also take a strong reference to the object passed in,
// to extend its lifecycle to match ours
_retainedResult = number;
}
It's going to get quite verbose as you add more properties but what you're doing is contrary to normal Objective-C conventions so limited compiler help is probably to be expected.

Resources