In diesem Artikel sind Informationen gesammelt, die zu einer erfolgreichen Einreichung einer App im Apple App Store benötigt werden. Außerdem finden sich hier Informationen, wie sich die Darstellung einer App auf der Produktseite optimieren lässt. Apple bietet hierzu verschiedene optionale Texte an, die je nach Bedarf für bestimmte Zwecke eingesetzt werden können.

App Name

App Name 30 Zeichen – einfach – leicht zu merken

Einen möglichst einfachen App Namen zu finden, der noch dazu leicht zu merken ist und der darauf schließen lässt, wozu eine App verwendet werden kann, ist immer eine Herausforderung. Der Aufwand zahlt sich allerdings aus, da der Name im Wesentlichen dazu beiträgt, wie Benutzer eine App im App Store finden.

Der Name einer App wird auf der Produktseite im App Store auf allen Geräten angezeigt. Er ist eines der Merkmale, anhand dessen die Benutzer eine App wiedererkennen können. Daher sollte bei der Wahl des Namens auf zu generische Bezeichnungen und Namen, die ähnlich zu bestehenden Apps sind, verzichtet werden.

Untertitel (optional)

30 Zeichen – iOS 11 only – ein Satz

Unterhalb des Namens kann auf Geräten ab iOS 11 der Untertitel angezeigt werden. Im Idealfall fasst er die App in einem Satz zusammen, hebt besondere Funktionen der App hervor oder beschreibt den Nutzungskontext der App für potentielle Käufer. Der Untertitel kann mit jeder Aktualisierung der App Version geändert werden.

Kategorien

Zwei Kategorien – Primary & Secondary

Eine App kann zwei Kategorien zugeordnet werden. Unter diesen Kategorien kann eine App beim Stöbern im App Store oder beim Filtern von Suchergebnissen gefunden werden.
App Store Kategorien
Die Hauptkategorie hat Einfluss darauf, ob die App unter „Apps“ oder „Spiele“ angezeigt wird. Die Kategorie sollte entsprechend der Hauptfunktionalitäten der App gewählt werden und diese widerspiegeln. Alle möglichen Kategorien, sowie zusätzliche Informationen sind hier zu finden: https://developer.apple.com/app-store/categories/

App Icon

Aussagekräftig – Einfach – keine Fotos, Texte oder Details

Der erste Eindruck zählt – Diese Aussage gilt auch für eine Produktseite im App Store. Das App Icon ist neben dem Namen der App eine der ersten Informationen, die der Nutzer zur App bekommt. Daher sollte das App Icon einen positiven und starken ersten Eindruck machen, auch, um den Wiedererkennungswert der App zu stärken.

Ein aussagekräftiges App Icon sollte den Nutzen und Zweck der App kommunizieren, ohne dabei zu sehr ins Detail zu gehen. Letzteres ist essentiell, da das App Icon auf den verschiedenen Geräten in unterschiedlichen Größen dargestellt wird. Zu kleine grafische Elemente, Fotos oder Text lassen sich dann meistens nur schwer erkennen.

Ein unangemessenes App Icon kann ein Grund sein, warum eine App das App Store Review von Apple nicht besteht. Eine App kann abgelehnt werden, wenn beispielsweise ein Android-ähnliches Gerät auf dem App Icon zu sehen ist.

Vorschau Video (optional)

Maximal 30 Sekunden – Ohne Ton – Bis zu drei pro Sprache

Ein Vorschau Video einer App wird als erstes Element neben den Screenshots angezeigt. Es sollte die App und die Hauptfunktionalität in maximal 30 Sekunden erklären. Es wird direkt auf der App Store-Seite, allerdings ohne Ton, abgespielt. Daher sollte es grafisch möglichst ansprechend aufbereitet sein. Auf der Produktseite können bis zu drei Vorschau Videos angezeigt werden. Pro Sprache der App im App Store, können ein bis drei Vorschauvideos hochgeladen werden.

Screenshots

Maximal fünf pro Gerät & Sprache – Visuelle Aufbereitung der Funktionen

Sofern kein Vorschauvideo der App auf der Produktseite präsentiert wird, sind die Screenshots ein essentielles Element, um die User Experience der App für den Benutzer visuell zu präsentieren. Es können bis zu fünf Screenshots pro Gerät und Sprache hochgeladen werden.

Die Screenshots sollten den Funktionsumfang der App visualisieren. Dazu kann es manchmal auch hilfreich sein, nicht nur simple Screenshots, sondern auch aufbereitete Grafiken zu präsentieren. Diese Grafiken können dann Screenshots im Gerät sowie Text beinhalten, der die Screenshots inhaltlich nochmal unterstreicht. Die Gesamtgröße und -anzahl der Grafiken richten sich nach den gleichen Vorgaben wie die einfachen Screenshots.

