Showing posts with label Objective C. Show all posts
Showing posts with label Objective C. Show all posts

Thursday, 19 March 2020

What happens in Objective C if you compare two NSObjects with a simple 'greater than' or 'less than'?

This is one of those odd bugs that goes unnoticed because a line of code that shouldn't work,  strangely does, at least most of the time.

The problem exhibited itself under certain circumstances. That led me to investigate and discover this line (in pseudocode):

if ([object getProperty] > [anotherObject getProperty]){

Years ago, this line was correct, because in this particular object's getProperty used to return a primitive such as an int or NSInteger (I can't remember which).

But at some point getProperty was changed so that it returned an NSNumber, which is an object rather than a simple value.

The line should have been updated to (and has been now):

if ([[object getProperty] integerValue] > [[anotherObject getProperty] integerValue]){

Of course I should have searched the source for 'getProperty' and updated accordingly but this particular line escaped. It went unnoticed. The compiler didn't complain, and everything still seemed to work.

If a tree falls in a forest......    If a bad line of code appears to work under testing and no-one reports a problem, is it still a bug?

It didn't always work though. Under certain circumstances that particular thing went screwy (no crash, no exception, just odd results sometimes). But not randomly. It worked or didn't work predictably, depending on the circumstances.

I can't find confirmation of this but it seems from what I've observed that

(NSObject > NSObject) 

returns true or false depending on the order that they were created. I'm assuming that the comparison is being made using the object's address (pointer) treated as a numeric value. This makes sense because declaring NSObject *myobject  declares myobject as a pointer, which is some kind of primitive, known to contain a memory address.

A simple experiment seems to bear this out.

    NSNumber *number1 = [NSNumber numberWithInt:2];
    NSNumber *number2 = [NSNumber numberWithInt:10];
   
    NSLog(@"number1: %p, number2: %p", number1, number2);
   
    if(number1 > number2){
        NSLog(@"number1 is greater than number2");
    }
    else{
        NSLog(@"number2 is greater than number1");
    }

returns:

NumberTest[89819:23978762] number1: 0x7312659ea1d74f79, number2: 0x7312659ea1d74779
NumberTest[89819:23978762] number1 is greater than number2

It's interesting that the objects seem to be allocated going backwards in memory in this example. I assume that allocation of memory is not predictable, but that there would be some general sequence to it, backwards or forwards.


I'm obviously pleased to have found and fixed a problem in this app. But more than anything this has amused and interested me.

If you have anything to contribute on this subject, leave it in the comments.

Wednesday, 27 March 2019

C64 / 6502 IDE for Mac

This post seems to fit equally well here on the PM blog as it does on the separate blog for my hobby project. To avoid duplication I'll post it there and link to it from here.

https://newstuffforoldstuff.blogspot.com/2019/03/homebrew-game-for-c64-part-5-c64-6502.html


Sunday, 9 July 2017

NSURLConnection won't die!


Integrity has been the most interesting project of my life.

I believe that the success of the web is down to the html standard and its flexibility *. It's human-readable, human-writable and web browsers do their best to render a page, whatever problems there might be.

I hate to think how many hundreds of hours of work that this has made for me over the last ten years.

With many applications, you can press every button, test all scenarios and be sure that it works. But with a web crawler you can test it on 99 websites, and it'll fail when the first person tries to use it **.

In order to have a reliable web crawler which parses html itself, you have to investigate every problem and improve your code  to handle whatever new unpredictable thing has been tripping it up.

It's taken ten years of this hard work for Integrity (and other related apps which use the same engine) to be as stable as it is. It has more users than ever and head-scratching problems are very few and far between now.

The worst times are where the problem happens at a deeper system level and you can get no debug information.

At a very high level, you can obtain data for a url in a single line. At the opposite extreme you can get involved with sockets etc. for Integrity I've taken the middle ground, creating the response and asynchronous  connection and using delegate methods to monitor what's happening and be able to intervene if necessary.

But there comes a point where you say au revoir to the request / connection and wait for your various notifications. If the response / notification is unexpected then you're down to some educated guesswork and trial and error.

That's what's happened this week. At a certain point through scanning particular sites, all NSURLConnections would appear to 'lock up' and all would return timeout notifications. (And any further NSURLConnections created to any url within that app would also time out until the app was quit and re-started even though the same urls would respond in any other app.

As usual there are many questions and answers online, with many suggestions that aren't relevant or have no effect.

I eventually got somewhere with a process of elimination - stripping the relevant code down to bare essentials until the problem had gone, then adding the original code back in, chunk by chunk until it stopped working again.

It appears that the problem is related to connections staying alive. The app obviously manages the number of simultaneous connections, and either lets one connection load all its data and complete naturally, or cancel it (if that data isn't needed)  before creating a new connection to replace it.

I think what's going wrong in these cases is that when you think you've let go of a connection with [connection cancel], that connection sometimes stays open, and the next one isn't replacing it but adding to the number until some limit is hit.

Removing all [connection cancel]s and allowing every connection to load and finish naturally completely solved the problem.

Making better use of the HEAD method (when you know that you only need the status but not the data) and explicitly making sure those requests have 'connection: close' in the request header should solve the problem but it doesn't entirely.

There's a lot I still don't know - why is a connection sometimes staying alive after it's been cancelled (or in the case of a HEAD request, when it has supplied the header info and says that it's done). If anyone knows, do tell!







* Despite Microsoft's and Netscape's best attempts to make it their own, it's survived as a truly universal standard - anyone can make a web page that can be read in any browser. It's an unusual thing and the IoT has a lesson to learn.

** There are some ridiculously unexpected things in the code of some websites (written by humans and written by machines)

Wednesday, 7 June 2017

Where has Objective C gone in Apple's Documentation?

Today for the first time I noticed that I'm no longer able to choose Objective C as my preferred syntax
It's bugged me for a long time that these pages always appear pre-selected with Swift and don't remember my selection of Objective C.

To be fair, this isn't entirely unexpected. When Swift was announced, it was unlikely that they'd run the two languages in parallel for any length of time. I've been forced to learn another language in the past; my first dealings with Cocoa were in Java-cocoa (sounds tasty!). (Java was the first OO language I learned). Translating an entire project from the Java syntax to Obj-C when I could no longer compile the Java-Cocoa code was a PITA.

I felt the wind of change when I started to notice that the first solutions I found to problems contained Swift code rather than Objective C, and I either had to translate (easy if you're just looking for the best method to use, but harder if it's a whole block of example code you want to use) or keep Googling for another answer with an Obj-C code example.

I guess I'll have to find out how to use the built-in documentation and wait for the day that things are available in Swift but not Obj C.

Wednesday, 8 June 2016

Retro button style in OSX cocoa


While developing a 'breadcrumb' or NSPathControl type class, I created some buttons programmatically -  initially very quick and dirty;  [[NSButton alloc] initWithFrame] and not bothering to set anything.

This may be the first time I've ever done that because I wasn't prepared for what happened:
It takes me back to Amiga Workbench, and some interesting times that I had to use Windows at various day jobs.

It makes sense; I haven't set any button type or border style. But I would have thought I'd get the basic push button (the ones you get for OK and Cancel in alerts etc).

This isn't a complaint at all, I'm just bemused to see OSX drawing these retro-looking buttons.

[Update]  it's not looking bad...

Monday, 1 October 2012

Tracking down zombies in objective-C applications

Aren't the worst bugs the ones that cause crashes infrequently seemingly randomly?

Even when you think you've found the problem and run the app successfully 100 times, you still can't be 100% sure you've got it! It's like trying to prove a negative.

Tracking down such a problem can be like the proverbial needle in a haystack. You can get clues from the crash report or debugger console, commenting out lines can work if the problem is fairly reproduceable but a good analysis tool can save many hours of poring over code and testing and re-testing.

In my experience a problem like this is most likely caused by object alloc / release. In such cases XCode will report 'EXC_BAD_ACCESS' which means (most likely but not always) that you're trying to access an object which has been released. 



Instruments with Zombies (XCode>Run with performance tool>Zombies) is a little baffling at first but is well worth it. If the bad access happens, it'll show you the life cycle of the object in question, where and when it was retained and released.