How to use a Lightroom Catalog on multiple computers

16. März 2013 2 Kommentare

I have one Lightroom database for my photographs, but multiple computers. Of course I want be able to edit my photos on every computer. The obvious solution is to put the catalog on a network share. But the catalog is a sqlite-Database which is build to be accessed by one process only. Because of this, Lightroom forbids to use a catalog that is located on a network share.

My solution is to rsync the catalog before Lightroom starts and sync it back, when the process terminates. To avoid conflicts, I create a lockfile during the runtime of Lightroom. Not very fancy, but this works well for me.

#!/bin/sh
 
if [ -f /Volumes/Pictures/Lightroom/lockfile ]; then
  echo "Catalog is locked by"
  cat /Volumes/Pictures/Lightroom/lockfile
  exit 1
fi 
 
echo `hostname` > /Volumes/Pictures/Lightroom/lockfile
 
rsync user@server.local:/zdata/Pictures/Lightroom/Lightroom-4-Catalog.lrcat /Users/$USER/Pictures/Lightroom/Lightroom-4-Catalog.lrcat && \
open -W -a "Adobe Photoshop Lightroom 4" --args /Users/$USER/Pictures/Lightroom/Lightroom-4-Catalog.lrcat && \
rsync /Users/$USER/Pictures/Lightroom/Lightroom-4-Catalog.lrcat user@server.local:/zdata/Pictures/Lightroom/Lightroom-4-Catalog.lrcat && \
rm /Volumes/Pictures/Lightroom/lockfile

KategorienFotografie, Nerd, Technik Tags:

Hamburg im November

1. Dezember 2012 Keine Kommentare
KategorienGeneral Tags:

Duplicity on Ubuntu 12.04: broken ssh backend

30. September 2012 Keine Kommentare

After setting up a new server with Ubuntu 12.04, I discovered that duplicity was unable to perform backups. The first problem was the missing ssh-backend. The package python-paramiko must be installed in order to use ssh-servers as backup target.

The second pitfall is a bit tricky, because of the confusing error message:

BackendException: ssh connection to server:22 failed: [Errno -2] Name or 
service not known

The bug is fixed in the Quantal-package. Simply download the .deb from the Quantal repository and install it:

dpkg -i duplicity_0.6.19-0ubuntu1_amd64.deb

KategorienLinux, Nerd Tags:

Ubuntu auf einem Rootserver mit ZFS

11. August 2012 2 Kommentare

Es ist mal wieder so weit: der alte Server ist zu klein, ein neuer muss her. Dieser Artikel ist ein Update zu meinem Update meiner alten Anleitung zum Setup des Rootservers.

Ein Serverumzug ist eine gute Gelegenheit, ein paar Dinge anders zu machen als bisher:

  • ZFS: die bisherige Lösung aus Raid + LVM + Dateisystem funktioniert zwar, aber das vergrößern / verkleinern der Partitionen ist doch immer eine recht spannende Angelegenheit und kann nicht immer online durchgeführt werden. Btrfs bietet noch nicht alle Features, die ich haben möchte (quotas auf FS-Ebene) und mit ZFS on Linux steht ZFS auch nativ als Kernelmodul zur Verfügung.
  • LXC statt OpenVZ: der Betrieb mit OpenVZ war nie ganz frei von Sorgen. Es ist super, dass man so viele Parameter hat und Resourcen flexibel zuweisen kann, aber speziell beim Webserver hatten wir das Gefühl, dass nie genug RAM vorhanden ist. Stößt der Server an das RAM-Limit, kommt es zu den seltsamsten Effekten, incl. Stillstand des Webserver. LXC ist im Mainline-Kernel und für unsere Zwecke gut genug, d.h. ich will Resourcen flexibel zuweisen und kann auf die höhere Sicherheit einer echten Virtualisierung verzichten.
  • nginx statt Apache: weil man es mal gemacht haben sollte ;-)

Eine Sache, die sich hingegen bewährt hat, ist alle Dienste (PAM, Mail, sonstige Accounts) über LDAP abzuwickeln. Damit ist LDAP der “single point of administration and failure”, der sich besonders gut scripten lässt.

Hier also wieder die Copy & Paste Anleitung für das Setup von

Ubuntu auf einem Rootserver mit ZFS