Unpassende Screenshots sind erfahrungsgemäß einer der häufigsten Gründe, warum Apple eine App im Review für den App Store ablehnt. Auf folgende Punkte sollte geachtet werden, um die Ablehnung der App zu vermeiden:

  • Die Geräte in den Grafiken sollten zur Gerätegröße passen. Auf einer Grafik für das iPhone X sollte dementsprechend auch ein iPhone X zu sehen sein und kein iPhone 7. Auch der Screenshot des Geräts sollte ein Original-Screenshot der Gerätegröße sein.
  • Es sollten keine Screenshots der Android App oder Android Geräte verwendet werden
  • Die Screenshots sollten die App bei der Benutzung zeigen. Splash- und Login-Screens sollten hier nicht gezeigt werden.

Beschreibung

App Beschreibung Mindestens 700, maximal 4000 Zeichen – kurz & aussagekräftig – keine Keywords oder Preise – Aktualisierung bei App Update

Die Beschreibung einer App sollte kurz und aussagekräftig formuliert sein. Inhaltlich sollte sie die Besonderheiten und Funktionalitäten hervorheben. Dies lässt sich beispielsweise durch einen kurzen informativen Abschnitt, auf den eine Liste der Hauptfunktionalitäten folgt, erreichen. Für potentielle Nutzer sollte nochmal hervor gehoben werden, was die App so einzigartig macht. Auf unnötige Häufungen von Keywords und vor allem auf die Nennung von Preisen sollte hier verzichtet werden, da diese Angaben je nach Region und Land unterschiedlich sein können.

Die Tonalität und Sprache der Beschreibung sollte entsprechend der eigenen Markenidentität gewählt werden. Gleichzeitig sollte aber auch auf eine Ausdrucksweise geachtet werden, die für die eigene Zielgruppe angemessen und verständlich ist.
Der erste Satz der Beschreibung wird immer auf der Produktseite angezeigt und kann direkt gelesen werden. Daher sollte auf ihn ein besonderes Augenmerk gelegt werden, um den Nutzer möglichst zum Weiterlesen zu bewegen und sein Interesse zu wecken.

Werbetext (optional)

170 Zeichen – Unabhängig vom App Update

Für wichtige Informationen, die unabhängig von einem App Update mit den App-Nutzern geteilt werden sollen, sollte der Werbetext eingesetzt werden. Beispielsweise können Neuigkeiten rund um die App, Ankündigungen neuer Funktionen, Inhalte oder Events durch den Werbetext bekannt gemacht werden.
Der Werbetext wird über der Beschreibung angezeigt und kann bis zu 170 Zeichen lang sein.

Keywords

100 Zeichen – Trennung durch Komma, ohne Leerzeichen

Keywords bestimmen maßgeblich, wie leicht eine App in den Suchergebnissen zu finden ist. Daher sollten die Keywords bewusst ausgewählt werden. Keywords sollten möglichst spezifische Wörter sein, welche die eigene Zielgruppe verwenden könnte, um nach einer App wie der Eigenen im App Store zu suchen. Vermieden werden sollten Wörter, die nicht relevante Informationen beinhalten, die zu offen formuliert sind oder nicht im Kontext zu der App stehen, da die eigene App ansonsten nur schwer zu finden ist.

100 Zeichen für Keywords erscheinen zunächst sehr wenig. Durch folgende Tipps kann die Anzahl der Wörter im Rahmen der vorgegeben Zeichen maximiert werden:

  • Keine Dopplungen von Wörtern, die bereits im Singular/Plural aufgeführt sind
  • Keine Verwendung von App Store-Kategorien und das Wort „App“
  • Keine doppelte Verwendung von Wörtern

Unangemessene Keywords sind ein häufiger Grund für Ablehnungen im App Store. Folgende Tipps können dabei helfen, damit unangemessene Keywords zu vermieden werden:

  • Keine unberechtigte Verwendung von Markennamen, berühmten Personen oder anderen geschützten Wörtern und Phrasen
  • Unwichtige Bezeichnungen weglassen, die für die eigene App nicht relevant sind
  • Keine App-Bezeichnungen die in Konkurrenz zueinanderstehen
  • Keine beleidigende oder anstößige Begriffe

What’s New (optional)

Neue App Funktionen 4000 Zeichen – Verbesserungen – Neue Funktionen

Nach einer Aktualisierung der App-Version kann dieser Bereich genutzt werden, um die Änderungen und Anpassungen einer App an die Nutzer zu kommunizieren. Beispielsweise können hier Verbesserungen erwähnt werden, die aus Nutzer-Feedback resultieren oder neue Funktionen in der Reihenfolge ihrer Wichtigkeit präsentiert werden.

In-App Käufe (optional)

Maximal 20 Elemente – Methode: SKPaymentTransactionObserver aktiviert?

Neben den Informationen rund um eine App, werden auf der Produktseite auch In-App Käufe angezeigt, die Benutzer hier und in der App tätigen können. Diese In-App-Käufe sowie Abonnements werden in verschiedenen Sektionen angezeigt. Es können maximal 20 Elemente, die jeweils aus einem Namen, einem Produktbild sowie einer Beschreibung bestehen, angezeigt werden. Die Reihenfolge der Elemente ist dabei frei wählbar. Hierdurch können bestimmte Inhalte gezielt hervorgehoben werden. Außer auf der Produktseite können die In-App-Käufe auch in den Suchergebnissen gefunden, sowie auf verschiedenen Seiten im App Store gefeatured werden. Dabei muss die App folgende Methode unterstützen, da ansonsten die In-App-Käufe nicht im App Store angezeigt werden: SKPaymentTransactionObserver.

Bewertungen und Reviews

App Store App Bewertungen Auf Fragen/Kritik reagieren – Nutzerfeedback zur Verbesserung einholen

Eine App kann von jedem Nutzer, der die App heruntergeladen oder gekauft hat, bewertet werden. Zusätzlich dazu kann jeder Benutzer ein Review der App schreiben. Diese Reviews können direkt beantwortet werden, um beispielsweise auf Fragen, Kritik oder andere Anliegen direkt zu reagieren. Wenn die Beantwortung der Fragen direkt über iTunes Connect vorgenommen werden, erhalten die Benutzer Benachrichtigungen und können ihre Reviews aktualisieren.

Das Updaten von Reviews und Antworten ist generell jederzeit möglich. Allerdings werden lediglich die neuesten Reviews und Antworten eines Benutzers auf der Produktseite angezeigt. Bewertungen, die auf der Produktseite angezeigt werden, sind pro Region/Gebiet spezifisch. Es gibt in iTunes Connect außerdem die Möglichkeit, die Bewertungen zurückzusetzen. Allerdings ist das lediglich bei einer Aktualisierung der App möglich. In manchen Fällen kann es durchaus sinnvoll sein, die Bewertungen zurückzusetzen. Eine geringe Anzahl von Bewertungen kann allerdings auch einen negativen Einfluss auf das Kauf- und Downloadverhalten der Nutzer haben. Daher empfiehlt Apple dies nur sehr sparsam und bewusst einzusetzen.

Posted in Technical | Leave a comment

1. Einleitung

Mit diesem Blogbeitrag soll ein Einblick in die Verwendung von Smart Pointern (Shared Pointern in C++) und die wichtigsten Grundlagen gegeben werden. Die nachfolgenden Erläuterungen zielen darauf ab, einen schnellen Einstieg in diese Thematik zu ermöglichen.

So wird im Einführungskapitel zunächst die Notwendigkeit von Shared Pointern verdeutlicht und zu deren Verwendung motiviert. Nicht ohne Grund sind Shared Pointer bei einigen höheren Programmiersprachen wie z.B. das ARC (Automatic Reference Counting) bei Objective-C ein Standard Sprach-Feature der Programmiersprache.
Smart Pointer in C++
Lesern, die bereits Grundkenntnisse besitzen und sich für ein lauffähiges Code Snippet interessieren, empfehlen wir, direkt bei Kapitel 2 Grundlagen der shared_ptr einzusteigen. Darin wird anhand von Beispielen die Verwendung der Shared Pointer erklärt.

1.1 Einführung – Das Mysterium der Shared Pointer in C++

“Warum kann man nicht einfach Shared Pointer verwenden? Shared Pointer sind eine sehr schlaue Methode zur Verwaltung von Pointern und des Speichers.”

Diese Aussage hört man sehr häufig. Aber werden Shared Pointer bereits wirklich im größeren Stil und konsequent in Projekten eingesetzt? Im Austausch mit Entwicklern gewinnt man schnell den Eindruck, dass Shared Pointer nicht besonders weit verbreitet oder populär sind. Woran liegt das? Gibt es Probleme, die bei der Verwendung häufiger auftreten? Sind Shared Pointer zu kompliziert zu verstehen oder ist es zu aufwendig, diese zu benutzen?

Wir hoffen, dass dieser Beitrag dabei helfen wird, einige der Fragen um das Mysterium Shared Pointer zu klären und ein gutes Beispiel dafür geben zu können, wie man diese korrekt verwendet.

1.2 Einführung – Eine Analogie zu Shared Pointern