Ich habe meinen Server bei Hetzner, aber das muss jeder selbst wissen. Als erstes bootet man ins Rescue-system und sichert den Inhalt der Datei /etc/network/interfaces:

### Hetzner Online AG - installimage
# Loopback device:
auto lo
iface lo inet loopback

# device: eth0
auto  eth0
iface eth0 inet static
  address   5.9.62.176
  broadcast 5.9.62.191
  netmask   255.255.255.224
  gateway   5.9.62.161

# default route to access subnet
up route add -net 5.9.62.160 netmask 255.255.255.224 gw 5.9.62.161 eth0

Festplatten einrichten

Dieses Mal soll der Server mit zfs betrieben werden. Es gibt zwar diverse Anleitungen, wie man Ubuntu in ein natives Root-ZFS-Dateisysten installiert, ich wähle aber bewusst einen anderen Weg.

Im Recovery-Fall komme ich nicht an den physikalischen Server. Ich kann nur ein Rescue-System booten, das kein ZFS-Kernelmodul hat, damit würde ich nicht an das root-Dateisystem kommen.
Deswegen installiere ich das Basissystem auf einer ext4-Partition. Hier läuft lediglich der LDAP-Server und ein SSH-Server. Alle anderen Dienste laufen in LX-Containern und diese können auf dem ZFS untergebracht werden.

Der Server ist mit 2×3 TB Festplatten ausgestattet, d.h. mit einem MBR kommt man nicht sehr weit, statt dessen wird eine GUID Partition Table (GPT) eingerichtet. Neben Root, Swap, ZFS wird auch eine “BIOS boot partition” benötigt (ein EFI-Requirement).

gdisk /dev/sda

Der Rechner hat 32 GB Ram, also sollten 16 GB Swap genügen. Für das Root-System stelle ich 10GB bereit. Auf LVM verzichte ich bewusst um die Komplexität gering zu halten. Ich verliere damit die Möglichkeit das Basissystem über LVM-Snapshots zu sichern, den Kompromiss kann ich eingehen. Root und Swap werden aber in einem Raid-1 betrieben um einen Plattenausfall abzufangen. So sieht das gewünschte Layout aus:

Command (? for help): p
Disk /dev/sda: 5860533168 sectors, 2.7 TiB
Logical sector size: 512 bytes
Disk identifier (GUID): 932661F7-1628-4107-A48F-8FDDC9DB29E4
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 5860533134
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            4095   1024.0 KiB  EF02  BIOS boot partition
   2            4096        33558527   16.0 GiB    FD00  Linux RAID
   3        33558528        54530047   10.0 GiB    FD00  Linux RAID
   4        54530048      5860533134   2.7 TiB     BF00  Solaris root

Die Platte /dev/sdb wird analog eingerichtet. Jetzt ist ein guter Zeitpunkt um das System zu rebooten (wir müssen nochmal das Rescue-System auswählen), damit die neue Partitionstabelle verwendet wird.

Dann können swap und / angelegt werden:

mdadm -v --create /dev/md0  --level=mirror  --metadata=0.90 --raid-devices=2 /dev/sda2 /dev/sdb2
mdadm -v --create /dev/md1  --level=mirror  --metadata=0.90 --raid-devices=2 /dev/sda3 /dev/sdb3

Wichtig: --metadata=0.90 sollte unbedingt gesetzt werden, sonst kann man große Probleme mit grub bekommen.

Swap aktivieren:

mkswap /dev/md0
swapon /dev/md0

Root-Dateisystem anlegen und mounten:

mkfs.ext4 /dev/md1
mkdir /mnt/install
mount /dev/md1 /mnt/install/

Jetzt können wir

Ubuntu installieren

Wir benötigen erstmal ein neueres debootstrap und installieren damit dann Ubuntu 12.04 LTS:

wget http://archive.ubuntu.com/ubuntu/pool/main/d/debootstrap/debootstrap_1.0.42_all.deb
dpkg -i debootstrap_1.0.42_all.deb 
debootstrap --arch amd64 precise /mnt/install http://archive.ubuntu.com/ubuntu

Die fstab muss angepasst werden:

vim /mnt/install/etc/fstab
proc                    /proc   proc    defaults 0 0
/dev/md0                none    swap    defaults,pri=1 0 0
/dev/md1                /       ext4    defaults 0 1

ausserdem muss die Netzwerk-Config, die wir vorhin gesichert haben wieder gesetzt werden:

vim /mnt/install/etc/network/interfaces
### Hetzner Online AG - installimage
# Loopback device:
auto lo
iface lo inet loopback

# device: eth0
auto  eth0
iface eth0 inet static
  address   5.9.62.176
  broadcast 5.9.62.191
  netmask   255.255.255.224
  gateway   5.9.62.161

# default route to access subnet
up route add -net 5.9.62.160 netmask 255.255.255.224 gw 5.9.62.161 eth0

Die Spezialmounts werden noch gemountet:

mount -t proc none /mnt/install/proc
mount --bind /sys /mnt/install/sys
mount --bind /dev /mnt/install/dev
mount -t devpts none /mnt/install/dev/pts

Dann können wir ins System chrooten…

chroot /mnt/install/ /bin/bash

… und ein paar Einstellungen vornehmen. Zuerst ein neues root-Passwort:

shadowconfig on
passwd

Wir brauchen das Universe-Repository

vim /etc/apt/sources.list
deb http://archive.ubuntu.com/ubuntu precise main universe

Dann werden ein paar grundsätzliche Tools installiert:

apt-get update
apt-get install aptitude
aptitude install man-db vim ssh

Ausserdem wird mdadm zur Raidverwaltung benötigt. Da in Ubuntu eine Abhängigkeit zu einem MTA existiert und der default Postfix ist, installieren wir das sehr schlanke Tool SSMTP mit:

aptitude install ssmtp mdadm

Dann brauchen wir noch einen Bootloader. Dieser wird als Abhängigkeit des Kernels installiert:

aptitude install linux-image-generic

Damit die Boot-Partitionen durch die initrd auch einwandfrei gemountet werden, sollte Änderung der Datei mdadm.conf noch die initrd neu angelegt werden, z.B. durch Ausführen von

dpkg-reconfigure mdadm

Sofern die entsprechenden Module, md bzw. raid0, raid1, noch nicht in der /etc/initramfs-tools/modules aktiviert wurden, muss dies noch erfolgen:

echo "raid0" >>/etc/initramfs-tools/modules
echo "raid1" >>/etc/initramfs-tools/modules
echo "md" >>/etc/initramfs-tools/modules

und anschließend das initramfs mit

update-initramfs -u -k all

aktualisiert werden.

Zum Schluss solle die Option “quiet splash” aus /etc/default/grub entfernt werden. Die Remote-Konsole LARA bei Hetzner kommt sonst mit dem Bildschirmmodus nicht klar und man würde bei bootproblemen nichts sehen.

vim default/grub

Dann noch grub updaten, aus dem Chroot aussteigen und rebooten.

update-grub
exit
reboot

Wenn kein Fehler aufgetreten und der Server wieder hoch gekommen ist, können wir

ZFS installieren

Für Ubuntu ist die Installation sehr einfach. Wir brauchen erstmal das Paket python-software-properties, da sonst add-apt nicht funktioniert:

aptitude install python-software-properties

Dann kann nach der Installationsanleitung verfahren werden:

add-apt-repository ppa:zfs-native/stable
aptitude update
aptitude install ubuntu-zfs

Das Kernelmodul zfs.ko sollte jetzt gebaut und geladen sein. Jetzt können wir einen zpool auf dem verbleibenden Festplattenplatz anlegen.

zpool create -o ashift=12 tank mirror /dev/disk/by-id/ata-ST3000DM001-9YN166_W1F0W2EW-part4 /dev/disk/by-id/ata-ST3000DM001-9YN166_W1F0WNK2-part4

Die Option ashift=12 sort für ein passendes Alignment. Die Namen der Festplatten müssen natürlich angepasst werden.

Der Pool ist angelegt…

zfs list
NAME   USED  AVAIL  REFER  MOUNTPOINT
tank   472K  2.66T   136K  /tank

…und kann jetzt mit Filesystemen bestückt werden:

zfs create -o mountpoint=/home tank/home  
zfs create -o mountpoint=/var/vm tank/vm 
zfs list
NAME        USED  AVAIL  REFER  MOUNTPOINT
tank        864K  2.66T   136K  /tank
tank/home   136K  2.66T   136K  /home
tank/vm     136K  2.66T   136K  /var/vm

Ich habe zunächst nur ein Filesystem für /home und für die Linuxcontainer angelegt. Ab hier weiss sicherlich jeder selbst am besten, wie das System organisiert wird.

Credits: Das Setup des Basissystems mit GPT hätte ich ohne die Anleitung von Mattias nicht so schnell hinbekommen.

KategorienLinux, Nerd Tags:

Nachmieter gesucht: 3 Zimmer in der Potsdamer City

4. Juni 2012 Keine Kommentare

Wir suchen ab September 2012 einen Nachmieter für unsere Wohnung in der Potsdamer Innenstadt (Charlottenstraße / Wilhelm-Staab-Straße). Die Wohnung ist super, wird uns aber mit drei Kindern langsam zu klein :)

Update (8.8.2012): für die Wohnung ist ein Nachmieter gefunden.

  • 73 m^2
  • 792€ Warmmiete
  • 3. Etage unterm Dach
  • 3 Zimmer, großer Flur
  • WG-tauglich
  • ruhige Innenhof-Lage
  • großer Balkon von jedem Zimmer zugänglich
  • Bad mit Fenster, Badewanne
  • optional: Tiefgaragenstellplatz im Haus (+42 Euro)
  • optional: kostenloser Internetzugang über Freifunk Potsdam

Wer Interesse hat oder jemanden kennt, der jemanden kennt, einfach Bescheid geben.

KategorienGeneral Tags:

An one finger rotation gesture recognizer

13. Februar 2012 14 Kommentare

Last year we developed the Raumfeld iPhone App. The goal was to build something that could replace our native controller completely. The main design feature of the native controller is the massive volume knob. Of course we wanted to have something similar in our App, so the designer created the volume screen with a big knob, as shown in the right picture.

To create a volume control, we needed a way to detect if the user performs a rotation gesture on the knob image. There is no default UIGestureRecognizer that detects such a gesture, so we had to implement a custom gesture recognizer. It was surprisingly easy – to be honest, the hardest part was to do the math right.

To track the finger movement, we need to check for every touch event, whether it is within the defined area. The gesture is rotating, so there is a center point m, the radius a which defines the minimum distance from m and the radius b which defines the maximum distance from m. Finally we need to calculate the angle α between the startpoint and the current finger position (relative to m).

For the first check, we calculate the distance d from the center and make sure, that a < d < b is true.

/** Calculates the distance between point1 and point 2. */
CGFloat distanceBetweenPoints(CGPoint point1, CGPoint point2)
{
    CGFloat dx = point1.x - point2.x;
    CGFloat dy = point1.y - point2.y;
    return sqrt(dx*dx + dy*dy);
}

The rotation angle is the angle between the two lines a and b. The arc tangent function atan() is the key:

/** The method is a bit too generic - in our case both lines share the same start point. */
CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA,
                                   CGPoint endLineA,
                                   CGPoint beginLineB,
                                   CGPoint endLineB)
{
    CGFloat a = endLineA.x - beginLineA.x;
    CGFloat b = endLineA.y - beginLineA.y;
    CGFloat c = endLineB.x - beginLineB.x;
    CGFloat d = endLineB.y - beginLineB.y;
 
    CGFloat atanA = atan2(a, b);
    CGFloat atanB = atan2(c, d);
 
    // convert radiants to degrees
    return (atanA - atanB) * 180 / M_PI;
}

Ok, that was the hard part :) To implement a custom gesture recognizer, let’s have a look at the UIGestureRecognizer API docs, especially the subclassing notes. We just need to overwrite five methods

- (void)reset;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

The key is touchMoved:withEvent: This method checks the distance of the touch event from the center point. If the touch is within the valid area, the angle between the start point and the current touch position is calculated. The result is sent to the delegate object of our class.

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];
 
    if (self.state == UIGestureRecognizerStateFailed) return;
 
    CGPoint nowPoint  = [[touches anyObject] locationInView: self.view];
    CGPoint prevPoint = [[touches anyObject] previousLocationInView: self.view];
 
    // make sure the new point is within the area
    CGFloat distance = distanceBetweenPoints(midPoint, nowPoint);
    if (   innerRadius < = distance
        && distance    <= outerRadius)
    {
        // calculate rotation angle between two points
        CGFloat angle = angleBetweenLinesInDegrees(midPoint, prevPoint, midPoint, nowPoint);
 
        // fix value, if the 12 o'clock position is between prevPoint and nowPoint
        if (angle > 180)
        {
            angle -= 360;
        }
        else if (angle < -180)
        {
            angle += 360;
        }
 
        // sum up single steps
        cumulatedAngle += angle;
 
        // call delegate
        if ([target respondsToSelector: @selector(rotation:)])
        {
            [target rotation:angle];
        }
    }
    else
    {
        // finger moved outside the area
        self.state = UIGestureRecognizerStateFailed;
    }
}