Anstatt direkt in technische Details zu springen, wird die Verwendung von Shared Pointern zunächst anhand eines Beispiels aus dem echten Leben erläutert:

Stellen Sie sich vor, Sie arbeiten in einer Firma und es gibt eine einfache Regelung für das Bürogebäude: der letzte Mitarbeiter, welcher das Gebäude verlässt, muss die Eingangstür zum Gebäude verriegeln und die Alarmanlage aktivieren. Falls jemand das Gebäude verlässt und die Alarmanlage ist bereits aktiviert, dann wird der Alarm ausgelöst. Deswegen sollten Sie hier genau prüfen, ob sich noch jemand anderes im Gebäude befindet, bevor Sie die Alarmanlage aktiv schalten.

Angenommen, das Bürogebäude besteht nur aus einem kleinen Zimmer und es gibt nur fünf Mitarbeiter, dann ist es für Sie sehr einfach festzustellen, ob Sie der letzte Mitarbeiter sind, der sich noch im Gebäude befindet. Stellen Sie sich jedoch vor, Sie arbeiten in einem riesigen Bürogebäude mit mehreren Stockwerken und vielen hunderten Mitarbeitern. Um hier noch zu wissen, ob Sie die letzte Person im Gebäude sind, müssten Sie alle Stockwerke und alle Zimmer absuchen. Nun überlegen Sie sich, jemand anderes sucht zur gleichen Zeit das Gebäude ab, um nachzusehen, ob noch jemand anderes im Gebäude ist und sie beide verfehlen sich dadurch. Am Ende schließen Sie aus Versehen die andere Person im Gebäude ein, mit aktivierter Alarmanlage. Was für ein Chaos!

Aber was wäre, wenn es eine Art “Smart Door”, also eine Art “schlaue Eingangstür” zum Gebäude gäbe, welche +1 für jede Person addiert, die das Gebäude betritt und -1 subtrahiert für jede Person, die das Gebäude verlässt. Sobald der Zählerstand also den Wert 0 erreicht, wird die Alarmanlage automatisch aktiviert.

Wäre das nicht eine großartige Lösung für dieses Problem? Die Idee, welche sich hinter den Shared Pointern verbirgt, ähnelt der “Smart Door” aus dem erläuterten Beispiel.

1.3 Einführung – Die Verwendung von C++ shared_ptr

Bei der Programmierung in C++ ist es oft nicht einfach, den Überblick über die Speicherverwaltung des gesamten Source Codes eines Projektes zu behalten. Sobald ein C++ Pointer erzeugt wurde, kommen häufig folgende Fragen auf:

  • Wann soll der Pointer wieder gelöscht werden?
  • Was, wenn ein anderer den Pointer noch benötigt?
  • Und wenn der Pointer nicht gelöscht wird, wissen andere Beteiligte dann, ob und wann der Pointer gelöscht werden soll?

Als simple Antwort auf diese Fragen folgt dann meist folgende Aussage:

“Der Pointer wird an der Stelle im Programm gelöscht, in der er als letztes verwendet wurde.”

Aber wie in dem Beispiel aus 1.2, in dem nicht eindeutig bestimmt werden kann, wieviele Leute sich in einem großen Gebäude befinden, und wo genau diese sich aufhalten, so ist es keineswegs einfach, den Überblick darüber zu behalten, wohin alle Pointer in einem Programm weitergereicht wurden.

“Woher weiß man, welches die letzte Stelle ist, an der ein Pointer verwendet wurde?”

Ein Pointer könnte unter Umständen durch mehrere Konstruktoren oder Methoden Parameter weitergereicht worden sein. Außerdem könnte es sein, dass man mit mehreren Teamkollegen zusammen am selben Source Code arbeitet.

Vergleicht man die Behandlung von Pointern in C++ mit dem Beispiel aus 1.2, dann wäre ein Aufruf des delete Befehls auf einen Pointer, für welchen der Speicher bereits freigegeben wurde, gleichbedeutend mit dem Versuch, das Gebäude zu verlassen, in welchem bereits die Alarmanlage aktiviert wurde. Sobald man versucht, die Tür von innen zu öffnen, würde die Alarmanlage ausgelöst und bei den Pointern das Programm abstürzen.

Welches die letzte Stelle ist, an der ein Pointer verwendet wurde und zu wissen zu welchem Zeitpunkt man delete aufrufen sollte, ist keine Frage die sich so einfach beantworten lässt, sofern es eine dritte Instanz gibt, welche sich um diese Aufgabe kümmert. Und mit dieser dritten Instanz kommen Shared Pointer, wie der std::shared_ptr ins Spiel.