target needs to implement some kind of protocol to allow the gesture recognizer notification of movements:

@protocol OneFingerRotationGestureRecognizerDelegate <nsobject>
@optional
/** A rotation gesture is in progress, the frist argument is the rotation-angle in degrees. */
- (void) rotation: (CGFloat) angle;
/** The gesture is finished, the first argument is the total rotation-angle. */
- (void) finalAngle: (CGFloat) angle;
@end
</nsobject>

And that's the whole magic. To use the gesture recognizer, create an image of a rotating control and add a gesture recognizer to your view:

// calculate center and radius of the control
CGPoint midPoint = CGPointMake(image.frame.origin.x + image.frame.size.width / 2,
                               image.frame.origin.y + image.frame.size.height / 2);
CGFloat outRadius = image.frame.size.width / 2;
 
// outRadius / 3 is arbitrary, just choose something >> 0 to avoid strange 
// effects when touching the control near of it's center
gestureRecognizer = [[OneFingerRotationGestureRecognizer alloc] initWithMidPoint: midPoint
                                                            innerRadius: outRadius / 3 
                                                            outerRadius: outRadius
                                                                 target: self];
[someView addGestureRecognizer: gestureRecognizer];

As soon as a gesture is detected, the delegate method is called and you can rotate the image according to the angle:

- (void) rotation: (CGFloat) angle
{
    // calculate rotation angle
    imageAngle += angle;
    if (imageAngle > 360)
        imageAngle -= 360;
    else if (imageAngle < -360)
        imageAngle += 360;
 
    // rotate image and update text field
    image.transform = CGAffineTransformMakeRotation(imageAngle *  M_PI / 180);
    textDisplay.text = [NSString stringWithFormat: @"\u03b1 = %.2f", imageAngle];
}

I've created a sample project on github, feel free to play with. Also make sure to read Ole Begemanns article about the UX details on gesture recognition.

KategorienApfel Tags:

Namensschilder für Polizisten: potentiell tödliche Waffen!!!1!!

15. September 2011 3 Kommentare

Nein, ernsthaft. Wenn jetzt alle Polizisten in Berlin ein Namensschild tragen, kann jeder dahergelaufene Gelegenheitsmörder dem Polizisten das Namensschild entreißen und damit ein Massaker anrichten. Bodo Pfalzgraf, Vorsitzender der deutschen Polizeigewerkschaft demonstriert die von den Namensschildern ausgehende Gefahr an einem Eisbein.

Die Plastikteile könnten den Polizisten entrissen und als lebensbedrohliche Waffe eingesetzt werden.

Große Sprüche auch von Rainer Wendt:

Die Zwangskennzeichnung ist ein gigantischer Misstrauensbeweis in unsere Polizei“, sagt Rainer Wendt, Bundesvorsitzender der Gewerkschaft. Das Vertrauensverhältnis der Bürger in die Polizei sei hervorragend, die individuelle Kennzeichnung daher überflüssig.

Richtig! Es ist ja noch NIE vorgekommen, dass die Polizei versehentlich Demonstranten mit verprügelt hat. Nie! NIE! Ehrenwort! Die Leidtragenden sind letztendlich die armen Polizisten:

Einige Kollegen hätten sich beim Anschnallen im Auto an den Kanten in den Arm geritzt.

Ich muss mir erstmal ein Taschentuch holen… schnüff..

KategorienGeneral Tags:

Morgenrunde

12. September 2011 Keine Kommentare

Mein Weg ins Büro, heute mal komplett mit dem Fahrrad.

KategorienGeneral Tags:

Chaos Communication Camp 2011: Hell yeah, it’s rocket science!

14. August 2011 Keine Kommentare
KategorienChaos Camp Tags:

CCCamp11 Quadrocopter Meetup

13. August 2011 Keine Kommentare

Gestern abend gab es ein kleines Quadrokopter-Treffen auf dem Buzz-Aldrin Boulevard:

CCCamp2011 Quadrocopter Meetup

CCCamp2011 Quadrocopter Meetup

KategorienChaos Camp, Nerd Tags:

CCCamp2011 bei Nacht

10. August 2011 2 Kommentare

Ein paar Eindrücke, rund um unser Zelt.

CCCamp2011 at night

CCCamp2011 at night

CCCamp2011 at night

CCCamp2011 at night

KategorienChaos Camp Tags:

Huch, schon August?

10. August 2011 Keine Kommentare

Oh, es ist ja Camp. Und gutes Wetter… Wir haben erstmal einen Graben gezogen, in der Hoffnung, dass das Zelt nicht absäuft.
Sehr cool: schon mit Kind 1 ein Throwie (LED + Batterie + Magnet) gebastelt und morgen für den Raketenworkshop angemeldet. Noch keinen Vortrag gesehen, aber dabei sein ist alles :)

KategorienChaos Camp, Nerd Tags:

Goldstaub

25. Mai 2011 1 Kommentar

Zumindest für mich: Müsli aus Chiang Khong. Die Woche sind die letzten Krümel im Frühstück gelandet, das jedes Mal wie Urlaub schmeckt :)

KategorienGeneral Tags:

Veranstaltungshinweis

2. Mai 2011 Keine Kommentare
KategorienMusik Tags:

Spring

25. April 2011 Keine Kommentare

Camera Model: Nikon D90
Lens: Nikon AF-S DX Nikkor 35mm 1:1,8G
Focal Length: 35.00 mm
Focal Length (35mm Equiv): 53 mm
Exposure Time: 1/4000 sec
F-Number: f/1.8
Shooting mode: Aperture priority
Exposure bias: 0 EV
Flash: No
ISO: 100
Image at flickr, large version

KategorienPhotoblog Tags:

(Oster-) Sonntagssoundtrack

24. April 2011 Keine Kommentare

KategorienMusik Tags:

Sonntagssoundtrack

10. April 2011 Keine Kommentare

KategorienMusik Tags:

Objective C: KVC und KVO

20. März 2011 1 Kommentar

Ich empfinde das Eintauchen in die Welt von Objective C und Cocoa als sehr erfrischend, jeden Tag gibt es etwas neues zu entdecken. Wenn man sich mit anderen Entwicklern, die Objective C nicht kennen, unterhält, kommt aus der C++ Ecke garantiert die Frage: “Kann man dort Operatoren überladen?“.

Nein kann man nicht. Braucht man auch gar nicht. Operatoren zu überladen ist zweifelsohne cool, aber ich multipliziere nicht den ganzen Tag Matrizen, sondern baue Dinge für Endanwender. Das angenehme an Objective C ist, dass es bestimmte Real World Probleme sehr schön löst.

Key Value Observing ist so eine Lösung und es ist ein so verdammt einfaches Konzept, dass ich mich wundere, wie die Welt bisher ohne leben konnte… (Um es vorweg zu nehmen: GObject hat ein ähnliches Konzept und es gibt bestimmt noch mehr Implementierungen dieses Patterns).

Das Real World Problem kommt bestimmt jedem bekannt vor: man hat eine Klasse A gebaut und eine andere Klasse B muss benachrichtigt werden, wenn sich in A irgendetwas interessantes ändert. Bisher wäre das Herangehen, dass A eine Referenz auf B mitschleifen muss, damit Klasse A die Klasse B benachrichtigen kann. Das bringt natürlich das schöne Klassendesign durcheinander, eigentlich hat A mit B nichts zu tun und die Referenz auf B zu handhaben ist auch nervig.

In Objective C gibt es Properties, die Membervariablen und deren Getter und Setter erzeugen können:

// foo.h
@interface Foo
@property(nonatomic, copy) NSString* bar;
@end
 
// foo.m
@implementation Foo 
@synthesize bar;
@end

Im obrigen Beispiel hat die Klasse “Foo” ein Member “bar” und @synthesize generiert die Getter und Setter, die dann so verwendet werden können:

Foo *fooInstance = [Foo new]; // oder [[Foo alloc] init]
fooInstance.bar = @"Value";
NSLog(@"foo.bar=%@", fooInstance.bar); // "foo.bar=Value"
 