Der Shared Pointer weiß genau, welches die letzte Stelle ist, an der ein Pointer verwendet wurde und sobald ein Pointer zum letzten Mal im gesamten Programm verwendet wurde, wird er automatisch deleted. Hierfür addiert er +1. Dies geschieht jedes Mal, wenn jemand eine Kopie erzeugt, also z.B. wenn der Shared Pointer in einer Methode oder in einem Konstruktor als Parameter übergeben wird. Des Weiteren wird immer -1 subtrahiert, wenn das Objekt, welches den Shared Pointer verwendet hat, gelöscht oder die Methode, welche den Shared Pointer verwendet hat, beendet wurde. Sobald der Wert 0 erreicht wird, wird der Pointer automatisch gelöscht und der belegte Speicher wieder freigegeben.

2. Grundlagen der shared_ptr

Nachfolgend werden nun einige Code Beispiele präsentiert. Aufgrund der Anschaulichkeit wird auch hier nochmal das Büro Beispiel aus Kapitel 1.2 aufgegriffen:

es wird zunächst eine Klasse OfficeBuilding definiert, also ein Gebäude, welches von Mitarbeitern betreten werden kann. Das bedeutet, ein OfficeBuilding kann mehrere Instanzen eines Mitarbeiter Objektes (Employee) referenzieren. Und als kleines, zusätzliches Feature gibt es in dem Gebäude ein WC (Washroom), welches von genau einem Mitarbeiter betreten werden kann.

Datenbankrelationen

2.1 Grundlagen der shared_ptr – Instanziierung eines shared_ptr

Möchte man z.B. eine Instanz eines Objektes Employee erzeugen, dann würde man normalerweise einen Pointer mit Hilfe des Schlüsselworts new instanziieren, um Heap-Speicher zu allokieren. Das Schlüsselwort delete würde man dazu verwenden, um den Speicher wieder freizugeben:

Employee  *employee1 = new Employee( 1, “Peter Parker”);
delete employee1;

Diese beiden Zeilen Code werden nun ersetzt durch die Folgende:

shared_ptr<Employee> employee1 = make_shared<Employee>(1, "Peter Parker");

Die linke Seite der Zuweisung shared_ptr<Employee> employee1 hält sich eine Instanz der Klasse Employee und anstatt das Schlüsselwort new zu verwenden, verwendet man das Schlüsselwort make_shared, um diese Instanz zu erzeugen.

Die Parameter, welche man normalerweise dem Konstruktor der Klasse Employee übergeben würde, werden stattdessen dem Ausdruck shared_ptr<Employee> (parameter1, parameter2, ...) übergeben.

Sobald der aktuelle Kontext verlassen wird, also z.B. der Kontext der momentan aufgerufenen Methode, wird der Speicher, welcher von der employee1 Instanz belegt wurde, automatisch freigegeben. Der Aufruf des delete-Befehls ist somit nicht notwendig.

2.2 Grundlagen der shared_ptr – Zugriff auf Class Members eines Shared Pointers

Angenommen, man möchte unter Verwendung der Getter Methode auf den Namen der Variablen employee1 zugreifen, dann könnte man dies tun, indem man die Variable wie einen ganz normalen C++ Pointer behandelt und den Pfeil Operator “->” verwendet:

string employeeName = employee1->getName();

Die Verwendung des Punkt Operators “.” gewährt im Gegensatz dazu den Zugriff auf hilfreiche Informationen über die shared reference wie zum Beispiel die Anzahl an Reference Counts:

long employee1UseCount = employee1.use_count();

Theoretisch betrachtet, ist es möglich, über den Shared Pointer auch noch auf den Raw Pointer zuzugreifen, welcher vom Shared Pointer verwaltet wird:

Employee *pointer = employee1.get();

Jedoch sollte ein Zugriff auf den Raw Pointer, wie in diesem Beispiel um jeden Preis vermieden werden, weil dies wieder nur zu altbekannten Problemen führt: jemand versucht, Speicher freizugeben, welcher bereits freigegeben wurde. Diese Möglichkeit soll hier nur aus Gründen der Vollständigkeit erwähnt werden.

2.3 Grundlagen der shared_ptr – Kopieren eines shared_ptr

Wie kann man eine Kopie eines shared_ptr machen, welcher auf allokierten Speicher verweist? Vergleicht man es mit normalen Pointern, dann würde man normalerweise Folgendes schreiben:

Employee  *employee1 = new Employee( 1, “Peter Parker”);
Employee  *employee1Copy = employee1;

Die erste Zeile des Codes allokiert ein Employee Objekt und lässt den Pointer employee1 auf den neu allokierten Speicher zeigen. Anschließend wird in der zweiten Zeile eine Kopie dieser Variable gemacht und die neue Variable employee1Copy verweist dann auf denselben Speicherbereich wie employee1. Wenn man etwas vergleichbares mit einem shared_ptr machen möchte, dann gibt es zwei Möglichkeiten:

shared_ptr<Employee> employee1 = make_shared<Employee>(1, "Peter Parker"); 

1) shared_ptr<Employee> employee1Copy = employee1; 
2) auto employee1Copy2 = employee1;

2.4 Wie verhält sich das Reference Counting?

Nachdem nun die Grundlagen über shared_ptr vermittelt wurden, wäre es als nächstes interessant, zu betrachten, wie sich das Reference Counting in einem richtigen Code Beispiel verhält.

Zunächst soll die Klasse Employee betrachtet werden. Ein Mitarbeiter (Employee) hat ein Attribut Name und eine eindeutige Id, welche dem Konstruktor übergeben werden kann. Diese Klasse enthält, um das Beispiel möglichst einfach zu halten, keine Pointer oder Shared Pointer.

#ifndef SHAREDPOINTEREXAMPLEAPPLICATION_EMPLOYEE_H
#define SHAREDPOINTEREXAMPLEAPPLICATION_EMPLOYEE_H

#import <vector>
#import <string>
#import <iostream>
using namespace std;

class Employee {

private:
    int id;
    string name;
public:
    Employee(int id, const string &name) : id(id), name(name){
    }

    int getId() const{
        return id;
    }

    const string &getName() const{
        return name;
    }

    void printCurrentUseCount(shared_ptr<Employee> employee){
        if(employee.use_count() > 0){

            printf("%s, use count: %li n", getName().c_str(), (employee.use_count() -1));

        } else{
            printf("use count: %li n", employee.use_count());
        }
    }
};

#endif //SHAREDPOINTEREXAMPLEAPPLICATION_EMPLOYEE_H

Um einen besseren Überblick über die Reference Counts zu haben, gibt es noch eine Methode printCurrentUseCount. Da bei jedem Aufruf der Methode ein neuer Kontext betreten wird und damit das Ergebnis um den Wert +1 “verfälscht” würde, wird hier wieder das auszugebende Ergebnis immer um -1 heruntergesetzt.

Das OfficeBuilding, in welchem die Mitarbeiter arbeiten, kann durch den Aufruf der Method enterBuilding und leaveBuilding von Mitarbeitern betreten bzw. verlassen werden. Bei genauerem Hinsehen fällt auf, dass die Klasse OfficeBuilding eine Klassenvariable vector<shared_ptr<Employee>> smartDoorPeopleCounter hat, welche sich merkt, welche Mitarbeiter sich zur Zeit im Gebäude befinden.

Über die beiden Methoden useWashRoom und leaveWashRoom kann immer genau ein Mitarbeiter das WC betreten bzw. wieder verlassen.

#ifndef SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEBUILDING_H
#define SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEBUILDING_H

#include "Employee.h"
#include "WashRoom.h"
#import <vector>
#import <string>
#include <thread>
#include <cstdlib>

class OfficeBuilding {

private:
    vector<shared_ptr<Employee>> smartDoorPeopleCounter;
    vector<WashRoom> washRoom;

public:
    void enterBuilding(shared_ptr<Employee> employeeWhoWantsToEnter) {
        smartDoorPeopleCounter.push_back(employeeWhoWantsToEnter);
        employeeWhoWantsToEnter->printCurrentUseCount(employeeWhoWantsToEnter);
    }

    void leaveBuilding(shared_ptr<Employee> employeeWhoWantsToLeave) {
        int indexToRemove = -1;
        for (int i = 0; i < smartDoorPeopleCounter.size(); i++) {
            auto currentEmployee = smartDoorPeopleCounter[i];
            if (currentEmployee->getId() == employeeWhoWantsToLeave->getId()) {
                indexToRemove = i;
            }
        }
        if (indexToRemove != -1) {
            smartDoorPeopleCounter.erase(smartDoorPeopleCounter.begin() + indexToRemove);
        }
        if (smartDoorPeopleCounter.size() == 0) {
            // activate alarm system
        }
    }

    void useWashRoom(shared_ptr<Employee> employee) {
        if (washRoom.size() == 0) {
            WashRoom emptyWashRoom;
            emptyWashRoom.setEmployee(employee);
            washRoom.push_back(emptyWashRoom);
        } else {
            //washroom is currently in use, please wait!
        }
    }

    void leaveWashRoom() {
        if (washRoom.size() == 1) {
            washRoom.clear();
        }
    }
};

#endif //SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEBUILDING_H

Das Objekt Washroom hält sich immer einen shared_ptr auf den aktuellen Mitarbeiter, welcher gerade das WC benutzt.