[fooInstance setBar:@"eulaV"]; // geht auch
NSLog("@foo.bar=%@", [fooInstance bar]); // "foo.bar=eulaV"
 
[fooInstance release];

Soweit, so trivial. Mit Key Value Coding kann man auf Properties programmatisch zugreifen. Das bedeutet, ich baue mir einen String zusammen, dessen Inhalt der Name einer Property ist und ich kann auf die Property zugreifen:

NSString* dings = [fooInstance valueForKey:@"bar"]; // "getter"
[fooInstance setValue:@"Hi there!" forKey:@"bar"]; // "setter"

Das wird unheimlich praktisch, wenn man z.B. XML parsen muss und die geparsten Key/Value-Paare direkt via KVC in den Properties einer Klasse speichern kann.

Key Value Observing ist nun die Lösung o.g. Problems: man kann sich einfach von außen als Observer für bestimmte Properties einer Klasse registrieren und wird benachrichtigt, falls sich ein Wert ändert.

Das spart eine Menge Code und die beobachtete Klasse A braucht sich um Klasse B nicht kümmern und muss keine Referenz auf B mitschleifen.

// als Observer registrieren ist einfach:
[fooInstance addObserver:self   // sag mir bescheid,
              forKeyPath:@"bar" // wenn sich bar ändert
               options:NSKeyValueObservingOptionNew
               context:NULL];
 
// der Observer muss diese methode implementieren, 
// um benachrichtigt zu werden:
- (void) observeValueForKeyPath:(NSString *)keyPath
		     ofObject:(id)object
		       change:(NSDictionary *)change
		      context:(void *)context 
{
    if ([keyPath isEqualToString:@"bar"])
        NSLog(@"new value for bar: %@", object);
}
 
// wenn man genug hat, meldet man sich wieder ab
[fooinstance removeObserver:self forKeyPath:@"bar"];

Ich finde das ziemlich elegant. Das Klassendesign bleibt sauber und trotzdem ist es möglich, sich kreuz und quer durch alle Schichten der Anwendung über Zustandsänderungen benachrichtigen zu lassen.

Update: Es gibt einen sehr schönen Wrapper von Andy Matuschak: Block Callbacks for Cocoa Observers (via Ole Bergmanns hoch interessantem Twitterstream)

KategorienApfel, Nerd Tags:

Der Einstieg in die iPhone / iPad Programmierung

13. Februar 2011 1 Kommentar

Ich lese mich gerade in die Welt der iOS-Programmierung ein, “for fun and profit” sozusagen. Die Hürde bei der Plattform ist etwas höher, weil man erstmal eine neue Programmiersprache (Objective C) lernen muss und zusätzlich die ganze Cocoa-API verstehen muss. Ich mache Dinge gerne gründlich, deswegen habe ich mir erstmal die Sprache an sich zu Brust genommen um dann in die iPhone-Programmierung einzusteigen.

Objective C macht auf mich erstmal einen sehr aufgeräumten Eindruck. Ich habe vorher C, Java und etwas C++ programmiert und von den Sprachen findet man einen guten Mix in Objective C wieder. Die umfangreiche Foundation- API ist vergleichbar mit dem, was man in Java vorfindet, trotzdem kann man bis auf C-Ebene hinunter. Die Objektorientierung ist angenehm gelöst, es heisst alles nur ein bisschen anders. So sind Protokolle z.B. das was man in Java Interfaces nennt.

Die Syntax ist etwas anstrengend, man merkt der Sprache deutlich an, dass die ersten Versionen vermutlich im C-Präprozessor implementiert wurden. Die vielen eckigen Klammern sind zwar erstmal ungewohnt, aber damit kommt man relativ schnell klar. Schlimmer ist, dass mit Objective C 2.0 ein Bruch erfolgte und Setter/Getter jetzt mit einem Punkt angesprochen werden können. Das spart zwei eckige Klammern ein, wirkt aber ziemlich inkonsistent.

Insgesamt ist die Sprache sehr mächtig, es gibt schöne dynamische Features. So kann man einer Klasse nachträglich Methoden hinzufügen (Categories), es gibt Dictionaries, wie man sie aus Python kennt und man kann auch mit dem for x in y Konstrukt durch solche assoziativen Speicher durchiterieren:

for (NSString *key in dictionary )
  NSLog (@”%@: %@”, key, [glossary objectForKey: key]);