#ifndef SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEROOM_H
#define SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEROOM_H

#include "Employee.h"

class WashRoom {
    shared_ptr<Employee> employee;
public:
    void setEmployee(const shared_ptr<Employee> &employee) {
        WashRoom::employee = employee;
    }

    const shared_ptr<Employee> &getEmployee() const {
        return employee;
    }
};

#endif //SHAREDPOINTEREXAMPLEAPPLICATION_OFFICEROOM_H

Der folgende Beispielcode allokiert einige Mitarbeiter Objekte (Employee) und lässt diese das OfficeBuilding betreten und wieder verlassen.

OfficeBuilding officeBuilding;

/* create employee 1 */
shared_ptr<Employee> employee1 = make_shared<Employee>(1, "Peter Parker");
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 1
string employeeName = employee1->getName(); // "Peter Parker"
Employee *pointer = employee1.get(); // attention: raw pointer should not be used!!
shared_ptr<Employee>  employee1Copy = employee1;
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 2
auto employee1Copy2 = employee1;
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 3

/* create employee 2 */
shared_ptr<Employee> employee2;
employee2->printCurrentUseCount(employee2); //use count: 0
employee2 = make_shared<Employee>(2, "Bruce Wayne");
employee2->printCurrentUseCount(employee2); //Bruce Wayne, use count: 1

/* create employee 3 */
auto employee3 = make_shared<Employee>(3, "Clark Kent");
employee3->printCurrentUseCount(employee3); //Clark Kent, use count: 1

/* employees entering building */
officeBuilding.enterBuilding(employee1); //Peter Parker, use count: 5
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 4
officeBuilding.enterBuilding(employee2); //Bruce Wayne, use count: 3
employee2->printCurrentUseCount(employee2); //Bruce Wayne, use count: 2
officeBuilding.enterBuilding(employee3);//Clark Kent, use count: 3
employee3->printCurrentUseCount(employee3);//Clark Kent, use count: 2

/* employee 1 entering using washroom */
officeBuilding.useWashRoom(employee1);
employee1->printCurrentUseCount(employee1); //Peter Parker, use count: 5
officeBuilding.leaveWashRoom();
employee1->printCurrentUseCount(employee1);//Peter Parker, use count: 4

/* employee 1 leaving building */
officeBuilding.leaveBuilding(employee1);
employee1->printCurrentUseCount(employee1);//Peter Parker, use count: 3

/* employee 1 visiting coffee house */
CoffeeHouse coffeeHouse;
coffeeHouse.visitCoffeeHouse(employee1);
employee1->printCurrentUseCount(employee1);//Peter Parker, use count: 4

bool finish = true;

Die Print-Ausgaben sollen hier verdeutlichen, wie jeweils der aktuelle Reference Count ist:

Peter Parker, use count: 1 
Peter Parker, use count: 2 
Peter Parker, use count: 3 
use count: 0 
Bruce Wayne, use count: 1 
Clark Kent, use count: 1 
Peter Parker, use count: 5 
Peter Parker, use count: 4 
Bruce Wayne, use count: 3 
Bruce Wayne, use count: 2 
Clark Kent, use count: 3 
Clark Kent, use count: 2 
Peter Parker, use count: 5 
Peter Parker, use count: 4 
Peter Parker, use count: 3 
use count inside context: 5 
use count outside context: 4 
use count from thread: 6 
use count after thread finished : 5 
Peter Parker, use count: 4

2.5 Die Verwendung von shared_ptr innerhalb von Threads

Es wird vermutet, dass es bei der Verwendung von C++ Shared Pointern zu Problemen im Zusammenhang mit Threads kommen könnte. Nachfolgend auch ein Beispiel für diesen speziellen Fall: es gibt ein Cafe, welches sich außerhalb des Gebäudes OfficeBuilding befindet und welches ein Mitarbeiter (Employee) besuchen kann.

Das Cafe kann mittels der Methode visitCoffeeHouse betreten werden. In dieser Methode wird zunächst der Shared Pointer auf den Mitarbeiter einer auto Variable zugewiesen und anschließend einem Shared Pointer struct, welcher als Container für die Parameter des Threads dient.

#ifndef SHAREDPOINTEREXAMPLEAPPLICATION_COFEEHOUSE_H
#define SHAREDPOINTEREXAMPLEAPPLICATION_COFEEHOUSE_H

#include "Employee.h"
#import <vector>
#import <string>
#include <thread>
#include <cstdlib>
#include <unistd.h>

using namespace std;
static pthread_t threadID = pthread_t();