Als Buch kann ich Programming in Objective-C 2.0* von Stephen Kochan empfehlen. Das Buch hat den Anspruch auch für Menschen geeignet zu sein, die bisher noch nicht in C programmiert haben. Trotzdem vermittelt es alle wichtigen Aspekte in der zu erwartenden Tiefe.

Was die Literatur zur “richtigen” Programmierung unter iOS angeht: der Markt ist erstmal sehr unübersichtlich. Es gibt viele Bücher, aber welches davon ist gut? Ziemlich hoch gelobt wurde in letzter Zeit IPhone Programming: The Big Nerd Ranch Guide*. Wer sich die 3500 Euro für ein 7-Tage Training auf der Big Nerd Ranch nicht leisten kann, ist mit dem Buch gut beraten.
Man merkt deutlich, dass es aus Schulungsunterlagen hervorgegangen ist, aber das ist kein Manko, im Gegenteil. Man hat sofort Erfolgserlebnisse und muss nicht erst 200 Seiten Urschleim wälzen, bevor man einen Button programmieren darf. Ich kann das Buch fast uneingeschränkt empfehlen. Einziger Kritikpunkt ist, dass es für meinen Geschmack etwas Tiefgang missen lässt. Ich habe das Buch zur Hälfte durch und bis jetzt wurde das Thema Speicherverwaltung, das allen iOS-Anfängern das Genick bricht, sträflich vernachlässigt.

Zur iOS-Umgebung selbst kann ich noch nicht so viel sagen. Es wirkt alles sehr durchdacht, allerdings muss man sich das lineare Denken abgewöhnen. Jeder Pups wird über einen Callback erledigt (er heisst nur Delegate), was einfache Probleme ein bisschen verkompliziert. Aber ich denke das ist nur Übungssache.

Die Dokumentation ist mindestens auf dem Niveau der MSDN, wenn nicht sogar besser. Man kann Beispiele mit einem Knopfdruck direkt in Xcode öffnen. Die IDE ist allerdings eine Zumutung, Xcode 3 ist ein Wirrwar von Fenstern, die man ständig zur Seite schieben muss. Ich frage mich welcher ADS-Patient für das Interface-Design verantwortlich ist.
Xcode 4 dürfte die nächsten Wochen das Licht der Welt erblicken, hier findet sich alles in einem iTunes-Artigen Fenster wieder, was sich wesentlich angenehmer anfühlt. Allerdings haben die Previews von Xcode 4 noch ein paar hässliche Bugs, so lange die Final noch nicht draußen ist, würde ich Anfängern raten, bei Xcode 3 zu bleiben.

Was braucht man, um erstmal loszulegen? Ein gutes Buch, einen intel-Mac / Hackintosh und früher oder später ein “richtiges” iOS-Gerät. Es genügt ein alter iPod-Touch, aber ohne geht es kaum. Der Simulator kann bestimmte Dinge nicht (Gravitationssensor, Kamera) und verhält sich auch manchmal ein klein bisschen anders (ich hatte speziell bei der Maps-API Probleme). Danke an Semmi, der mir so selbstlos seinen iPod Touch zur Verfügung gestellt hat. Ich verspreche, Du bekommst ihn noch in diesem Leben zurück ;-)

Als Entwickler kann man kann nur Anwendungen auf iOS Geräten installieren, wenn man ein Entwicklerzertifikat hat, das kostet pro Jahr 79 Euro. Das ist die einzige Kröte, die man schlucken muss. Ob das gerechtfertigt ist, vermag ich nicht zu sagen, aber man kann sich so ein Zertifikat auch teilen und dann bis zu 100 iOS-Geräte bestücken. Das klappt natürlich nur mit Leuten, denen man halbwegs vertrauen kann.

*=affiliate Link

KategorienNerd Tags:

OH HAI new job

27. Januar 2011 4 Kommentare

Der letzte Chaos Communication Congress hat mir einen neuen Job beschert :) Interessant ist auch, wie schnell so etwas gehen kann. Einen Monat nach dem ersten informellen Gespräch auf dem Kongress habe ich am Montag meinen letzten Arbeitstag in der alten Firma. Wie alles im Leben sind eben auch Kündigungsfristen verhandelbar.

KategorienGeneral Tags:
Pages: 1 2 3 4 5 6 7 8 ... 92 93 94 >>>