class CoffeeHouse {

private:
    shared_ptr<vector<shared_ptr<Employee>>> cafeteriaCounter;
    struct threadParams {
        shared_ptr<Employee> employeeCopy;
        shared_ptr<vector<shared_ptr<Employee>>> cafeteriaCounterCopy;
    };

    static void *visitCoffeeHouseAsync(void *context) {

        unsigned int microseconds = 5000000; // sleep for 5 seconds
        usleep(microseconds);
        auto params = *(shared_ptr<threadParams> *) context;
        auto employee =  params->employeeCopy;
        string employeeName = employee->getName();

        if (employeeName.length() > 0) {
            params->cafeteriaCounterCopy->push_back(employee);
            printf("use count from thread: %li n", (employee.use_count()));
        }

        pthread_detach(pthread_self());
        return nullptr;
    }

public:

    CoffeeHouse(){
        cafeteriaCounter = make_shared<vector<shared_ptr<Employee>>>();
    }

    void visitCoffeeHouse(shared_ptr<Employee> employee) {
        {
            shared_ptr<threadParams> params = make_shared<threadParams>();
            params->employeeCopy = employee;
            params->cafeteriaCounterCopy = cafeteriaCounter;
            printf("use count inside context: %li n", (employee.use_count())); //use count inside context: 5
            pthread_create(&threadID, NULL, visitCoffeeHouseAsync, &params);
        }

        printf("use count outside context: %li n", (employee.use_count())); //use count outside context: 4
        pthread_join(threadID, NULL);
        printf("use count after thread finished : %li n", (employee.use_count()));//use count after thread finished : 5
    }
};

#endif //SHAREDPOINTEREXAMPLEAPPLICATION_COFEEHOUSE_H

Der Thread wird innerhalb eines speziellen Kontextes gestartet, wobei der Kontext durch die geschweiften Klammern { } markiert ist. Innerhalb des Kontextes wird der useCount den Wert 5 haben, kurz bevor der Thread gestartet wird. Nachdem der Thread gestartet wurde, wird der Kontext verlassen, während der Thread für 5 Sekunden schläft und in der Zwischenzeit wird der Reference Count durch den Main Thread auf den Wert 4 verringert.

Sobald der Thread wieder erwacht, wird dieser eine lokale und eine globale Kopie des Shared Pointer machen, was zu der Ausgabe des Wertes 6 führt.
Nachdem der Thread beendet wurde, verbleibt lediglich die globale Kopie, was zu einer Ausgabe des Wertes 5 führt. Das Endergebnis nach dem Verlassen der Methode visitCoffeeHouse für den Mitarbeiter “Peter Parker” ist der Wert 4.

Auf den ersten Blick sieht mit den Threads alles relativ normal aus. Das heißt, die reference counts scheinen korrekt erhöht und verringert zu werden. Jedoch können Probleme nie völlig ausgeschlossen werden. Möchte man ganz sicher gehen, empfiehlt es sich, nur auf einer Kopie der Daten zu arbeiten.

3. Zusammenfassung

Das Konzept der C++ shared_ptr bietet eine Alternative zu den traditionellen C++ Pointern. Es ist jedoch entscheidend, niemals normale C++ Pointer und Shared Pointer zu vermischen, weil dies zu ernsthaften Problemen führen kann.

Der große Vorteil der Shared Pointer, die automatische Verwaltung von Speicher-Referenzen, ersetzt außerdem die mühselige, manuelle Speicherfreigabe. Des Weiteren wurde ein Einblick gegeben, wie sich das Reference Counting verhält. Einfach gesagt: Wann immer ein neuer Kontext betreten wird z.B. eine Methode oder eine Kopie eines Shared Pointers, wird der Reference Count um +1 erhöht. Sobald ein Kontext wieder verlassen wird, wird der Reference Count automatisch verringert, abhängig von der Anzahl der Kopien in diesem Kontext. Nach intensiver Einarbeitung in die Thematik, lässt sich festhalten, dass Shared Pointer den Entwicklungsprozess deutlich komfortabler und einfacher machen können. Auch die Kollaboration mit Teamkollegen wird durch diese deutlich erleichtert. Es dürfte spannend werden, Shared Pointer zukünftig auch in größeren Projekten einzusetzen.

Einen kleinen Nachteil könnte es dabei jedoch geben: sofern man Shared Pointer in einem Projekt verwendet und im selben Projekt auch eine externe Bibliothek wie z.B. OpenCV verwenden möchte, welche vielleicht in den Interfaces keine Shared Pointer anbietet, sollte man sehr vorsichtig damit umgehen. Möglicherweise bietet es sich an, hier dann eine “tiefe Kopie” der Daten zu verwenden, welche man als Parameter den Schnittstellen der Bibliothek übergeben möchte.

Posted in Technical | Leave a comment