Apple URL Loading System Programming Guide Manuel
Apple sur Fnac.com
- Pour voir la liste complète des manuels APPLE, cliquez ici
TELECHARGER LE PDF sur :
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.pdf
Commander un produit Apple sur Fnac.com





Voir également d'autres Guides et documentation APPLE :
Apple-TV_2nd_gen_Setup_Guide.pdf-manuel
Apple-Archives-and-Serializations-Programming-manuel
Apple-SafariWebContent.pdf-Guide-manuel
Apple-iTunes_ExtrasandiTunes_LPTestGuide1.1.pdf-manuel
Apple-Text-System-User-Interface-Layer-Programming-manuel
Apple-CocoaTextArchitecture.pdf-manuel
Apple-Key-Value-Observing-Programming-Guide-manuel
Apple-Location-Awareness-Programming-Guide-manuel
Apple-SharkUserGuide.pdf-manuel
Apple-drawingprintingios.pdf-manuel
Apple-QuickTime7_User_Guide.pdf-manuel
Apple-Event-Handling-Guide-for-iOS-manuel
Apple-ipod_nano_3rd_gen_features_guide.pdf-manuel
Apple-iTunes_VideoandAudio_Asset_Guide5.0.pdf-manuel
Apple-ARD3_AdminGuide.pdf-manuel
Apple-SafariWebContent.pdf-Guide-manue
Apple-iphone_3gs_finger_tips.pdf-manuel
Apple-InstrumentsUserGuide.pdf-manuel
Apple-Logic-Pro-9-TDM-Guide-manuel
Apple-macbook_air_users_guide.pdf-manuel
Apple-macbook_air-13-inch_mid-2012-qs_ta.pdf-manuel
Apple-AppStoreMarketingGuidelines-JP.pdf-Japon-manuel
Apple-macbook_pro_retina_qs_ta.pdf-manuel
Apple-ipad_user_guide_tu.pdf-manuel
Apple-ipad_user_guide_th.pdf-manuel
Apple-iphone_user_guide_gr.pdf-manuel
Apple-Nike_Plus_iPod_Sensor_UG_2A.pdf-manuel
Apple-ipad_manual_del_usuario.pdf-manuel
Apple-ipad_uzivatelska_prirucka.pdf-manuel
Apple-ipad_wifi_informations_importantes.pdf-manuel
Apple-Xsan_2_Admin_Guide_v2.3.pdf-manuel
Apple-macbook_pro-13-inch-late-2012-quick_start.pdf-manuel
Apple-CocoaDrawingGuide.pdf-manuel
Apple-Cryptographic-Services-Guide-manuel
Apple-Resource-Programming-Guide-manuel
AppleSafariVisualEffectsProgGuide.pdf-manuel
/Apple-WorkingWithUSB.pdf-manuel
Apple-macbook_pro-retina-mid-2012-important_product_info_f.pdf-manuel
Apple-iOS_Security_May12.pdf-manue
Apple-Mac-Pro-2008-Performance-and-Productivity-for-Creative-Pros
Apple-iPod_shuffle_4thgen_Manuale_utente.pdf-Italie-Manuel
Apple-KernelProgramming.pdf-manuel
Apple-Core-Data-Model-Versioning-and-Data-Migration-Programming-Guide-manuel
Apple-RED_Workflows_with_Final_Cut_Pro_X.pdf-manuel
Apple-Transitioning-to-ARC-Release-Notes-manuel
Apple-iTunes-Connect-Sales-and-Trends-Guide-manuel
Apple-App-Sandbox-Design-Guide-manuel
Apple-String-Programming-Guide-manuel
Apple-Secure-Coding-Guide-manuel
Apple_AirPort_Networks_Early2009.pdf-manuel
Apple-TimeCapsule_SetupGuide_TA.pdf-manuel
Apple-time_capsule_4th_gen_setup.pdf-manuel
Apple-TimeCapsule_SetupGuide.pdf-manuel
Apple-TimeCapsule_SetupGuide_CH.pdf-Chinois-manuel
Apple-CodeSigningGuide.pdf-manuel
Apple-ViewControllerPGforiOS.pdf-manuel
Apple-KeyValueObserving.pdf-manuel
Apple-mac_mini-late-2012-quick_start.pdf-manuel
Apple-OS-X-Mountain-Lion-Core-Technologies-Overview-June-2012-manuel
Apple-OS-X-Server-Product-Overview-June-2012-manuel
Apple-Apple_Server_Diagnostics_UG_109.pdf-manuel
Apple-PackageMaker_UserGuide.pdf-manuel
Apple-InstrumentsUserGuide.pdf-manuel
Apple-Logic-Pro-9-TDM-Guide-manuel
Apple-macbook_air_users_guide.pdf-manuel
Apple-macbook_air-13-inch_mid-2012-qs_ta.pdf-manuel
Apple-AppStoreMarketingGuidelines-JP.pdf-Japon-manuel
Apple-macbook_pro_retina_qs_ta.pdf-manuel
Apple-ipad_user_guide_tu.pdf-manuel
Apple-ipad_user_guide_th.pdf-manuel
Apple-iphone_user_guide_gr.pdf-manuel
Apple-Nike_Plus_iPod_Sensor_UG_2A.pdf-manuel
Apple-ipad_manual_del_usuario.pdf-manuel
Apple-ipad_uzivatelska_prirucka.pdf-manuel
Apple-ipad_wifi_informations_importantes.pdf-manuel
Apple-Xsan_2_Admin_Guide_v2.3.pdf-manuel
Apple-macbook_pro-13-inch-late-2012-quick_start.pdf-manuel
Apple-CocoaDrawingGuide.pdf-manuel
Apple-Cryptographic-Services-Guide-manuel
Apple-Resource-Programming-Guide-manuel
AppleSafariVisualEffectsProgGuide.pdf-manuel
/Apple-WorkingWithUSB.pdf-manuel
Apple-macbook_pro-retina-mid-2012-important_product_info_f.pdf-manuel
Apple-iOS_Security_May12.pdf-manue
Apple-Mac-Pro-2008-Performance-and-Productivity-for-Creative-Pros
Apple-iPod_shuffle_4thgen_Manuale_utente.pdf-Italie-Manuel
Apple-KernelProgramming.pdf-manuel
Apple-Core-Data-Model-Versioning-and-Data-Migration-Programming-Guide-manuel
Apple-RED_Workflows_with_Final_Cut_Pro_X.pdf-manuel
Apple-Transitioning-to-ARC-Release-Notes-manuel
Apple-iTunes-Connect-Sales-and-Trends-Guide-manuel
Apple-App-Sandbox-Design-Guide-manuel
Apple-String-Programming-Guide-manuel
Apple-Secure-Coding-Guide-manuel
Apple_AirPort_Networks_Early2009.pdf-manuel
Apple-TimeCapsule_SetupGuide_TA.pdf-manuel
Apple-time_capsule_4th_gen_setup.pdf-manuel
Apple-TimeCapsule_SetupGuide.pdf-manuel
Apple-TimeCapsule_SetupGuide_CH.pdf-Chinois-manuel
Apple-CodeSigningGuide.pdf-manuel
Apple-ViewControllerPGforiOS.pdf-manuel
Apple-KeyValueObserving.pdf-manuel
Apple-mac_mini-late-2012-quick_start.pdf-manuel
Apple-OS-X-Mountain-Lion-Core-Technologies-Overview-June-2012-manuel
Apple-OS-X-Server-Product-Overview-June-2012-manuel
Apple-Apple_Server_Diagnostics_UG_109.pdf-manuel
Apple-PackageMaker_UserGuide.pdf-manuel
Apple-Instrumentos_y_efectos_de_Logic_Studio.pdf-Manuel
Apple-ipod_nano_kayttoopas.pdf-Finlande-Manuel
Apple_ProRes_White_Paper_October_2012.pdf-Manuel
Apple-wp_osx_configuration_profiles.pdf-Manuel
Apple-UsingiTunesProducerFreeBooks.pdf-Manuel
Apple-ipad_manual_do_usuario.pdf-Portugais-Manuel
Apple-Instruments_et_effets_Logic_Studio.pdf-Manuel
Apple-ipod_touch_gebruikershandleiding.pdf-Neerlandais-Manuel
AppleiPod_shuffle_4thgen_Manual_del_usuario.pdf-Espagnol-Manuel
Apple-Premiers-contacts-avec-votre-PowerBook-G4-Manuel
Apple_Composite_AV_Cable.pdf-Manuel
Apple-iPod_shuffle_3rdGen_UG_DK.pdf-Danemark-Manuel
Apple-iPod_classic_160GB_Benutzerhandbuch.pdf-Allemand-Manuel
Apple-VoiceOver_GettingStarted-Manuel
Apple-iPod_touch_2.2_Benutzerhandbuch.pdf-Allemand-Manuel
Apple-Apple_TV_Opstillingsvejledning.pdf-Allemand-Manuel
Apple-iPod_shuffle_4thgen_Manuale_utente.pdf-Italie-Manuel
Apple-iphone_prirucka_uzivatela.pdf-Manuel
Apple-Aan-de-slag-Neerlandais-Manuel
Apple-airmac_express-80211n-2nd-gen_setup_guide.pdf-Thailande-Manuel
Apple-ipod_nano_benutzerhandbuch.pdf-Allemand-Manuel
Apple-aperture3.4_101.pdf-Manuel
Apple-Pages09_Anvandarhandbok.pdf-Manuel
Apple-nike_plus_ipod_sensor_ug_la.pdf-Mexique-Manuel
Apple-ResEdit-Reference-For-ResEdit02.1-Manuel
Apple-ipad_guide_de_l_utilisateur.pdf-Manuel
Apple-Compressor-4-Benutzerhandbuch-Allemand-Manuel
Apple-AirPort_Networks_Early2009_DK.pdf-Danemark-Manuel
Apple-MacBook_Pro_Mid2007_2.4_2.2GHz_F.pdf-Manuel
Apple-MacBook_13inch_Mid2010_UG_F.pdf-Manuel
Apple-Xserve-RAID-Presentation-technologique-Janvier-2004-Manuel
Apple-MacBook_Pro_15inch_Mid2010_F.pdf-Manuel
Apple-AirPort_Express-opstillingsvejledning.pdf-Danemark-Manuel
Apple-DEiPod_photo_Benutzerhandbuch_DE0190269.pdf-Allemand-Manuel
Apple-Final-Cut-Pro-X-Logic-Effects-Reference-Manuel
Apple-iPod_touch_2.1_Brugerhandbog.pdf-Danemark-Manuel
Apple-Remote-Desktop-Administratorhandbuch-Version-3.1-Allemand-Manuel
Apple-Qmaster-4-User-Manual-Manuel
Apple-Server_Administration_v10.5.pdf-Manuel
Apple-ipod_classic_features_guide.pdf-Manuel
Apple-Lecteur-Optique-Manuel
Apple-Carte-AirPort-Manuel
Apple-iPhone_Finger_Tips_Guide.pdf-Anglais-Manuel
Apple-Couvercle-Manuel
Apple-battery.cube.pdf-Manuel
Apple-Boitier-de-l-ordinateur-Manuel
Apple-Pile-Interne-Manuel
Apple-atacable.pdf-Manuel
Apple-videocard.pdf-Manuel
Apple-Guide_de_configuration_de_l_Airport_Express_5.1.pdf-Manuel
Apple-iMac_Mid2010_UG_F.pdf-Manuel
Apple-MacBook_13inch_Mid2009_F.pdf-Manuel
Apple-MacBook_Mid2007_UserGuide.F.pdf-Manuel
Apple-Designing_AirPort_Networks_10.5-Windows_F.pdf-Manuel
Apple-Administration_de_QuickTime_Streaming_et_Broadcasting_10.5.pdf-Manuel
Apple-Opstillingsvejledning_til_TimeCapsule.pdf-Danemark-Manuel
Apple-iPod_nano_5th_gen_Benutzerhandbuch.pdf-Manuel
Apple-iOS_Business.pdf-Manuel
Apple-AirPort_Extreme_Installationshandbuch.pdf-Manuel
Apple-Final_Cut_Express_4_Installation_de_votre_logiciel.pdf-Manuel
Apple-MacBook_Pro_15inch_2.53GHz_Mid2009.pdf-Manuel
Apple-Network_Services.pdf-Manuel
Apple-Aperture_Performing_Adjustments_f.pdf-Manuel
Apple-Supplement_au_guide_Premiers_contacts.pdf-Manuel
Apple-Administration_des_images_systeme_et_de_la_mise_a_jour_de_logiciels_10.5.pdf-Manuel
Apple-Mac_OSX_Server_v10.6_Premiers_contacts.pdf-Francais-Manuel
Apple-Designing_AirPort_Networks_10.5-Windows_F.pdf-Manuel
Apple-Mise_a_niveau_et_migration_v10.5.pdf-Manue
Apple-MacBookPro_Late_2007_2.4_2.2GHz_F.pdf-Manuel
Apple-Mac_mini_Late2009_SL_Server_F.pdf-Manuel
Apple-Mac_OS_X_Server_10.5_Premiers_contacts.pdf-Manuel
Apple-iPod_touch_2.0_Guide_de_l_utilisateur_CA.pdf-Manuel
Apple-MacBook_Pro_17inch_Mid2010_F.pdf-Manuel
Apple-Comment_demarrer_Leopard.pdf-Manuel
Apple-iPod_2ndGen_USB_Power_Adapter-FR.pdf-Manuel
Apple-Feuille_de_operations_10.4.pdf-Manuel
Apple-Time_Capsule_Installationshandbuch.pdf-Allemand-Manuel
Apple-F034-2262AXerve-grappe.pdf-Manuel
Apple-Mac_Pro_Early2009_4707_UG_F
Apple-imacg5_17inch_Power_Supply
Apple-Logic_Studio_Installieren_Ihrer_Software_Retail
Apple-IntroductionXserve1.0.1
Apple-Aperture_Getting_Started_d.pdf-Allemand
Apple-getting_started_with_passbook
Apple-iPod_mini_2nd_Gen_UserGuide.pdf-Anglais
Apple-Deploiement-d-iPhone-et-d-iPad-Reseaux-prives-virtuels
Apple-F034-2262AXerve-grappe
Apple-Mac_OS_X_Server_Glossaire_10.5
Apple-FRLogic_Pro_7_Guide_TDM
Apple-iphone_bluetooth_headset_userguide
Apple-Administration_des_services_reseau_10.5
Apple-imacg5_17inch_harddrive
Apple-iPod_nano_4th_gen_Manuale_utente
Apple-iBook-G4-Getting-Started
Apple-XsanGettingStarted
Apple-Mac_mini_UG-Early2006
Apple-Guide_des_fonctionnalites_de_l_iPod_classic
Apple-Guide_de_configuration_d_Xsan_2
Apple-MacBook_Late2006_UsersGuide
Apple-sur-Fnac.com
Apple-Mac_mini_Mid2010_User_Guide_F.pdf-Francais
Apple-PowerBookG3UserManual.PDF.Anglais
Apple-Installation_de_votre_logiciel_Logic_Studio_Retail
Apple-Pages-Guide-de-l-utilisateur
Apple-MacBook_Pro_13inch_Mid2009.pdf.Anglais
Apple-MacBook_Pro_15inch_Mid2009
Apple-Installation_de_votre_logiciel_Logic_Studio_Upgrade
Apple-FRLogic_Pro_7_Guide_TDM
Apple-airportextreme_802.11n_userguide
Apple-iPod_shuffle_3rdGen_UG
Apple-iPod_classic_160GB_User_Guide
Apple-iPod_nano_5th_gen_UserGuide
Apple-ipod_touch_features_guide
Apple-Wireless_Mighty_Mouse_UG
Apple-Advanced-Memory-Management-Programming-Guide
Apple-iOS-App-Programming-Guide
Apple-Concurrency-Programming-Guide
Apple-MainStage-2-User-Manual-Anglais
Apple-iMacG3_2002MultilingualUserGuide
Apple-iBookG3_DualUSBUserGuideMultilingual.PDF.Anglais
Apple-imacG5_20inch_AirPort
Apple-Guide_de_l_utilisateur_de_Mac_Pro_Early_2008
Apple-Installation_de_votre_logiciel_Logic_Express_8
Apple-iMac_Guide_de_l_utilisateur_Mid2007
Apple-imacg5_20inch_OpticalDrive
Apple-FCP6_Formats_de_diffusion_et_formats_HD
Apple-prise_en_charge_des_surfaces_de_controle_logic_pro_8
Apple-Aperture_Quick_Reference_f
Apple-Shake_4_User_Manual
Apple-aluminumAppleKeyboard_wireless2007_UserGuide
Apple-ipod_shuffle_features_guide
Apple-Color-User-Manual
Apple-XsanGettingStarted
Apple-Migration_10.4_2e_Ed
Apple-MacBook_Air_SuperDrive
Apple-MacBook_Late2007-f
ApplePowerMacG5_(Early_2005)_UserGuide
Apple-iSightUserGuide
Apple-MacBook_Pro_Early_2008_Guide_de_l_utilisateur
Apple-Nouvelles-fonctionnalites-aperture-1.5
Apple-premiers_contacts_2e_ed_10.4.pdf-Mac-OS-X-Server
Apple-premiers_contacts_2e_ed_10.4
Apple-eMac_2005UserGuide
Apple-imacg5_20inch_Inverter
Apple-Keynote2_UserGuide.pdf-Japon
Apple-Welcome_to_Tiger.pdf-Japon
Apple-XsanAdminGuide_j.pdf-Japon
Apple-PowerBookG4_UG_15GE.PDF-Japon
Apple-Xsan_Migration.pdf-Japon
Apple-Xserve_Intel_DIY_TopCover_JA.pdf-Japon
Apple-iPod_nano_6thgen_User_Guide_J.pdf-Japon
Apple-Aperture_Photography_Fundamentals.pdf-Japon
Apple-nikeipod_users_guide.pdf-Japon
Apple-QuickTime71_UsersGuide.pdf-Japon
Apple-iMacG5_iSight_UG.pdf-Japon
Apple-Aperture_Performing_Adjustments_j.pdf-Japon
Apple-iMacG5_17inch_HardDrive.pdf-Japon
Apple-iPod_shuffle_Features_Guide_J.pdf-Japon
Apple-MacBook_Air_User_Guide.pdf-Japon
Apple-MacBook_UsersGuide.pdf-Japon
Apple-iPad_iOS4_Brukerhandbok.pdf-Norge-Norvege
Apple-Apple_AirPort_Networks_Early2009_H.pd-Norge-Norvege
Apple-iPod_classic_120GB_no.pdf-Norge-Norvege
Apple-StoreKitGuide.pdf-Japon
Apple-Xserve_Intel_DIY_ExpansionCardRiser_JA.pdf-Japon
Apple-iMacG5_Battery.pdf-Japon
Apple-Logic_Pro_8_Getting_Started.pdf-Japon
Apple-PowerBook-handbok-Norge-Norveg
Apple-iWork09_formler_og_funksjoner.pdf-Norge-Norvege
Apple-MacBook_Pro_15inch_Mid2010_H.pdf-Norge-Norvege
Apple-MacPro_HardDrive_DIY.pdf-Japon
Apple-iPod_Fifth_Gen_Funksjonsoversikt.pdf-Norge-Norvege
Apple-MacBook_13inch_white_Early2009_H.pdf-Norge-Norvege
Apple-GarageBand_09_Komme_i_gang.pdf-Norge-Norvege
Apple-MacBook_Pro_15inch_Mid2009_H.pdf-Norge-Norvege
Apple-imac_mid2011_ug_h.pdf-Norge-Norvege
Apple-iDVD_08_Komme_i_gang.pdf-Norge-Norvege
Apple-MacBook_Air_11inch_Late2010_UG_H.pdf-Norge-Norvege
Apple-iMac_Mid2010_UG_H.pdf-Norge-Norvege
Apple-MacBook_13inch_Mid2009_H.pdf-Norge-Norvege
/Apple-iPhone_3G_Viktig_produktinformasjon_H-Norge-Norvege
Apple-MacBook_13inch_Mid2010_UG_H.pdf-Norge-Norvege
Apple-macbook_air_13inch_mid2011_ug_no.pdf-Norge-Norvege
Apple-Mac_mini_Early2009_UG_H.pdf-Norge-Norvege
Apple-ipad2_brukerhandbok.pdf-Norge-Norvege
Apple-iPhoto_08_Komme_i_gang.pdf-Norge-Norvege
Apple-MacBook_Air_Brukerhandbok_Late2008.pdf-Norge-Norvege
Apple-Pages09_Brukerhandbok.pdf-Norge-Norvege
Apple-MacBook_13inch_Late2009_UG_H.pdf-Norge-Norvege
Apple-iPhone_3GS_Viktig_produktinformasjon.pdf-Norge-Norvege
Apple-MacBook_13inch_Aluminum_Late2008_H.pdf-Norge-Norvege
Apple-Wireless_Keyboard_Aluminum_2007_H-Norge-Norvege
Apple-NiPod_photo_Brukerhandbok_N0190269.pdf-Norge-Norvege
Apple-MacBook_Pro_13inch_Mid2010_H.pdf-Norge-Norvege
Apple-MacBook_Pro_17inch_Mid2010_H.pdf-Norge-Norvege
Apple-Velkommen_til_Snow_Leopard.pdf-Norge-Norvege.htm
Apple-TimeCapsule_Klargjoringsoversikt.pdf-Norge-Norvege
Apple-iPhone_3GS_Hurtigstart.pdf-Norge-Norvege
Apple-Snow_Leopard_Installeringsinstruksjoner.pdf-Norge-Norvege
Apple-iMacG5_iSight_UG.pdf-Norge-Norvege
Apple-iPod_Handbok_S0342141.pdf-Norge-Norvege
Apple-ipad_brukerhandbok.pdf-Norge-Norvege
Apple-GE_Money_Bank_Handlekonto.pdf-Norge-Norvege
Apple-MacBook_Air_11inch_Late2010_UG_H.pdf-Norge-Norvege
Apple-iPod_nano_6thgen_Brukerhandbok.pdf-Norge-Norvege
Apple-iPod_touch_iOS4_Brukerhandbok.pdf-Norge-Norvege
Apple-MacBook_Air_13inch_Late2010_UG_H.pdf-Norge-Norvege
Apple-MacBook_Pro_15inch_Early2011_H.pdf-Norge-Norvege
Apple-Numbers09_Brukerhandbok.pdf-Norge-Norvege
Apple-Welcome_to_Leopard.pdf-Japon
Apple-PowerMacG5_UserGuide.pdf-Norge-Norvege
Apple-iPod_touch_2.1_Brukerhandbok.pdf-Norge-Norvege
Apple-Boot_Camp_Installering-klargjoring.pdf-Norge-Norvege
Apple-MacOSX10.3_Welcome.pdf-Norge-Norvege
Apple-iPod_shuffle_3rdGen_UG_H.pdf-Norge-Norvege
Apple-iPhone_4_Viktig_produktinformasjon.pdf-Norge-Norvege
Apple_TV_Klargjoringsoversikt.pdf-Norge-Norvege
Apple-iMovie_08_Komme_i_gang.pdf-Norge-Norvege
Apple-iPod_classic_160GB_Brukerhandbok.pdf-Norge-Norvege
Apple-Boot_Camp_Installering_10.6.pdf-Norge-Norvege
Apple-Network-Services-Location-Manager-Veiledning-for-nettverksadministratorer-Norge-Norvege
Apple-iOS_Business_Mar12_FR.pdf
Apple-PCIDualAttachedFDDICard.pdf
Apple-Aperture_Installing_Your_Software_f.pdf
Apple-User_Management_Admin_v10.4.pdf
Apple-Compressor-4-ユーザーズマニュアル Japon
Apple-Network_Services_v10.4.pdf
Apple-iPod_2ndGen_USB_Power_Adapter-DE
Apple-Mail_Service_v10.4.pdf
Apple-AirPort_Express_Opstillingsvejledning_5.1.pdf
Apple-MagSafe_Airline_Adapter.pdf
Apple-L-Apple-Multiple-Scan-20-Display
Apple-Administration_du_service_de_messagerie_10.5.pdf
Apple-System_Image_Admin.pdf
Apple-iMac_Intel-based_Late2006.pdf-Japon
Apple-iPhone_3GS_Finger_Tips_J.pdf-Japon
Apple-Power-Mac-G4-Mirrored-Drive-Doors-Japon
Apple-AirMac-カード取り付け手順-Japon
Apple-iPhone開発ガイド-Japon
Apple-atadrive_pmg4mdd.j.pdf-Japon
Apple-iPod_touch_2.2_User_Guide_J.pdf-Japon
Apple-Mac_OS_X_Server_v10.2.pdf
Apple-AppleCare_Protection_Plan_for_Apple_TV.pdf
Apple_Component_AV_Cable.pdf
Apple-DVD_Studio_Pro_4_Installation_de_votre_logiciel
Apple-Windows_Services
Apple-Motion_3_New_Features_F
Apple-g4mdd-fw800-lowerfan
Apple-MacOSX10.3_Welcome
Apple-Print_Service
Apple-Xserve_Setup_Guide_F
Apple-PowerBookG4_17inch1.67GHzUG
Apple-iMac_Intel-based_Late2006
Apple-Installation_de_votre_logiciel
Apple-guide_des_fonctions_de_l_iPod_nano
Apple-Administration_de_serveur_v10.5
Apple-Mac-OS-X-Server-Premiers-contacts-Pour-la-version-10.3-ou-ulterieure
Apple-boot_camp_install-setup
Apple-iBookG3_14inchUserGuideMultilingual
Apple-mac_pro_server_mid2010_ug_f
Apple-Motion_Supplemental_Documentation
Apple-imac_mid2011_ug_f
Apple-iphone_guide_de_l_utilisateur
Apple-macbook_air_11inch_mid2011_ug_fr
Apple-NouvellesfonctionnalitesdeLogicExpress7.2
Apple-QT_Streaming_Server
Apple-Web_Technologies_Admin
Apple-Mac_Pro_Early2009_4707_UG
Apple-guide_de_l_utilisateur_de_Numbers08
Apple-Decouverte_d_Aperture_2
Apple-Guide_de_configuration_et_d'administration
Apple-mac_integration_basics_fr_106.
Apple-iPod_shuffle_4thgen_Guide_de_l_utilisateur
Apple-ARA_Japan
Apple-081811_APP_iPhone_Japanese_v5.4.pdf-Japan
Apple-Recycle_Contract120919.pdf-Japan
Apple-World_Travel_Adapter_Kit_UG
Apple-iPod_nano_6thgen_User_Guide
Apple-RemoteSupportJP
Apple-Mac_mini_Early2009_UG_F.pdf-Manuel-de-l-utilisateur
Apple-Compressor_3_Batch_Monitor_User_Manual_F.pdf-Manuel-de-l-utilisateur
Apple-Premiers__contacts_avec_iDVD_08
Apple-Mac_mini_Intel_User_Guide.pdf
Apple-Prise_en_charge_des_surfaces_de_controle_Logic_Express_8
Apple-mac_integration_basics_fr_107.pdf
Apple-Final-Cut-Pro-7-Niveau-1-Guide-de-preparation-a-l-examen
Apple-Logic9-examen-prep-fr.pdf-Logic-Pro-9-Niveau-1-Guide-de-preparation-a-l-examen
Apple-aperture_photography_fundamentals.pdf-Manuel-de-l-utilisateu
Apple-emac-memory.pdf-Manuel-de-l-utilisateur
Apple-Apple-Installation-et-configuration-de-votre-Power-Mac-G4
Apple-Guide_de_l_administrateur_d_Xsan_2.pdf
Apple-premiers_contacts_avec_imovie6.pdf
Apple-Tiger_Guide_Installation_et_de_configuration.pdf
Apple-Final-Cut-Pro-7-Level-One-Exam-Preparation-Guide-and-Practice-Exam
Apple-Open_Directory.pdf
Apple-Nike_+_iPod_User_guide
Apple-ard_admin_guide_2.2_fr.pdf
Apple-systemoverviewj.pdf-Japon
Apple-Xserve_TO_J070411.pdf-Japon
Apple-Mac_Pro_User_Guide.pdf
Apple-iMacG5_iSight_UG.pdf
Apple-premiers_contacts_avec_iwork_08.pdf
Apple-services_de_collaboration_2e_ed_10.4.pdf
Apple-iPhone_Bluetooth_Headset_Benutzerhandbuch.pdf
Apple-Guide_de_l_utilisateur_de_Keynote08.pdf
APPLE/Apple-Logic-Pro-9-Effectsrfr.pdf
Apple-Logic-Pro-9-Effectsrfr.pdf
Apple-iPod_shuffle_3rdGen_UG_F.pdf
Apple-iPod_classic_160Go_Guide_de_l_utilisateur.pdf
Apple-iBookG4GettingStarted.pdf
Apple-Administration_de_technologies_web_10.5.pdf
Apple-Compressor-4-User-Manual-fr
Apple-MainStage-User-Manual-fr.pdf
Apple-Logic_Pro_8.0_lbn_j.pdf
Apple-PowerBookG4_15inch1.67-1.5GHzUserGuide.pdf
Apple-MacBook_Pro_15inch_Mid2010_CH.pdf
Apple-LED_Cinema_Display_27-inch_UG.pdf
Apple-MacBook_Pro_15inch_Mid2009_RS.pdf
Apple-macbook_pro_13inch_early2011_f.pdf
Apple-iMac_Mid2010_UG_BR.pdf
Apple-iMac_Late2009_UG_J.pdf
Apple-iphone_user_guide-For-iOS-6-Software
Apple-iDVD5_Getting_Started.pdf
Apple-guide_des_fonctionnalites_de_l_ipod_touch.pdf
Apple_iPod_touch_User_Guide
Apple_macbook_pro_13inch_early2011_f
Apple_Guide_de_l_utilisateur_d_Utilitaire_RAID
Apple_Time_Capsule_Early2009_Setup_F
Apple_iphone_4s_finger_tips_guide_rs
Apple_iphone_upute_za_uporabu
Apple_ipad_user_guide_ta
Apple_iPod_touch_User_Guide
apple_earpods_user_guide
apple_iphone_gebruikershandleiding
apple_iphone_5_info
apple_iphone_brukerhandbok
apple_apple_tv_3rd_gen_setup_tw
apple_macbook_pro-retina-mid-2012-important_product_info_ch
apple_Macintosh-User-s-Guide-for-Macintosh-PowerBook-145
Apple_ipod_touch_user_guide_ta
Apple_TV_2nd_gen_Setup_Guide_h
Apple_ipod_touch_manual_del_usuario
Apple_iphone_4s_finger_tips_guide_tu
Apple_macbook_pro_retina_qs_th
Apple-Manuel_de_l'utilisateur_de_Final_Cut_Server
Apple-iMac_G5_de_lutilisateur
Apple-Cinema_Tools_4.0_User_Manual_F
Apple-Personal-LaserWriter300-User-s-Guide
Apple-QuickTake-100-User-s-Guide-for-Macintosh
Apple-User-s-Guide-Macintosh-LC-630-DOS-Compatible
Apple-iPhone_iOS3.1_User_Guide
Apple-iphone_4s_important_product_information_guide
Apple-iPod_shuffle_Features_Guide_F
Liste-documentation-apple
Apple-Premiers_contacts_avec_iMovie_08
Apple-macbook_pro-retina-mid-2012-important_product_info_br
Apple-macbook_pro-13-inch-mid-2012-important_product_info
Apple-macbook_air-11-inch_mid-2012-qs_br
Apple-Manuel_de_l_utilisateur_de_MainStage
Apple-Compressor_3_User_Manual_F
Apple-Color_1.0_User_Manual_F
Apple-guide_de_configuration_airport_express_4.2
Apple-TimeCapsule_SetupGuide
Apple-Instruments_et_effets_Logic_Express_8
Apple-Manuel_de_l_utilisateur_de_WaveBurner
Apple-Macmini_Guide_de_l'utilisateur
Apple-PowerMacG5_UserGuide
Disque dur, ATA parallèle Instructions de remplacement
Apple-final_cut_pro_x_logic_effects_ref_f
Apple-Leopard_Installationshandbok
Manuale Utente PowerBookG4
Apple-thunderbolt_display_getting_started_1e
Apple-Compressor-4-Benutzerhandbuch
Apple-macbook_air_11inch_mid2011_ug
Apple-macbook_air-mid-2012-important_product_info_j
Apple-iPod-nano-Guide-des-fonctionnalites
Apple-iPod-nano-Guide-des-fonctionnalites
Apple-iPod-nano-Guide-de-l-utilisateur-4eme-generation
Apple-iPod-nano-Guide-de-l-utilisateur-4eme-generation
Apple-Manuel_de_l_utilisateur_d_Utilitaire_de_reponse_d_impulsion
Apple-Aperture_2_Raccourcis_clavier
AppleTV_Setup-Guide
Apple-livetype_2_user_manual_f
Apple-imacG5_17inch_harddrive
Apple-macbook_air_guide_de_l_utilisateur
Apple-MacBook_Early_2008_Guide_de_l_utilisateur
Apple-Keynote-2-Guide-de-l-utilisateur
Apple-PowerBook-User-s-Guide-for-PowerBook-computers
Apple-Macintosh-Performa-User-s-Guide-5200CD-and-5300CD
Apple-Macintosh-Performa-User-s-Guide
Apple-Workgroup-Server-Guide
Apple-iPod-nano-Guide-des-fonctionnalites
Apple-iPad-User-Guide-For-iOS-5-1-Software
Apple-Boot-Camp-Guide-d-installation-et-de-configuration
Apple-iPod-nano-Guide-de-l-utilisateur-4eme-generation
Power Mac G5 Guide de l’utilisateur APPLE
Guide de l'utilisateur PAGE '08 APPLE
Guide de l'utilisateur KEYNOTE '09 APPLE
Guide de l'Utilisateur KEYNOTE '3 APPLE
Guide de l'Utilisateur UTILITAIRE RAID
Guide de l'Utilisateur Logic Studio
Power Mac G5 Guide de l’utilisateur APPLE
Guide de l'utilisateur PAGE '08 APPLE
Guide de l'utilisateur KEYNOTE '09 APPLE
Guide de l'Utilisateur KEYNOTE '3 APPLE
Guide de l'Utilisateur UTILITAIRE RAID
Guide de l'Utilisateur Logic Studio
Guide de l’utilisateur ipad Pour le logiciel iOS 5.1
PowerBook G4 Premiers Contacts APPLE
Guide de l'Utilisateur iphone pour le logiciel ios 5.1 APPLE
Guide de l’utilisateur ipad Pour le logiciel iOS 4,3
Guide de l’utilisateur iPod nano 5ème génération
Guide de l'utilisateur iPod Touch 2.2 APPLE
Guide de l’utilisateur QuickTime 7 Mac OS X 10.3.9 et ultérieur Windows XP et Windows 2000
Guide de l'utilisateur MacBook 13 pouces Mi 2010
Guide de l’utilisateur iPhone (Pour les logiciels iOS 4.2 et 4.3)
Guide-de-l-utilisateur-iPod-touch-pour-le-logiciel-ios-4-3-APPLE
Guide-de-l-utilisateur-iPad-2-pour-le-logiciel-ios-4-3-APPLE
Guide de déploiement en entreprise iPhone OS
Guide-de-l-administrateur-Apple-Remote-Desktop-3-1
Guide-de-l-utilisateur-Apple-Xserve-Diagnostics-Version-3X103
Guide-de-configuration-AirPort-Extreme-802.11n-5e-Generation
Guide-de-configuration-AirPort-Extreme-802-11n-5e-Generation
Guide-de-l-utilisateur-Capteur-Nike-iPod
Guide-de-l-utilisateur-iMac-21-5-pouces-et-27-pouces-mi-2011-APPLE
Guide-de-l-utilisateur-Apple-Qadministrator-4
Guide-d-installation-Apple-TV-3-eme-generation
User-Guide-iPad-For-ios-5-1-Software
URL Loading System
Programming GuideContents
Introduction 5
Organization of This Document 5
See Also 6
URL Loading System Overview 7
URL Loading 7
Cache Management 9
Authentication and Credentials 9
Cookie Storage 10
Protocol Support 11
Using NSURLConnection 12
Creating a Connection 12
Controlling Response Caching 15
Estimating Upload Progress 16
Downloading Data Synchronously 17
Using NSURLDownload 18
Downloading to a Predetermined Destination 18
Downloading a File Using the Suggested Filename 20
Displaying Download Progress 22
Resuming Downloads 24
Decoding Encoded Files 24
Handling Redirects and Other Request Changes 26
Authentication Challenges 28
Deciding How to Respond to an Authentication Challenge 28
Responding to an Authentication Challenge 29
Providing Credentials 29
Continuing Without Credentials 30
Canceling the Connection 30
Understanding Cache Access 32
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
2Using the Cache for a Request 32
Cache Use Semantics for the http Protocol 33
Document Revision History 34
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
3Figures and Listings
URL Loading System Overview 7
Figure 1 The URL loading system class hierarchy 7
Using NSURLConnection 12
Listing 1 Creating a connection using NSURLConnection 12
Listing 2 Example connection:didReceiveResponse: implementation 13
Listing 3 Example connection:didReceiveData: implementation 14
Listing 4 Example connectionDidFailWithError: implementation 14
Listing 5 Example connectionDidFinishLoading: implementation 15
Listing 6 Example connection:withCacheResponse: implementation 16
Using NSURLDownload 18
Listing 1 Using NSURLDownload with a predetermined destination file location 18
Listing 2 Using NSURLDownload with a filename derived from the download 20
Listing 3 Logging the finalized filename using download:didCreateDestination: 22
Listing 4 Displaying the download progress 22
Listing 5 Example implementation of download:shouldDecodeSourceDataOfMIMEType: method. 24
Handling Redirects and Other Request Changes 26
Listing 1 Example of an implementation of connection:willSendRequest:redirectResponse:
26
Authentication Challenges 28
Listing 1 An example of using the connection:didReceiveAuthenticationChallenge: delegate
method 30
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
4This guide describes the Foundation framework classes available for interacting with URLs and communicating
with servers using standard Internet protocols. Together these classes are referred to asthe URL loading system.
The NSURL class provides the ability to manipulate URLs and the resources they refer to.
The Foundation framework also provides a rich collection of classes that include support for URL loading,
cookie storage, response caching, credentialstorage and authentication, and writing custom protocol extensions.
The URL loading system provides support for accessing resources using the following protocols:
● File Transfer Protocol (ftp://)
● Hypertext Transfer Protocol (http://)
● Secure 128-bit Hypertext Transfer Protocol (https://)
● Local file URLs (file:///)
It also transparently supports both proxy servers and SOCKS gateways using the user’s system preferences.
Organization of This Document
This guide includes the following articles:
●
“URL Loading System Overview” (page 7) describes the classes of the URL loading system and their
interaction.
●
“Using NSURLConnection” (page 12) describes using NSURLConnection for asynchronous connections.
●
“Using NSURLDownload” (page 18) describes using NSURLDownload to download files asynchronously
to disk.
●
“Handling Redirects and Other Request Changes” (page 26) describesthe options you have for responding
to a change to your URL request.
●
“Authentication Challenges” (page 28) describes the process for authenticating your connection against
a secure server.
●
“Understanding Cache Access” (page 32) describes how a connection uses the cache during a request.
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
5
IntroductionSee Also
The following sample code is available through Apple Developer Connection:
● SpecialPictureProtocol implements a custom NSURLProtocol that creates jpeg images in memory as
data is downloaded.
● AutoUpdater demonstrates how to check for, and download, an application update using
NSURLConnection and NSURLDownload.
Introduction
See Also
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
6The URL loading system is a set of classes and protocols that provide the underlying capability for an application
to access the data specified by a URL.
These classes fall into five categories: URL loading, cache management, authentication and credentials, cookie
storage, and protocol support.
Figure 1 The URL loading system class hierarchy
NSObject
URL Loading
NSURLConnection
NSURLRequest NSMutableURLRequest
NSURLResponse NSHTTPURLResponse
NSURLDownload
Cache Management
NSCacheURLRequest
NSURLCache
Cookie Storage
NSHTTPCookie
NSHTTPCookieStorage
Protocol Support
NSURLProtocolClient
NSURLProtocol
Authentication and Credentials
NSURLCredential
NSURLCredentialStorage
NSURLProtectionSpace
NSURLAuthenticationChallenge
NSURLAuthenticationChallengeSender
URL Loading
The most commonly used classes in the URL loading system allow an application to create a request for the
content of a URL and download it from the source.
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
7
URL Loading System OverviewA request for the contents of a URL is represented by an NSURLRequest object. The NSURLRequest class
encapsulates a URL and any protocol-specific properties, in a protocol-independent manner. It also provides
an interface to set the timeout for a connection and specifiesthe policy regarding the use of any locally cached
data. The NSMutableURLRequest classis a mutable subclass of NSURLRequest that allows a client application
to alter an existing request.
Note: When a client application initiates a connection or download using an instance of
NSMutableURLRequest, a deep copy is made of the request. Changes made to the initiating request
have no effect once a download has been initialized.
Protocols,such as HTTP, thatsupport protocol-specific properties must create categories on the NSURLRequest
and NSMutableURLRequest classesto provide accessorsfor those properties. As an example, the HTTP protocol
adds methods to NSURLRequest to return the HTTP request body, headers, and transfer method. It also adds
methodsto NSMutableURLRequest to set the corresponding values. Methodsforsetting and getting property
values in those accessors are exposed in the NSURLProtocol class.
The response from a server to a request can be viewed as two parts: metadata describing the contents and
the URL content data. The metadata that is common to most protocolsis encapsulated by the NSURLResponse
class and consists of the MIME type, expected content length, text encoding (where applicable), and the URL
that provided the response. Protocols can create subclasses of NSURLResponse to store protocol-specific
metadata. NSHTTPURLResponse, for example, stores the headers and the status code returned by the web
server.
Note: It’s important to remember that only the metadata for the response is stored in an
NSURLResponse object. An NSCachedURLResponse instance is used to encapsulate an
NSURLResponse, the URL content data, and any application-provided information. See “Cache
Management” (page 9) for details.
The NSURLConnection and NSURLDownload classes provide the interface to make a connection specified
by an NSURLRequest object and download the contents. An NSURLConnection object provides data to the
delegate as it is received from the originating source, whereas an NSURLDownload object writes the request
data directly to disk. Both classes provide extensive delegate support for responding to redirects, authentication
challenges, and error conditions.
The NSURLConnection class provides a delegate method that allows an application to control the caching
of a response on a per-request basis. Downloads initiated by an NSURLDownload instance are not cached.
URL Loading System Overview
URL Loading
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
8Cache Management
The URL loading system provides a composite on-disk and in-memory cache allowing an application to reduce
its dependence on a network connection and provide faster turnaround for previously cached responses. The
cache is stored on a per-application basis.
The cache is queried by NSURLConnection according to the cache policy specified by the initiating
NSURLRequest.
The NSURLCache class provides methods to configure the cache size and its location on disk. It also provides
methods to manage the collection of NSCachedURLResponse objects that contain the cached responses.
An NSCachedURLResponse object encapsulates the NSURLResponse and the URL content data.
NSCachedURLResponse also provides a user info dictionary that can be used by an application to cache any
custom data.
Not all protocol implementations support response caching. Currently only http and https requests are
cached, and https requests are never cached to disk.
An NSURLConnection can control whether a response is cached and whether the response should be cached
only in memory by implementing the connection:willCacheResponse: delegate method.
Authentication and Credentials
Some servers restrict access to certain content, requiring a user to authenticate with a valid user name and
password in order to gain access. In the case of a web server, restricted content is grouped together into a
realm that requires a single set of credentials.
The URL loading system provides classesthat model credentials and protected areas as well as providing secure
credential persistence. Credentials can be specified to persist for a single request, for the duration of an
application’s launch, or permanently in the user’s keychain.
Note: Credentials stored in persistent storage are kept in the user's keychain and shared among all
applications.
The NSURLCredential class encapsulates a credential consisting of the user name, password, and the type
of persistence to use. The NSURLProtectionSpace classrepresents an area that requires a specific credential.
A protection space can be limited to a single URL, encompass a realm on a web server, or refer to a proxy.
URL Loading System Overview
Cache Management
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
9A shared instance of the NSURLCredentialStorage class manages credential storage and provides the
mapping of an NSURLCredential object to the corresponding NSURLProtectionSpace object for which
it provides authentication.
The NSURLAuthenticationChallenge class encapsulates the information required by an NSURLProtocol
implementation to authenticate a request: a proposed credential, the protection space involved, the error or
response that the protocol used to determine that authentication isrequired, and the number of authentication
attemptsthat have been made. An NSURLAuthenticationChallenge instance also specifiesthe object that
initiated the authentication. The initiating object, referred to as the sender, must conform to the
NSURLAuthenticationChallengeSender protocol.
NSURLAuthenticationChallenge instances are used by NSURLProtocol subclasses to inform the URL
loading system that authentication is required. They are also provided to the delegate methods of
NSURLConnection and NSURLDownload that facilitate customized authentication handling.
Cookie Storage
Due to the stateless nature of the HTTP protocol, cookies are often used to provide persistent storage of data
across URL requests. The URL loading system provides interfaces to create and manage cookies as well as
sending and receiving cookies from web servers.
The NSHTTPCookie class encapsulates a cookie, providing accessors for many of the common cookie attributes.
It also provides methods to convert HTTP cookie headers to NSHTTPCookie instances and convert an
NSHTTPCookie instance to headers suitable for use with an NSURLRequest. The URL loading system
automatically sends any stored cookies appropriate for an NSURLRequest. unless the request specifies not to
send cookies. Likewise, cookies returned in an NSURLResponse are accepted in accordance with the current
cookie acceptance policy.
The NSHTTPCookieStorage class provides the interface for managing the collection of NSHTTPCookie
objects shared by all applications.
iOS Note: Cookies are not shared by applications in iOS.
NSHTTPCookieStorage allows an application to specify a cookie acceptance policy. The cookie acceptance
policy controls whether cookies should always be accepted, never be accepted, or accepted only from the
same domain as the main document URL.
URL Loading System Overview
Cookie Storage
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
10Note: Changing the cookie acceptance policy in an application affectsthe cookie acceptance policy
for all other running applications.
When another application changesthe cookie storage or the cookie acceptance policy, NSHTTPCookieStorage
notifies an application by posting the NSHTTPCookieStorageCookiesChangedNotification and
NSHTTPCookieStorageAcceptPolicyChangedNotification notifications.
Protocol Support
The URL loading system design allows a client application to extend the protocols that are supported for
transferring data. The URL loading system natively supports http, https, file, and ftp protocols.
Custom protocols are implemented by subclassing NSURLProtocol and then registering the new class with
the URL loading system using the NSURLProtocol class method registerClass:. When an
NSURLConnection or NSURLDownload object initiates a connection for an NSURLRequest, the URL loading
system consults each of the registered classes in the reverse order of their registration. The first class that
returns YES for a canInitWithRequest: message is used to handle the request.
The URL loading system is responsible for creating and releasing NSURLProtocol instances when connections
start and complete. An application should never create an instance of NSURLProtocol directly.
When an NSURLProtocol subclass is initialized by the URL loading system, it is provided a client object that
conforms to the NSURLProtocolClient protocol. The NSURLProtocol subclass sends messages from the
NSURLProtocolClient protocol to the client object to inform the URL loading system of its actions as it creates
a response, receives data, redirectsto a new URL, requires authentication, and completesthe load. If the custom
protocolsupports authentication, then it must conform to the NSURLAuthenticationChallengeSender protocol.
URL Loading System Overview
Protocol Support
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
11NSURLConnection provides the most flexible method of downloading the contents of a URL. It provides a
simple interface for creating and canceling a connection, and supports a collection of delegate methods that
provide feedback and control of many aspects of the connection. These classes fall into five categories: URL
loading, cache management, authentication and credentials, cookie storage, and protocol support.
Creating a Connection
In order to download the contents of a URL, an application needsto provide a delegate object that, at a minimum,
implements the following delegate methods: connection:didReceiveResponse:,
connection:didReceiveData:, connection:didFailWithError: and
connectionDidFinishLoading:.
The example in Listing 1 initiates a connection for a URL. It begins by creating an NSURLRequest instance for
the URL, specifying the cache access policy and the timeout interval for the connection. It then creates an
NSURLConnection instance,specifying the request and a delegate. If NSURLConnection can’t create a connection
for the request, initWithRequest:delegate: returns nil. If the connection is successful, an instance of
NSMutableData is created to store the data that is provided to the delegate incrementally.
Listing 1 Creating a connection using NSURLConnection
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL
URLWithString:@"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest
delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
12
Using NSURLConnectionreceivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
The download starts immediately upon receiving the initWithRequest:delegate: message. It can be
canceled any time before the delegate receives a connectionDidFinishLoading: or
connection:didFailWithError: message by sending the connection a cancel message.
When the server has provided sufficient data to create an NSURLResponse object, the delegate receives a
connection:didReceiveResponse: message. The delegate method can examine the provided
NSURLResponse and determine the expected content length of the data, MIME type, suggested filename and
other metadata provided by the server.
You should be prepared for your delegate to receive the connection:didReceiveResponse: message
multiple times for a single connection. This message can be sent due to server redirects, or in rare cases
multi-part MIME documents. Each time the delegate receives the connection:didReceiveResponse:
message, it should reset any progress indication and discard all previously received data. The example
implementation in Listing 2 simply resets the length of the received data to 0 each time it is called.
Listing 2 Example connection:didReceiveResponse: implementation
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse
*)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
The delegate is periodically sent connection:didReceiveData: messages as the data is received. The
delegate implementation is responsible for storing the newly received data. In the example implementation
in Listing 3, the new data is appended to the NSMutableData object created in Listing 1.
Using NSURLConnection
Creating a Connection
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
13Listing 3 Example connection:didReceiveData: implementation
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
You can also use the connection:didReceiveData: method to provide an indication of the connection’s
progress to the user.
If an error is encountered during the download, the delegate receives a connection:didFailWithError:
message. The NSError object passed as the parameter specifies the details of the error. It also provides the URL
of the request that failed in the user info dictionary using the key NSURLErrorFailingURLStringErrorKey.
After the delegate receives a message connection:didFailWithError:, it receives no further delegate
messages for the specified connection.
The example in Listing 4 releases the connection, as well as any received data, and logs the error.
Listing 4 Example connectionDidFailWithError: implementation
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
// inform the user
NSLog(@"Connection failed! Error - %@ %@",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
Using NSURLConnection
Creating a Connection
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
14Finally, if the connection succeeds in downloading the request, the delegate receives the
connectionDidFinishLoading: message. The delegate will receive no further messagesfor the connection
and the NSURLConnection object can be released.
The example implementation in Listing 5 logsthe length of the received data and releases both the connection
object and the received data.
Listing 5 Example connectionDidFinishLoading: implementation
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);
// release the connection, and the data object
[connection release];
[receivedData release];
}
This represents the simplest implementation of a client using NSURLConnection. Additional delegate methods
provide the ability to customize the handling of server redirects, authorization requests and caching of the
response.
Controlling Response Caching
By default the data for a connection is cached according to the support provided by the NSURLProtocolsubclass
that handles the request. An NSURLConnection delegate can further refine that behavior by implementing
connection:willCacheResponse:.
This delegate method can examine the provided NSCachedURLResponse object and change how the response
is cached, for example restricting its storage to memory only or preventing it from being cached altogether.
It is also possible to insert objects in an NSCachedURLResponse’s user info dictionary, causing them to be
stored in the cache as part of the response.
Using NSURLConnection
Controlling Response Caching
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
15Note: The delegate receives connection:willCacheResponse: messages only for protocols
that support caching.
The example in Listing 6 prevents the caching of https responses. It also adds the current date to the user
info dictionary for responses that are cached.
Listing 6 Example connection:withCacheResponse: implementation
-(NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
NSCachedURLResponse *newCachedResponse = cachedResponse;
if ([[[[cachedResponse response] URL] scheme] isEqual:@"https"]) {
newCachedResponse = nil;
} else {
NSDictionary *newUserInfo;
newUserInfo = [NSDictionary dictionaryWithObject:[NSCalendarDate date]
forKey:@"Cached Date"];
newCachedResponse = [[[NSCachedURLResponse alloc]
initWithResponse:[cachedResponse response]
data:[cachedResponse data]
userInfo:newUserInfo
storagePolicy:[cachedResponse storagePolicy]]
autorelease];
}
return newCachedResponse;
}
Estimating Upload Progress
You can estimate the progress of an HTTP POST upload with the
connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite: delegatemethod.
Note that this is not an exact measurement of upload progress, because the connection may fail or the
connection may encounter an authentication challenge.
Using NSURLConnection
Estimating Upload Progress
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
16Downloading Data Synchronously
NSURLConnection provides support for downloading the contents of an NSURLRequest in a synchronous
manner using the class method sendSynchronousRequest:returningResponse:error:. Using this
method is not recommended, because it has severe limitations:
● The client application blocks until the data has been completely received, an error is encountered, or the
request times out.
● Minimal support is provided for requests that require authentication.
● There is no means of modifying the default behavior of response caching or accepting server redirects.
If the download succeeds, the contents of the request are returned as an NSData object and an NSURLResponse
for the request is returned by reference. If NSURLConnection is unable to download the URL, the method
returns nil and any available NSError instance by reference in the appropriate parameter.
If the request requires authentication in order to make the connection, valid credentials must already be
available in the NSURLCredentialStorage, or must be provided as part of the requested URL. If the credentials
are not available or fail to authenticate, the URL loading system responds by sending the NSURLProtocol
subclass handling the connection a continueWithoutCredentialForAuthenticationChallenge:
message.
When a synchronous connection attempt encounters a server redirect, the redirect is always honored. Likewise
the response data is stored in the cache according to the default support provided by the protocol
implementation.
Using NSURLConnection
Downloading Data Synchronously
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
17NSURLDownload provides an application the ability to download the contents of a URL directly to disk. It
provides an interface similar to NSURLConnection, adding an additional method forspecifying the destination
of the file. NSURLDownload can also decode commonly used encoding schemes such as MacBinary, BinHex
and gzip. Unlike NSURLConnection, data downloaded using NSURLDownload is notstored in the cache system.
If your application is not restricted to using Foundation classes, the WebKit framework includes WebDownload,
a subclass of NSURLDownload that provides a user interface for authentication.
iOS Note: The NSURLDownload class is not available in iOS, because downloading directly to the
file system is discouraged. Use the NSURLConnection class instead. See “Using
NSURLConnection” (page 12) for more information.
Downloading to a Predetermined Destination
One usage pattern for NSURLDownload is downloading a file to a predetermined filename on disk. If the
application knows the destination of the download, it can set it explicitly using
setDestination:allowOverwrite:. Multiple setDestination:allowOverwrite: messages to an
NSURLDownload instance are ignored.
The download starts immediately upon receiving the initWithRequest:delegate: message. It can be
canceled any time before the delegate receives a downloadDidFinish: or download:didFailWithError:
message by sending the download a cancel message.
The example in Listing 1 sets the destination, and thus requires the delegate only implement the
download:didFailWithError: and downloadDidFinish: methods.
Listing 1 Using NSURLDownload with a predetermined destination file location
- (void)startDownloadingURL:sender
{
// Create the request.
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL
URLWithString:@"http://www.apple.com"]
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
18
Using NSURLDownloadcachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create the connection with the request and start loading the data.
NSURLDownload *theDownload = [[NSURLDownload alloc] initWithRequest:theRequest
delegate:self];
if (theDownload) {
// Set the destination file.
[theDownload setDestination:@"/tmp" allowOverwrite:YES];
} else {
// inform the user that the download failed.
}
}
- (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
{
// Release the connection.
[download release];
// Inform the user.
NSLog(@"Download failed! Error - %@ %@",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)downloadDidFinish:(NSURLDownload *)download
{
// Release the connection.
[download release];
// Do something with the data.
NSLog(@"%@",@"downloadDidFinish");
}
Using NSURLDownload
Downloading to a Predetermined Destination
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
19Additional methods can be implemented by the delegate to customize the handling of authentication, server
redirects and file decoding.
Downloading a File Using the Suggested Filename
Sometimesthe application must derive the destination filename from the downloaded data itself. Thisrequires
you to implement the delegate method download:decideDestinationWithSuggestedFilename: and
call setDestination:allowOverwrite: with the suggested filename. The example in Listing 2 saves the
downloaded file to the desktop using the suggested filename.
Listing 2 Using NSURLDownload with a filename derived from the download
- (void)startDownloadingURL:sender
{
// Create the request.
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL
URLWithString:@"http://www.apple.com/index.html"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create the download with the request and start loading the data.
NSURLDownload *theDownload = [[NSURLDownload alloc] initWithRequest:theRequest
delegate:self];
if (!theDownload) {
// Inform the user that the download failed.
}
}
- (void)download:(NSURLDownload *)download
decideDestinationWithSuggestedFilename:(NSString *)filename
{
NSString *destinationFilename;
NSString *homeDirectory = NSHomeDirectory();
destinationFilename = [[homeDirectory stringByAppendingPathComponent:@"Desktop"]
stringByAppendingPathComponent:filename];
Using NSURLDownload
Downloading a File Using the Suggested Filename
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
20[download setDestination:destinationFilename allowOverwrite:NO];
}
- (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
{
// Release the download.
[download release];
// Inform the user.
NSLog(@"Download failed! Error - %@ %@",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)downloadDidFinish:(NSURLDownload *)download
{
// Release the download.
[download release];
// Do something with the data.
NSLog(@"%@",@"downloadDidFinish");
}
The downloaded file is stored on the user's desktop with the name index.html, which was derived from the
downloaded content. Passing NO to setDestination:allowOverwrite: prevents an existing file from
being overwritten by the download. Instead a unique filename is created by inserting a sequential number
after the filename, for example, index-1.html.
The delegate is informed when a file is created on disk if it implements the
download:didCreateDestination: method. This method also gives the application the opportunity to
determine the finalized filename with which the download is saved.
The example in Listing 3 logs the finalized filename.
Using NSURLDownload
Downloading a File Using the Suggested Filename
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
21Listing 3 Logging the finalized filename using download:didCreateDestination:
-(void)download:(NSURLDownload *)download didCreateDestination:(NSString *)path
{
// path now contains the destination path
// of the download, taking into account any
// unique naming caused by -setDestination:allowOverwrite:
NSLog(@"Final file destination: %@",path);
}
This message is sent to the delegate after it has been given an opportunity to respond to the
download:shouldDecodeSourceDataOfMIMEType: and
download:decideDestinationWithSuggestedFilename: messages.
Displaying Download Progress
The progress of the download can be determined by implementing the delegate methods
download:didReceiveResponse: and download:didReceiveDataOfLength:.
The download:didReceiveResponse: method provides the delegate an opportunity to determine the
expected content length from the NSURLResponse. The delegate should reset the progress each time this
message is received.
The example implementation in Listing 4 demonstrates using these methods to provide progress feedback to
the user.
Listing 4 Displaying the download progress
- (void)setDownloadResponse:(NSURLResponse *)aDownloadResponse
{
[aDownloadResponse retain];
// downloadResponse is an instance variable defined elsewhere.
[downloadResponse release];
downloadResponse = aDownloadResponse;
}
Using NSURLDownload
Displaying Download Progress
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
22- (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse
*)response
{
// Reset the progress, this might be called multiple times.
// bytesReceived is an instance variable defined elsewhere.
bytesReceived = 0;
// Retain the response to use later.
[self setDownloadResponse:response];
}
- (void)download:(NSURLDownload *)download didReceiveDataOfLength:(unsigned)length
{
long long expectedLength = [[self downloadResponse] expectedContentLength];
bytesReceived = bytesReceived + length;
if (expectedLength != NSURLResponseUnknownLength) {
// If the expected content length is
// available, display percent complete.
float percentComplete = (bytesReceived/(float)expectedLength)*100.0;
NSLog(@"Percent complete - %f",percentComplete);
} else {
// If the expected content length is
// unknown, just log the progress.
NSLog(@"Bytes received - %d",bytesReceived);
}
}
The delegate receives a download:didReceiveResponse: message before it begins receiving
download:didReceiveDataOfLength: messages.
Using NSURLDownload
Displaying Download Progress
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
23Resuming Downloads
In some cases, you can resume a download that was canceled or that failed while in progress. To do so, first
make sure your original download doesn’t delete its data upon failure by passing NO to the download’s
setDeletesFileUponFailure: method. If the original download fails, you can obtain its data with the
resumeData method. You can then initialize a new download with the
initWithResumeData:delegate:path: method. When the download resumes, the download’s delegate
receives the download:willResumeWithResponse:fromByte: message.
You can resume a download only if both the protocol of the connection and the MIME type of the file being
downloaded support resuming. You can determine whether your file’s MIME type is supported with the
canResumeDownloadDecodedWithEncodingMIMEType: method.
Decoding Encoded Files
NSURLDownload provides support for decoding selected file formats: MacBinary, BinHex and gzip. If
NSURLDownload determines that a file is encoded in a supported format, it attempts to send the delegate a
download:shouldDecodeSourceDataOfMIMEType: message. If the delegate implements this method, it
should examine the passed MIME type and return YES if the file should be decoded.
The example in Listing 5 compares the MIME type of the file and allows decoding of MacBinary and BinHex
encoded content.
Listing 5 Example implementation of download:shouldDecodeSourceDataOfMIMEType: method.
- (BOOL)download:(NSURLDownload *)download
shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType;
{
BOOL shouldDecode = NO;
if ([encodingType isEqual:@"application/macbinary"]) {
shouldDecode = YES;
} else if ([encodingType isEqual:@"application/binhex"]) {
shouldDecode = YES;
} else if ([encodingType isEqual:@"application/gzip"]) {
shouldDecode = NO;
}
return shouldDecode;
Using NSURLDownload
Resuming Downloads
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
24}
Using NSURLDownload
Decoding Encoded Files
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
25A server may redirect a request for one URL to another URL. The delegates for NSURLConnection and
NSURLDownload can be notified when this occurs for their connection.
To handle a redirect for an instance of NSURLConnection, implement the
connection:willSendRequest:redirectResponse: delegate method (for NSURLDownload, implement
download:willSendRequest:redirectResponse:). If the delegate implements this method, it can
examine the new request and the response that caused the redirect, and respond in one of four ways:
● The delegate can allow the redirect by simply returning the provided request.
● The delegate can create a new request, pointing to a different URL, and return that request.
● The delegate can reject the redirect and receive any existing data from the connection by returning nil.
● The delegate can cancel both the redirect and the connection by sending the cancelmessage to the
NSURLConnection or NSURLDownload.
The delegate also receives the connection:willSendRequest:redirectResponse: message if the
NSURLProtocol subclass that handles the request has changed the NSURLRequest in order to standardize
its format, for example, changing a request for http://www.apple.com to http://www.apple.com/. This
occurs because the standardized, or canonical, version of the request is used for cache management. In this
special case, the response passed to the delegate is nil and the delegate should simply return the provided
request.
The example implementation in Listing 1 allows canonical changes and denies all server redirects.
Listing 1 Example of an implementation of connection:willSendRequest:redirectResponse:
-(NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
{
NSURLRequest *newRequest = request;
if (redirectResponse) {
newRequest = nil;
}
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
26
Handling Redirects and Other Request Changesreturn newRequest;
}
If the delegate doesn't implement connection:willSendRequest:redirectResponse:, all canonical
changes and server redirects are allowed.
Handling Redirects and Other Request Changes
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
27An NSURLRequest object often encounters an authentication challenge, or a request for credentials from the
server it is connecting to. The delegates for NSURLConnection and NSURLDownload can be notified when
their request encounters an authentication challenge, so that they can act accordingly.
Deciding How to Respond to an Authentication Challenge
If an NSURLRequest object requires authentication, the delegate of the NSURLConnection (or NSURLDownload)
object associated with the request first receives a
connection:canAuthenticateAgainstProtectionSpace: (or
download:canAuthenticateAgainstProtectionSpace:) message. This allows the delegate to analyze
properties of the server, including its protocol and authentication method, before attempting to authenticate
against it. If your delegate is not prepared to authenticate against the server’s protection space, you can return
NO, and the system attempts to authenticate with information from the user’s keychain.
Note: If your delegate does not implement the
connection:canAuthenticateAgainstProtectionSpace: method and the protection space
uses client certificate authentication or server trust authentication, the system behaves as if you
returned NO. The system behaves as if you returned YES for all other authentication methods.
If your delegate returns YES from connection:canAuthenticateAgainstProtectionSpace: or doesn’t
implement it, and there are no valid credentials available, either as part of the requested URL or in the shared
NSURLCredentialStorage,the delegate receives a connection:didReceiveAuthenticationChallenge:
message. In order for the connection to continue, the delegate has three options:
● Provide authentication credentials
● Attempt to continue without credentials
● Cancel the authentication challenge
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
28
Authentication ChallengesTo help determine the correct course of action, the NSURLAuthenticationChallenge instance passed to
the method contains information about what triggered the authentication challenge, how many attempts
were made for the challenge, any previously attempted credentials, the NSURLProtectionSpace that requires
the credentials, and the sender of the challenge.
If the authentication challenge has tried to authenticate previously and failed, you can obtain the attempted
credentials by calling proposedCredential on the authentication challenge. The delegate can then use
these credentials to populate a dialog that it presents to the user.
Calling previousFailureCount on the authentication challenge returns the total number of previous
authentication attempts, including those from different authentication protocols. The delegate can provide
this information to the end user, to determine whether the credentials it supplied previously are failing, or to
limit the maximum number of authentication attempts.
Responding to an Authentication Challenge
The following are the three ways you can respond to the
connection:didReceiveAuthenticationChallenge: delegate method.
Providing Credentials
To attempt to authenticate, the application should create an NSURLCredential object with authentication
information of the form expected by the server. You can determine the server’s authentication method by
calling authenticationMethod on the protection space of the provided authentication challenge. Some
authentication methods supported by NSURLCredential are:
HTTP Basic Authentication (NSURLAuthenticationMethodHTTPBasic)
The basic authentication method requires a user name and password. Prompt the user for the necessary
information and create an NSURLCredential object with
credentialWithUser:password:persistence:.
HTTP Digest Authentication (NSURLAuthenticationMethodHTTPDigest)
Like basic authentication, digest authentication just requires a user name and password (the digest is
generated automatically). Prompt the user for the necessary information and create an NSURLCredential
object with credentialWithUser:password:persistence:.
Client Certificate Authentication (NSURLAuthenticationMethodClientCertificate)
Client certificate authentication requires the system identity and all certificates needed to authenticate
with the server. Create an NSURLCredential object with
credentialWithIdentity:certificates:persistence:.
Authentication Challenges
Responding to an Authentication Challenge
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
29Server Trust Authentication (NSURLAuthenticationMethodServerTrust)
Server trust authentication requires a trust provided by the protection space of the authentication
challenge. Create an NSURLCredential object with credentialForTrust:.
After you’ve created the NSURLCredential object, pass it to the authentication challenge’s sender with
useCredential:forAuthenticationChallenge:.
Continuing Without Credentials
If the delegate chooses not to provide a credential for the authentication challenge, it can attempt to continue
without one by calling continueWithoutCredentialsForAuthenticationChallenge: on [challenge
sender]. Depending on the protocol implementation, continuing without credentials may either cause the
connection to fail, resulting in a connectionDidFailWithError: message, or return alternate URL contents
that don’t require authentication.
Canceling the Connection
The delegate may also choose to cancel the authentication challenge by calling
cancelAuthenticationChallenge: on [challenge sender]. The delegate receives a
connection:didCancelAuthenticationChallenge: message, providing the opportunity to give the
user feedback.
Para
The implementation shown in Listing 1 attempts to authenticate the challenge by creating an
NSURLCredential instance with a user name and password supplied by the application’s preferences. If the
authentication has failed previously, it cancels the authentication challenge and informs the user.
Listing 1 An example of using the connection:didReceiveAuthenticationChallenge: delegate method
-(void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential = [NSURLCredential credentialWithUser:[self preferencesName]
password:[self preferencesPassword]
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential
Authentication Challenges
Responding to an Authentication Challenge
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
30forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
// inform the user that the user name and password
// in the preferences are incorrect
[self showPreferencesCredentialsAreIncorrectPanel:self];
}
}
If the delegate doesn’t implement connection:didReceiveAuthenticationChallenge: and the request
requires authentication, valid credentials must already be available in the URL credential storage or must be
provided as part of the requested URL. If the credentials are not available or if they fail to authenticate, a
continueWithoutCredentialForAuthenticationChallenge: message is sent by the underlying
implementation.
Authentication Challenges
Responding to an Authentication Challenge
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
31The URL loading system provides a composite on-disk and in-memory cache of responses to requests. This
cache allows an application to reduce its dependency on a network connection and increase its performance.
Using the Cache for a Request
An NSURLRequest instance specifies how the local cache is used by setting the cache policy to one of the
NSURLRequestCachePolicy values: NSURLRequestUseProtocolCachePolicy,
NSURLRequestReloadIgnoringCacheData, NSURLRequestReturnCacheDataElseLoad, or
NSURLRequestReturnCacheDataDontLoad.
The default cache policy for an NSURLRequest instance is NSURLRequestUseProtocolCachePolicy. The
NSURLRequestUseProtocolCachePolicy behavior is protocol specific and is defined as being the best
conforming policy for the protocol.
Setting the cache policy to NSURLRequestReloadIgnoringCacheData causes the URL loading system to
load the data from the originating source, ignoring the cache completely.
The NSURLRequestReturnCacheDataElseLoad cache policy will cause the URL loading system to use
cached data ignoring its age or expiration date, if it exists, and load the data from the originating source only
if there is no cached version.
The NSURLRequestReturnCacheDataDontLoad policy allows an application to specify that only data in the
cache should be returned. Attempting to create an NSURLConnection or NSURLDownload instance with this
cache policy returns nil immediately if the response is not in the local cache. This is similar in function to an
“offline” mode and never brings up a network connection.
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
32
Understanding Cache AccessNote: Currently, only responsesto http and https requests are cached. The ftp and file protocols
attempt to access the originating source as allowed by the cache policy. Custom NSURLProtocol
classes can provide caching if they choose.
Cache Use Semantics for the http Protocol
The most complicated cache use situation is when a request uses the http protocol and has set the cache
policy to NSURLRequestUseProtocolCachePolicy.
If an NSCachedURLResponse does not exist for the request, then the data isfetched from the originating source.
If there is a cached response for the request, the URL loading system checks the response to determine if it
specifies that the contents must be revalidated. If the contents must be revalidated a connection is made to
the originating source to see if it has changed. If it has not changed, then the response is returned from the
local cache. If it has changed, the data is fetched from the originating source.
If the cached response doesn’t specify that the contents must be revalidated, the maximum age or expiration
specified in the response is examined. If the cached response is recent enough, then the response is returned
from the local cache. If the response is determined to be stale, the originating source is checked for newer
data. If newer data is available, the data is fetched from the originating source, otherwise it is returned from
the cache.
RFC 2616, Section 13 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13) specifies the semantics
involved in detail.
Understanding Cache Access
Cache Use Semantics for the http Protocol
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
33This table describes the changes to URL Loading System Programming Guide .
Date Notes
2010-09-01 Fixed typos and removed deprecated symbols from code examples.
Restructured content and added discussions of new authentication
functionality.
2010-03-24
2009-08-14 Added links to Cocoa Core Competencies.
2008-05-20 Updated to include content about NSURLDownload availability in iOS.
2008-05-06 Made minor editorial changes.
2007-07-10 Corrected minor typos.
2006-05-23 Added links to sample code.
2006-03-08 Updated sample code.
2005-09-08 Corrected connectionDidFinishLoading: method signature.
2005-04-08 Added accessor method to sample code. Corrected minor typos.
2004-08-31 Corrected minor typos.
Corrected table of contents ordering.
Corrected willSendRequest:redirectResponse: method signature
throughout topic.
2003-07-03
Added additional article outlining differences in behavior between
NSURLDownload and NSURLConnection.
2003-06-11
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
34
Document Revision HistoryDate Notes
First release of conceptual and task material covering the usage of new
classes in Mac OS X v10.2 with Safari 1.0 for downloading content from
the Internet.
2003-06-06
Document Revision History
2010-09-01 | © 2003, 2010 Apple Inc. All Rights Reserved.
35Apple Inc.
© 2003, 2010 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, Cocoa, Mac, Mac OS, OS
X, and Safari are trademarks of Apple Inc.,
registered in the U.S. and other countries.
iOS is a trademark or registered trademark of
Cisco in the U.S. and other countries and is used
under license.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
External Accessory
Programming TopicsContents
About External Accessories 4
At a Glance 4
Including the External Accessory Framework in Your Project 4
Declaring the Protocols Your App Supports 5
Communicating with an Accessory 5
See Also 5
Connecting to an Accessory 6
Monitoring Accessory-Related Events 9
Document Revision History 10
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
2Listings
Connecting to an Accessory 6
Listing 1 Creating a communications session for an accessory 6
Listing 2 Processing stream events 8
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
3The External Accessory framework (ExternalAccessory.framework) provides a conduit for communicating
with accessories attached to any iOS-based device. App developers can use this conduit to integrate
accessory-level features into their apps.
Communicating with an external accessory requires you to work closely with the accessory manufacturer to
understand the services provided by that accessory. Manufacturers must build explicit support into their
accessory hardware for communicating with iOS. As part of this support, an accessory must support at least
one command protocol, which is a custom scheme for sending data back and forth between the accessory
and an attached app. Apple does not maintain a registry of protocols; it is up to the manufacturer to decide
which protocols to support and whether to use custom protocols or standard protocols supported by other
manufacturers.
As part of your communication with the accessory manufacturer, you must find out what protocols a given
accessory supports. To prevent namespace conflicts, protocol names are specified as reverse-DNS strings of
the form com.apple.myProtocol. This allows each manufacturer to define as many protocols as needed to
support their line of accessories.
Note: If you are interested in becoming a developer of accessories for iPad, iPhone, or iPod touch,
you can find information about how to do so on http://developer.apple.com.
At a Glance
Communicating with accessories requires information about the accessory itself, which you must obtain from
the hardware manufacturer. From there, you use the classes of the External Accessory framework to create the
bridge between the hardware and your app.
Including the External Accessory Framework in Your Project
To use the features of the External Accessory framework, you must add ExternalAccessory.framework
to your Xcode project and link against it in any relevant targets. To access the classes and headers of the
framework, include an #import statement at the top of
any relevant source files.
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
4
About External AccessoriesDeclaring the Protocols Your App Supports
Appsthat are able to communicate with an external accessory must declare the protocolsthey support in their
Info.plist file. Declaring support for specific protocols lets the system know that your app can be launched
when that accessory is connected. If no app supports the connected accessory, the system may choose to
launch the App Store and point out apps that do.
To declare the protocols your app supports, you must include the
UISupportedExternalAccessoryProtocols key in your app’s Info.plist file. This key contains an array
ofstringsthat identify the communications protocolsthat your app supports. Your app can include any number
of protocols in this list and the protocols can be in any order. The system does not use this list to determine
which protocol your app should choose; it uses it only to determine if your app is capable of communicating
with the accessory. It is up to your code to choose an appropriate communications protocol when it begins
talking to the accessory.
For more information about the keys you put into your app’s Info.plist file, see Information Property List
Key Reference .
Communicating with an Accessory
An app communicates with an accessory by creating an EASession object for managing the accessory
interactions. Session objects work with the underlying system to transfer data packetsto and from the accessory.
Data transfer in your app occurs through NSInputStream and NSOutputStream objects, which are vended
by the session object once the connection is made. To receive data from the accessory, monitor the input
stream using a custom delegate object. To send data to the accessory, write data packets to the output stream.
The format of the incoming and outgoing data packetsis determined by the protocol you use to communicate
with the accessory.
Relevant Article: “Connecting to an Accessory” (page 6), “Monitoring Accessory-Related
Events” (page 9)
See Also
For information about the classes of the External Accessory framework, see External Accessory Framework
Reference
About External Accessories
See Also
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
5Accessories are not visible through the External Accessory framework until they have been connected by the
system and made ready for use. When an accessory does become visible, your app can get the appropriate
accessory object and open a session using one or more of the protocols supported by the accessory.
The shared EAAccessoryManager object provides the main entry point for apps looking to communicate
with accessories. This class contains an array of already connected accessory objects that you can enumerate
to see if there is one your app supports. Most of the information in an EAAccessory object (such asthe name,
manufacturer, and model information) is intended for display purposes only. To determine whether your app
can connect to an accessory, you must look at the accessory’s protocols and see if there is one your app
supports.
Note: It is possible for more than one accessory object to support the same protocol. If that happens,
your code is responsible for choosing which accessory object to use.
For a given accessory object, only one session at a time is allowed for a specific protocol. The protocolStrings
property of each EAAccessory object contains a dictionary whose keys are the supported protocols. If you
attempt to create a session using a protocol that is already in use, the External Accessory framework closes
the existing session before opening the new one.
Listing 1 shows a method that checks the list of connected accessories and grabs the first one that the app
supports. It creates a session for the designated protocol and configures the input and output streams of the
session. By the time this method returnsthe session object, it is connected to the accessory and ready to begin
sending and receiving data.
Listing 1 Creating a communications session for an accessory
- (EASession *)openSessionForProtocol:(NSString *)protocolString
{
NSArray *accessories = [[EAAccessoryManager sharedAccessoryManager]
connectedAccessories];
EAAccessory *accessory = nil;
EASession *session = nil;
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
6
Connecting to an Accessoryfor (EAAccessory *obj in accessories)
{
if ([[obj protocolStrings] containsObject:protocolString])
{
accessory = obj;
break;
}
}
if (accessory)
{
session = [[EASession alloc] initWithAccessory:accessory
forProtocol:protocolString];
if (session)
{
[[session inputStream] setDelegate:self];
[[session inputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[[session inputStream] open];
[[session outputStream] setDelegate:self];
[[session outputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[[session outputStream] open];
[session autorelease];
}
}
return session;
}
After the input and output streams are configured, the final step is to process the stream-related data. Listing
2 shows the fundamental structure of a delegate’s stream processing code. This method responds to events
from both input and output streams of the accessory. As the accessory sends data to your app an event arrives
indicating there are bytes available to be read. Similarly, when the accessory is ready to receive data from your
app, events arrive indicating that fact. (Of course, your app does not always have to wait for an event to arrive
Connecting to an Accessory
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
7before it can write bytes to the stream. It can also call the stream’s hasBytesAvailable method to see if the
accessory is still able to receive data.) For more information on streams and handling stream-related events,
see Stream Programming Guide .
Listing 2 Processing stream events
// Handle communications from the streams.
- (void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent
{
switch (streamEvent)
{
case NSStreamHasBytesAvailable:
// Process the incoming stream data.
break;
case NSStreamEventHasSpaceAvailable:
// Send the next queued command.
break;
default:
break;
}
}
Connecting to an Accessory
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
8The External Accessory framework is capable of sending notifications whenever a hardware accessory is
connected or disconnected. Although it is capable, it does not do so automatically. Your app must specifically
request that notifications be generated by calling the registerForLocalNotifications method of the
EAAccessoryManager class. When an accessory is connected, authenticated, and ready to interact with your
app, the framework sends an EAAccessoryDidConnectNotification notification. When an accessory is
disconnected, it sends an EAAccessoryDidDisconnectNotification notification. You can register to
receive these notifications using the default NSNotificationCenter, and both notifications include
information about which accessory was affected.
In addition to receiving notificationsthrough the default notification center, an app that is currently interacting
with an accessory can assign a delegate to the corresponding EAAccessory object and be notified of changes.
Delegate objects must conform to the EAAccessoryDelegateprotocol, which currently contains the optional
accessoryDidDisconnect: method. You can use this method to receive disconnection notices without
first setting up a notification observer.
If your app is suspended in the background when an accessory notification arrives, that notification is put in
a queue. When your app begins running again (either in the foreground or background), notifications in the
queue are delivered to your app. Notifications are also coalesced and filtered wherever possible to eliminate
any irrelevant events. For example, if an accessory was connected and subsequently disconnected while your
app was suspended, your app would ultimately not receive any indication that such events took place.
For more information about how to register to receive notifications, see Notification Programming Topics.
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
9
Monitoring Accessory-Related EventsThis table describes the changes to External Accessory Programming Topics.
Date Notes
2012-02-24 Clarified that protocols must be declared in an app's Info.plist file.
Corrected information about what happens when you connect to an
existing session.
2011-09-22
2010-05-26 New document describing how to attach to external hardware devices.
2012-02-24 | © 2012 Apple Inc. All Rights Reserved.
10
Document Revision HistoryApple Inc.
© 2012 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, iPad, iPhone, iPod, iPod
touch, and Xcode are trademarks of Apple Inc.,
registered in the U.S. and other countries.
App Store is a service mark of Apple Inc.
iOS is a trademark or registered trademark of
Cisco in the U.S. and other countries and is used
under license.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
Multimedia
Programming GuideContents
About Audio and Video 4
Organization of This Document 4
Using Audio 5
The Basics: Audio Codecs, Supported Audio Formats, and Audio Sessions 6
iOS Hardware and Software Audio Codecs 7
Audio Sessions 9
Playing Audio 11
Playing Audio Items with iPod Library Access 12
Playing UI Sound Effects or Invoking Vibration Using System Sound Services 12
Playing Sounds Easily with the AVAudioPlayer Class 15
Playing Sounds with Control Using Audio Queue Services 17
Playing Sounds with Positioning Using OpenAL 21
Recording Audio 21
Recording with the AVAudioRecorder Class 22
Recording with Audio Queue Services 24
Parsing Streamed Audio 25
Audio Unit Support in iOS 26
Best Practices for iOS Audio 27
Tips for Using Audio 27
Preferred Audio Formats in iOS 28
Using Video 30
Recording and Editing Video 30
Playing Video Files 31
Document Revision History 34
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
2Figures, Tables, and Listings
Using Audio 5
Figure 1-1 Using iPod library access 12
Table 1-1 Audio playback formats and codecs 7
Table 1-2 Audio recording formats and codecs 8
Table 1-3 Features provided by the audio session APIs 9
Table 1-4 Handling audio interruptions 11
Table 1-5 System-supplied audio units 26
Table 1-6 Audio tips 27
Listing 1-1 Creating a sound ID object 13
Listing 1-2 Playing a system sound 14
Listing 1-3 Triggering vibration 14
Listing 1-4 Configuring an AVAudioPlayer object 15
Listing 1-5 Implementing an AVAudioPlayer delegate method 16
Listing 1-6 Controlling an AVAudioPlayer object 17
Listing 1-7 Creating an audio queue object 18
Listing 1-8 Setting the playback level directly 20
Listing 1-9 The AudioQueueLevelMeterState structure 21
Listing 1-10 Setting up the audio session and the sound file URL 22
Listing 1-11 A record/stop method using the AVAudioRecorder class 23
Using Video 30
Figure 2-1 Media player interface with transport controls 31
Listing 2-1 Playing full-screen movies 31
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
3Whether multimedia features are central or incidental to your application, iPhone, iPod touch, and iPad users
expect high quality. When presenting video content, take advantage of the device’s high-resolution screen
and high frame rates. When designing the audio portion of your application, keep in mind that compelling
sound adds immeasurably to a user’s overall experience.
You can take advantage of the iOS multimedia frameworks for adding features like:
● High-quality audio recording, playback, and streaming
●
Immersive game sounds
● Live voice chat
● Playback of content from a user’s iPod library
● Video playback and recording on supported devices
In iOS 4.0 and later, the AV Foundation framework gives you fine-grained control over inspecting, editing, and
presenting audio-visual assets.
Organization of This Document
This document contains the following chapters:
●
“Using Audio” (page 5) shows how to use the system’s audio technologies to play and record audio.
●
“Using Video” (page 30) shows how to use the system’s video technologies to play and capture video.
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
4
About Audio and VideoImportant: This document contains information that used to be in iOS App Programming Guide . The
information in this document has not been updated specifically for iOS 4.0.
iOS offers a rich set of toolsfor working with sound in your application. These tools are arranged into frameworks
according to the features they provide, as follows:
● Use the Media Player framework to play songs, audio books, or audio podcasts from a user’s iPod library.
For details, see Media Player Framework Reference , iPod Library Access Programming Guide , and the
AddMusic sample code project.
● Use the AV Foundation framework to play and record audio using a simple Objective-C interface. For
details, see AV Foundation Framework Reference and the avTouch sample code project.
● Use the Audio Toolbox framework to play audio with synchronization capabilities, access packets of
incoming audio, parse audio streams, convert audio formats, and record audio with access to individual
packets. For details, see Audio Toolbox Framework Reference and the SpeakHere sample code project.
● Use the Audio Unit framework to connect to and use audio processing plug-ins. For details, see Audio
Unit Hosting Guide for iOS .
● Use the OpenAL framework to provide positional audio playback in games and other applications. iOS
supports OpenAL 1.1. For information on OpenAL, see the OpenAL website, OpenAL FAQ for iPhone OS ,
and the oalTouch sample code project.
To allow your code to use the features of an audio framework, add that framework to your Xcode project, link
against it in any relevant targets, and add an appropriate #import statement near the top of relevant source
files. For example, to provide access to the AV Foundation framework in a source file, add a #import
statement near the top of the file. For detailed information on how to
add frameworks to your project, see “Files in Projects” in Xcode Project Management Guide .
Important: To use the features of the Audio Unit framework, add the Audio Toolbox framework to your
Xcode project and link against it in any relevant targets. Then add a #import
statement near the top of relevant source files.
This section on sound provides a quick introduction to implementing iOS audio features, as listed here:
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
5
Using Audio● To play songs, audio podcasts, and audio books from a user’s iPod library, see “Playing Audio Items with
iPod Library Access” (page 12).
● To play and record audio in the fewest lines of code, use the AV Foundation framework. See “Playing
Sounds Easily with the AVAudioPlayer Class” (page 15) and “Recording with the AVAudioRecorder
Class” (page 22).
● To provide full-featured audio playback including stereo positioning, level control, and simultaneous
sounds, use OpenAL. See “Playing Sounds with Positioning Using OpenAL” (page 21).
● To provide lowest latency audio, especially when doing simultaneous input and output (such as for a VoIP
application), use the I/O unit or the Voice Processing I/O unit. See “Audio Unit Support in iOS” (page 26).
● To play sounds with the highest degree of control, including support forsynchronization, use Audio Queue
Services. See “Playing Sounds with Control Using Audio Queue Services” (page 17). Audio Queue Services
also supports recording and provides access to incoming audio packets, as described in “Recording with
Audio Queue Services” (page 24).
● To parse audio streamed from a network connection, use Audio File Stream Services. See “Parsing Streamed
Audio” (page 25).
● To play user-interface sound effects, or to invoke vibration on devicesthat provide that feature, use System
Sound Services. See “Playing UI Sound Effects or Invoking Vibration Using System Sound Services” (page
12).
Be sure to read the nextsection,“The Basics: Audio Codecs, Supported Audio Formats, and Audio Sessions” (page
6), for critical information on how audio works in iOS. Also read “Best Practices for iOS Audio” (page 27),
which offers guidelines and liststhe audio and file formatsto use for best performance and best user experience.
When you’re ready to dig deeper, the iOS Dev Center contains guides, reference books, sample code, and
more. For tips on how to perform common audio tasks, see Audio & Video Coding How-To's. For in-depth
explanations of audio development in iOS, see Core Audio Overview, Audio Session Programming Guide , Audio
Queue Services Programming Guide , Audio Unit Hosting Guide for iOS , and iPod Library Access Programming
Guide .
The Basics: Audio Codecs, Supported Audio Formats, and Audio
Sessions
To get oriented toward iOS audio development, it’s important to understand a few critical things about the
hardware and software architecture of iOS devices—described in this section.
Using Audio
The Basics: Audio Codecs, Supported Audio Formats, and Audio Sessions
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
6iOS Hardware and Software Audio Codecs
To ensure optimum performance and quality, you need to pick the right audio format and audio codec type.
Starting in iOS 3.0, most audio formats can use software-based encoding (for recording) and decoding (for
playback). Software codecs support simultaneous playback of multiple sounds, but may entail significant CPU
overhead.
Hardware-assisted decoding provides excellent performance—but does not support simultaneous playback
of multiple sounds. If you need to maximize video frame rate in your application, minimize the CPU impact of
your audio playback by using uncompressed audio or the IMA4 format, or use hardware-assisted decoding of
your compressed audio assets.
For best-practice advice on picking an audio format, see “Preferred Audio Formats in iOS” (page 28).
Table 1-1 describes the playback audio codecs available on iOS devices.
Table 1-1 Audio playback formats and codecs
Hardware-assisted Software-based decoding
decoding
Audio decoder/playback format
AAC (MPEG-4 Advanced Audio Coding) Yes Yes, starting in iOS 3.0
ALAC (Apple Lossless) Yes Yes, starting in iOS 3.0
HE-AAC (MPEG-4 High Efficiency AAC) Yes -
iLBC (internet Low Bitrate Codec, another format - Yes
for speech)
IMA4 (IMA/ADPCM) - Yes
Linear PCM (uncompressed, linear pulse-code - Yes
modulation)
MP3 (MPEG-1 audio layer 3) Yes Yes, starting in iOS 3.0
µ-law and a-law - Yes
When using hardware-assisted decoding, the device can play only a single instance of one of the supported
formats at a time. For example, if you are playing a stereo MP3 sound using the hardware codec, a second
simultaneous MP3 sound will use software decoding. Similarly, you cannot simultaneously play an AAC and
an ALAC sound using hardware. If the iPod application is playing an AAC or MP3 sound in the background, it
has claimed the hardware codec; your application then plays AAC, ALAC, and MP3 audio using software
decoding.
Using Audio
The Basics: Audio Codecs, Supported Audio Formats, and Audio Sessions
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
7To play multiple sounds with best performance, or to efficiently play sounds while the iPod is playing in the
background, use linear PCM (uncompressed) or IMA4 (compressed) audio.
To learn how to check at runtime which hardware and software codecs are available on a device, read the
discussion for the kAudioFormatProperty_HardwareCodecCapabilities constant in Audio Format
Services Reference and read Technical Q&A QA1663, “Determining the availability of the AAC hardware encoder
at runtime.”
To summarize how iOS supports audio formats for single or multiple playback:
● Linear PCM and IMA4 (IMA/ADPCM) You can play multiple linear PCM or IMA4 sounds simultaneously
in iOS without incurring CPU resource problems. The same is true for the iLBC speech-quality format, and
for the µ-law and a-law compressed formats. When using compressed formats, check the sound quality
to ensure it meets your needs.
● AAC, HE-AAC, MP3, and ALAC (Apple Lossless) Playback for AAC, HE-AAC, MP3, and ALAC sounds can
use efficient hardware-assisted decoding on iOS devices, but these codecs all share a single hardware
path. The device can play only a single instance of one of these formats at a time using hardware-assisted
decoding.
The single hardware path for AAC, HE-AAC, MP3, and ALAC playback has implications for “play along” style
applications, such as a virtual piano. If the user is playing a song in one of these three formats in the iPod
application, then your application—to play along over that audio—will employ software decoding.
Table 1-2 describes the recording audio codecs available on iOS devices.
Table 1-2 Audio recording formats and codecs
Audio encoder/recording format Hardware-assisted encoding Software-based encoding
Yes, starting in iOS 4.0 for
iPhone 3GS and iPod
touch (2nd generation)
Yes, starting in iOS 3.1 for
iPhone 3GS and iPod touch
(2nd generation)
Yes, starting in iOS 3.2 for
iPad
AAC (MPEG-4 Advanced Audio Coding)
ALAC (Apple Lossless) - Yes
iLBC (internet Low Bitrate Codec, for - Yes
speech)
IMA4 (IMA/ADPCM) - Yes
Linear PCM (uncompressed, linear - Yes
pulse-code modulation)
Using Audio
The Basics: Audio Codecs, Supported Audio Formats, and Audio Sessions
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
8Audio encoder/recording format Hardware-assisted encoding Software-based encoding
µ-law and a-law - Yes
Audio Sessions
The iOS audio session APIs let you define your application’s general audio behavior and design it to work well
within the larger audio context of the device it’s running on. These APIs are described in Audio Session Services
Reference and AVAudioSession Class Reference . Using these APIs, you can specify such behaviors as:
● Whether or not your audio should be silenced by the Silent switch (on iPhone, this is called the Ring/Silent
switch )
● Whether or not your audio should stop upon screen lock
● Whether other audio, such as from the iPod, should continue playing or be silenced when your audio
starts
The audio session APIs also let you respond to user actions, such as the plugging in or unplugging of headsets,
and to events that use the device’s sound hardware, such as Clock and Calendar alarms and incoming phone
calls.
The audio session APIs provide three programmatic features, described in Table 1-3.
Table 1-3 Features provided by the audio session APIs
Audio session feature Description
A category is a key that identifies a set of audio behaviorsfor your application.
By setting a category, you indicate your audio intentions to iOS, such as
whether your audio should continue when the screen locks. There are six
categories, described in “Audio Session Categories”. You can fine-tune the
behavior of some categories, as explained in “Fine-Tuning the Category” in
Audio Session Programming Guide .
Setting categories
Your audio session posts messages when your audio is interrupted, when an
interruption ends, and when the hardware audio route changes. These
messages let you respond gracefully to changes in the larger audio
environment—such as an interruption due to an incoming phone call. For
details, see “Handling Audio Hardware Route Changes” and “Handling Audio
Interruptions”.
Handling interruptions
and route changes
You can query the audio session to discover characteristics of the device your
application isrunning on,such as hardware sample rate, number of hardware
channels, and whether audio input is available. For details, see “Optimizing
for Device Hardware”.
Optimizing for
hardware
characteristics
Using Audio
The Basics: Audio Codecs, Supported Audio Formats, and Audio Sessions
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
9There are two interfaces for working with the audio session:
● A streamlined, objective-C interface that gives you accessto the core audio session features and is described
in AVAudioSession Class Reference and AVAudioSessionDelegate Protocol Reference .
● A C-based interface that provides comprehensive access to all basic and advanced audio session features
and is described in Audio Session Services Reference .
You can mix and match audio session code from AV Foundation and Audio Session Services—the interfaces
are completely compatible.
An audio session comes with some default behavior that you can use to get started in development. However,
except for certain special cases, the default behavior is unsuitable for a shipping application that uses audio.
For example, when using the default audio session, audio in your application stops when the Auto-Lock period
times out and the screen locks. If you want to ensure that playback continues with the screen locked, include
the following lines in your application’s initialization code:
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error: &setCategoryErr];
[[AVAudioSession sharedInstance]
setActive: YES
error: &activationErr];
The AVAudioSessionCategoryPlayback category ensures that playback continues when the screen locks.
Activating the audio session puts the specified category into effect.
How you handle the interruption caused by an incoming phone call or Clock or Calendar alarm depends on
the audio technology you are using, as shown in Table 1-4.
Using Audio
The Basics: Audio Codecs, Supported Audio Formats, and Audio Sessions
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
10Table 1-4 Handling audio interruptions
Audio technology How interruptions work
The AVAudioPlayer and AVAudioRecorder classes provide delegate
methods for interruption start and end. Implement these methods to
update your user interface and optionally, after interruption ends, to
resume paused playback. The system automatically pauses playback or
recording upon interruption, and reactivates your audio session when
you resume playback or recording.
If you want to save and restore playback position between application
launches,save playback position on interruption as well as on application
quit.
AV Foundation framework
These technologies put your application in control of handling
interruptions. You are responsible for saving playback or recording
position and reactivating your audio session after interruption ends.
Implement the AVAudioSession interruption delegate methods or
write an interruption listener callback function.
Audio Queue Services,
I/O audio unit
When using OpenAL for playback, implement the AVAudioSession
interruption delegate methods or write an interruption listener callback
function—as when using Audio Queue Services. However, the delegate
or callback must additionally manage the OpenAL context.
OpenAL
Sounds played using System Sound Services go silent when an
interruption starts. They can automatically be used again if the
interruption ends. Applications cannotinfluence the interruption behavior
for sounds that use this playback technology.
System Sound Services
Every iOS application—with rare exception—should actively manage its audio session. For a complete
explanation of how to do this,read Audio Session ProgrammingGuide . To ensure that your application conforms
to Apple recommendations for audio session behavior, read “Sound” in iOS Human Interface Guidelinesin iOS
Human Interface Guidelines.
Playing Audio
This section introduces you to playing sounds in iOS using iPod library access, System Sound Services, Audio
Queue Services, the AV Foundation framework, and OpenAL.
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
11Playing Audio Items with iPod Library Access
Starting in iOS 3.0, iPod library accesslets your application play a user’ssongs, audio books, and audio podcasts.
The API design makes basic playback very simple while also supporting advanced searching and playback
control.
As shown in Figure 1-1, your application has two ways to retrieve media items. The media item picker, shown
on the left, is an easy-to-use, pre-packaged view controller that behaves like the built-in iPod application’s
music selection interface. For many applications, this is sufficient. If the picker doesn’t provide the specialized
access control you want, the media query interface will. Itsupports predicate-based specification of itemsfrom
the iPod library.
Figure 1-1 Using iPod library access
iPod Library
Music Player
Media Picker Media Query
Your application
As depicted in the figure to the right of your application, you then play the retrieved media items using the
music player provided by this API.
For a complete explanation of how to add media item playback to your application, see iPod Library Access
Programming Guide . For a code example, see the AddMusic sample code project.
Playing UI Sound Effects or Invoking Vibration Using System Sound Services
To play user-interface sound effects (such as button clicks), or to invoke vibration on devices that support it,
use System Sound Services. This compact interface is described in System Sound Services Reference . You can
find sample code in the Audio UI Sounds (SysSound) sample in the iOS Dev Center.
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
12Note: Sounds played with System Sound Services are not subject to configuration using your audio
session. As a result, you cannot keep the behavior of System Sound Services audio in line with other
audio behavior in your application. This is the most important reason to avoid using System Sound
Services for any audio apart from its intended uses.
The AudioServicesPlaySystemSound function lets you very simply play short sound files. The simplicity
carries with it a few restrictions. Your sound files must be:
● No longer than 30 seconds in duration
●
In linear PCM or IMA4 (IMA/ADPCM) format
● Packaged in a .caf, .aif, or .wav file
In addition, when you use the AudioServicesPlaySystemSound function:
● Sounds play at the current system audio volume, with no programmatic volume control available
● Sounds play immediately
● Looping and stereo positioning are unavailable
● Simultaneous playback is unavailable: You can play only one sound at a time
The similar AudioServicesPlayAlertSound function plays a shortsound as an alert. If a user has configured
their device to vibrate in Ring Settings, calling this function invokes vibration in addition to playing the sound
file.
Note: System-supplied alert sounds and system-supplied user-interface sound effects are not
available to your application. For example, using the kSystemSoundID_UserPreferredAlert
constant as a parameter to the AudioServicesPlayAlertSound function will not play anything.
To play a sound with the AudioServicesPlaySystemSound or AudioServicesPlayAlertSound function,
first create a sound ID object, as shown in Listing 1-1.
Listing 1-1 Creating a sound ID object
// Get the main bundle for the app
CFBundleRef mainBundle = CFBundleGetMainBundle ();
// Get the URL to the sound file to play. The file in this case
// is "tap.aif"
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
13soundFileURLRef = CFBundleCopyResourceURL (
mainBundle,
CFSTR ("tap"),
CFSTR ("aif"),
NULL
);
// Create a system sound object representing the sound file
AudioServicesCreateSystemSoundID (
soundFileURLRef,
&soundFileObject
);
Then play the sound, as shown in Listing 1-2.
Listing 1-2 Playing a system sound
- (IBAction) playSystemSound {
AudioServicesPlaySystemSound (self.soundFileObject);
}
In typical use, which includes playing a sound occasionally or repeatedly, retain the sound ID object until your
application quits. If you know that you will use a sound only once—for example, in the case of a startup
sound—you can destroy the sound ID object immediately after playing the sound, freeing memory.
Applicationsrunning on iOS devicesthatsupport vibration can trigger that feature using System Sound Services.
You specify the vibrate option with the kSystemSoundID_Vibrate identifier. To trigger it, use the
AudioServicesPlaySystemSound function, as shown in Listing 1-3.
Listing 1-3 Triggering vibration
#import
#import
- (void) vibratePhone {
AudioServicesPlaySystemSound (kSystemSoundID_Vibrate);
}
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
14If your application is running on an iPod touch, this code does nothing.
Playing Sounds Easily with the AVAudioPlayer Class
The AVAudioPlayer class provides a simple Objective-C interface for playing sounds. If your application does
not require stereo positioning or precise synchronization, and if you are not playing audio captured from a
network stream, Apple recommends that you use this class for playback.
Using an audio player you can:
● Play sounds of any duration
● Play sounds from files or memory buffers
● Loop sounds
● Play multiple sounds simultaneously (although not with precise synchronization)
● Control relative playback level for each sound you are playing
● Seek to a particular point in a sound file, which supports application features such as fast forward and
rewind
● Obtain audio power data that you can use for audio level metering
The AVAudioPlayer class lets you play sound in any audio format available in iOS, as described in Table
1-1 (page 7). For a complete description of this class’s interface, see AVAudioPlayer Class Reference .
To configure an audio player:
1. Assign a sound file to the audio player.
2. Prepare the audio player for playback, which acquires the hardware resources it needs.
3. Designate an audio player delegate object, which handlesinterruptions as well asthe playback-completed
event.
The code in Listing 1-4 illustratesthese steps. It would typically go into an initialization method of the controller
class for your application. (In production code, you’d include appropriate error handling.)
Listing 1-4 Configuring an AVAudioPlayer object
// in the corresponding .h file:
// @property (nonatomic, retain) AVAudioPlayer *player;
// in the .m file:
@synthesize player; // the player object
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
15NSString *soundFilePath =
[[NSBundle mainBundle] pathForResource: @"sound"
ofType: @"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
AVAudioPlayer *newPlayer =
[[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: nil];
[fileURL release];
self.player = newPlayer;
[newPlayer release];
[player prepareToPlay];
[player setDelegate: self];
The delegate (which can be your controller object) handles interruptions and updates the user interface when
a sound has finished playing. The delegate methods for the AVAudioPlayer class are described in
AVAudioPlayerDelegate Protocol Reference . Listing 1-5 shows a simple implementation of one delegatemethod.
This code updates the title of a Play/Pause toggle button when a sound has finished playing.
Listing 1-5 Implementing an AVAudioPlayer delegate method
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player
successfully: (BOOL) completed {
if (completed == YES) {
[self.button setTitle: @"Play" forState: UIControlStateNormal];
}
}
To play, pause, or stop an AVAudioPlayer object, call one of its playback control methods. You can test
whether or not playback is in progress by using the playing property. Listing 1-6 shows a basic play/pause
toggle method that controls playback and updates the title of a UIButton object.
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
16Listing 1-6 Controlling an AVAudioPlayer object
- (IBAction) playOrPause: (id) sender {
// if already playing, then pause
if (self.player.playing) {
[self.button setTitle: @"Play" forState: UIControlStateHighlighted];
[self.button setTitle: @"Play" forState: UIControlStateNormal];
[self.player pause];
// if stopped or paused, start playing
} else {
[self.button setTitle: @"Pause" forState: UIControlStateHighlighted];
[self.button setTitle: @"Pause" forState: UIControlStateNormal];
[self.player play];
}
}
The AVAudioPlayer class uses the Objective-C declared properties feature for managing information about a
sound—such as the playback point within the sound’s timeline, and for accessing playback options—such as
volume and looping. For example, you can set the playback volume for an audio player as shown here:
[self.player setVolume: 1.0]; // available range is 0.0 through 1.0
For more information on the AVAudioPlayer class, see AVAudioPlayer Class Reference .
Playing Sounds with Control Using Audio Queue Services
Audio Queue Services adds playback capabilities beyond those available with the AVAudioPlayer class. Using
Audio Queue Services for playback lets you:
● Precisely schedule when a sound plays, allowing synchronization
● Precisely control volume on a buffer-by-buffer basis
● Play audio that you have captured from a stream using Audio File Stream Services
Audio Queue Services lets you play sound in any audio format available in iOS, as described in Table 1-1 (page
7). You can also use this technology for recording, as explained in “Recording Audio” (page 21).
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
17For detailed information on using this technology, see Audio Queue Services Programming Guide and Audio
Queue Services Reference . For sample code, see the SpeakHere sample.
Creating an Audio Queue Object
To create an audio queue object for playback, perform these three steps:
1. Create a data structure to manage information needed by the audio queue, such as the audio format for
the data you want to play.
2. Define a callback function for managing audio queue buffers. The callback uses Audio File Servicesto read
the file you want to play. (In iOS 2.1 and later, you can also use Extended Audio File Services to read the
file.)
3. Instantiate the playback audio queue using the AudioQueueNewOutput function.
Listing 1-7 illustrates these steps using ANSI C. (In production code, you’d include appropriate error handling.)
The SpeakHere sample project shows these same steps in the context of a C++ program.
Listing 1-7 Creating an audio queue object
static const int kNumberBuffers = 3;
// Create a data structure to manage information needed by the audio queue
struct myAQStruct {
AudioFileID mAudioFile;
CAStreamBasicDescription mDataFormat;
AudioQueueRef mQueue;
AudioQueueBufferRef mBuffers[kNumberBuffers];
SInt64 mCurrentPacket;
UInt32 mNumPacketsToRead;
AudioStreamPacketDescription *mPacketDescs;
bool mDone;
};
// Define a playback audio queue callback function
static void AQTestBufferCallback(
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inCompleteAQBuffer
) {
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
18myAQStruct *myInfo = (myAQStruct *)inUserData;
if (myInfo->mDone) return;
UInt32 numBytes;
UInt32 nPackets = myInfo->mNumPacketsToRead;
AudioFileReadPackets (
myInfo->mAudioFile,
false,
&numBytes,
myInfo->mPacketDescs,
myInfo->mCurrentPacket,
&nPackets,
inCompleteAQBuffer->mAudioData
);
if (nPackets > 0) {
inCompleteAQBuffer->mAudioDataByteSize = numBytes;
AudioQueueEnqueueBuffer (
inAQ,
inCompleteAQBuffer,
(myInfo->mPacketDescs ? nPackets : 0),
myInfo->mPacketDescs
);
myInfo->mCurrentPacket += nPackets;
} else {
AudioQueueStop (
myInfo->mQueue,
false
);
myInfo->mDone = true;
}
}
// Instantiate an audio queue object
AudioQueueNewOutput (
&myInfo.mDataFormat,
AQTestBufferCallback,
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
19&myInfo,
CFRunLoopGetCurrent(),
kCFRunLoopCommonModes,
0,
&myInfo.mQueue
);
Controlling the Playback Level
Audio queue objects give you two ways to control playback level.
To set playback level directly, use the AudioQueueSetParameter function with the
kAudioQueueParam_Volume parameter, as shown in Listing 1-8. Level change takes effect immediately.
Listing 1-8 Setting the playback level directly
Float32 volume = 1; // linear scale, range from 0.0 through 1.0
AudioQueueSetParameter (
myAQstruct.audioQueueObject,
kAudioQueueParam_Volume,
volume
);
You can also set playback level for an audio queue buffer by using the
AudioQueueEnqueueBufferWithParameters function. This lets you assign audio queue settings that are,
in effect, carried by an audio queue buffer as you enqueue it. Such changes take effect when the buffer begins
playing.
In both cases, level changes for an audio queue remain in effect until you change them again.
Indicating Playback Level
You can obtain the current playback level from an audio queue object by:
1. Enabling metering for the audio queue object by setting its
kAudioQueueProperty_EnableLevelMetering property to true
2. Querying the audio queue object’s kAudioQueueProperty_CurrentLevelMeter property
Using Audio
Playing Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
20The value of this property is an array of AudioQueueLevelMeterState structures, one per channel. Listing
1-9 shows this structure:
Listing 1-9 The AudioQueueLevelMeterState structure
typedef struct AudioQueueLevelMeterState {
Float32 mAveragePower;
Float32 mPeakPower;
}; AudioQueueLevelMeterState;
Playing Multiple Sounds Simultaneously
To play multiple sounds simultaneously, create one playback audio queue object for each sound. For each
audio queue, schedule the first buffer of audio to start at the same time using the
AudioQueueEnqueueBufferWithParameters function.
Starting in iOS 3.0, nearly all supported audio formats can be used for simultaneous playback—namely, all
those that can be played using software decoding, as described in Table 1-1 (page 7). For the most
processor-efficient multiple playback, use linear PCM (uncompressed) or IMA4 (compressed) audio.
Playing Sounds with Positioning Using OpenAL
The open-sourced OpenAL audio API, available in iOS in the OpenAL framework, provides an interface optimized
for positioning sounds in a stereo field during playback. Playing, positioning, and moving sounds works just
asit does on other platforms. OpenAL also lets you mix sounds. OpenAL usesthe I/O unit for playback, resulting
in the lowest latency.
For all of these reasons, OpenAL is your best choice for playing sounds in game applications on iOS-based
devices. However, OpenAL is also a good choice for general iOS application audio playback needs.
OpenAL 1.1 support in iOS is built on top of Core Audio. For more information, see OpenAL FAQ for iPhone OS .
For OpenAL documentation, see the OpenAL website at http://openal.org. For sample code, see oalTouch .
Recording Audio
iOS supports audio recording using the AVAudioRecorder class and Audio Queue Services. These interfaces
do the work of connecting to the audio hardware, managing memory, and employing codecs as needed. You
can record audio in any of the formats listed in Table 1-2 (page 8).
Using Audio
Recording Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
21Recording takes place at a system-defined input level in iOS. The system takes input from the audio source
that the user has chosen—the built-in microphone or, if connected, the headset microphone or other input
source.
Recording with the AVAudioRecorder Class
The easiest way to record sound in iOS is with the AVAudioRecorder class, described in AVAudioRecorder
Class Reference . This class provides a highly-streamlined, Objective-C interface that makes it easy to provide
sophisticated features like pausing/resuming recording and handling audio interruptions. At the same time,
you retain complete control over recording format.
To prepare for recording using an audio recorder:
1. Specify a sound file URL.
2. Set up the audio session.
3. Configure the audio recorder’s initial state.
Application launch is a good time to do this part of the setup, as shown in Listing 1-10. Variables such as
soundFileURL and recording in this example are declared in the class interface. (In production code, you
would include appropriate error handling.)
Listing 1-10 Setting up the audio session and the sound file URL
- (void) viewDidLoad {
[super viewDidLoad];
NSString *tempDir = NSTemporaryDirectory ();
NSString *soundFilePath =
[tempDir stringByAppendingString: @"sound.caf"];
NSURL *newURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
self.soundFileURL = newURL;
[newURL release];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
audioSession.delegate = self;
[audioSession setActive: YES error: nil];
Using Audio
Recording Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
22recording = NO;
playing = NO;
}
To handle interruptions and the completion of recording, add the AVAudioSessionDelegate and
AVAudioRecorderDelegate protocol names to the interface declaration for your implementation. If your
application also does playback, also adopt the AVAudioPlayerDelegate Protocol Reference protocol.
To implement a record method, you can use code such as that shown in Listing 1-11. (In production code, you
would include appropriate error handling.)
Listing 1-11 A record/stop method using the AVAudioRecorder class
- (IBAction) recordOrStop: (id) sender {
if (recording) {
[soundRecorder stop];
recording = NO;
self.soundRecorder = nil;
[recordOrStopButton setTitle: @"Record" forState:
UIControlStateNormal];
[recordOrStopButton setTitle: @"Record" forState:
UIControlStateHighlighted];
[[AVAudioSession sharedInstance] setActive: NO error: nil];
} else {
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryRecord
error: nil];
NSDictionary *recordSettings =
[[NSDictionary alloc] initWithObjectsAndKeys:
Using Audio
Recording Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
23[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax],
AVEncoderAudioQualityKey,
nil];
AVAudioRecorder *newRecorder =
[[AVAudioRecorder alloc] initWithURL: soundFileURL
settings: recordSettings
error: nil];
[recordSettings release];
self.soundRecorder = newRecorder;
[newRecorder release];
soundRecorder.delegate = self;
[soundRecorder prepareToRecord];
[soundRecorder record];
[recordOrStopButton setTitle: @"Stop" forState: UIControlStateNormal];
[recordOrStopButton setTitle: @"Stop" forState: UIControlStateHighlighted];
recording = YES;
}
}
For more information on the AVAudioRecorder class, see AVAudioRecorder Class Reference .
Recording with Audio Queue Services
To set up for recording with audio with Audio Queue Services, your application instantiates a recording audio
queue object and provides a callback function. The callback storesincoming audio data in memory for immediate
use or writes it to a file for long-term storage.
Just as with playback, you can obtain the current recording audio level from an audio queue object by querying
its kAudioQueueProperty_CurrentLevelMeter property, as described in “Indicating Playback Level” (page
20).
Using Audio
Recording Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
24For detailed examples of how to use Audio Queue Services to record audio, see “Recording Audio” in Audio
Queue Services Programming Guide . For sample code, see the SpeakHere sample.
Parsing Streamed Audio
To play streamed audio content, such as from a network connection, use Audio File Stream Services in concert
with Audio Queue Services. Audio File Stream Services parses audio packets and metadata from common audio
file container formats in a network bitstream. You can also use it to parse packets and metadata from on-disk
files.
In iOS, you can parse the same audio file and bitstream formats that you can in Mac OS X, as follows:
● MPEG-1 Audio Layer 3, used for .mp3 files
● MPEG-2 ADTS, used for the .aac audio data format
● AIFC
● AIFF
● CAF
● MPEG-4, used for .m4a, .mp4, and .3gp files
● NeXT
● WAVE
Having retrieved audio packets, you can play back the recovered sound in any of the formats supported in
iOS, as listed in Table 1-1 (page 7).
For best performance, network streaming applications should use data from Wi-Fi connections. iOS lets you
determine which networks are reachable and available through its System Configuration framework and its
SCNetworkReachabilityRef opaque type, described in SCNetworkReachability Reference . Forsample code,
see the Reachability sample in the iOS Dev Center.
To connect to a network stream, use interfaces from Core Foundation, such as the one described in
CFHTTPMessage Reference . Parse the network packetsto recover audio packets using Audio File StreamServices.
Then buffer the audio packets and send them to a playback audio queue object.
Audio File Stream Services relies on interfaces from Audio File Services, such as the
AudioFramePacketTranslation structure and the AudioFilePacketTableInfo structure. These are
described in Audio File Services Reference .
For more information on using streams, refer to Audio File Stream Services Reference .
Using Audio
Parsing Streamed Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
25Audio Unit Support in iOS
iOS provides a set of audio processing plug-ins, known as audio units, that you can use in any application. The
interfaces in the Audio Unit framework let you open, connect, and use these audio units.
To use the features of the Audio Unit framework, add the Audio Toolbox framework to your Xcode project and
link against it in any relevant targets. Then add a #import statement
near the top of relevant source files. For detailed information on how to add frameworks to your project, see
“Files in Projects” in Xcode Project Management Guide .
Table 1-5 lists the audio units provided in iOS.
Table 1-5 System-supplied audio units
Audio unit Description
The iPod EQ unit, of type kAudioUnitSubType_AUiPodEQ, provides a
simple, preset-based equalizer you can use in your application. For a
demonstration of how to use this audio unit,see the sample code project
iPhoneMixerEQGraphTest.
iPod Equalizer unit
The 3DMixer unit, oftype kAudioUnitSubType_AU3DMixerEmbedded,
lets you mix multiple audio streams, specify stereo output panning,
manipulate playback rate, and more. OpenAL is built on top of this audio
unit and provides a higher-level API well suited for game apps.
3D Mixer unit
The Multichannel Mixer unit, of type kAudioUnitSubType_-
MultiChannelMixer, lets you mix multiple mono or stereo audio
streams to a single stereo stream. It also supports left/right panning for
each input. For a demonstration of how to use this audio unit, see the
sample code project Audio Mixer (MixerHost).
Multichannel Mixer unit
The Remote I/Ounit, oftype kAudioUnitSubType_RemoteIO, connects
to audio input and output hardware and supports realtime I/O. For
demonstrations of how to use this audio unit,see the sample code project
aurioTouch .
Remote I/O unit
The Voice Processing I/O unit, of type kAudioUnitSubType_-
VoiceProcessingIO, has the characteristics of the I/O unit and adds
echo suppression and other features for two-way communication.
Voice Processing I/O unit
The Generic Output unit, of type kAudioUnitSubType_-
GenericOutput, supports converting to and from linear PCM format;
can be used to start and stop a graph.
Generic Output unit
Using Audio
Audio Unit Support in iOS
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
26Audio unit Description
The Converter unit, of type kAudioUnitSubType_AUConverter, lets
you convert audio data from one format to another. You typically obtain
the features of this audio unit by using the Remote I/O unit, which
incorporates a Converter unit.
Converter unit
For more information on using system audio units, see Audio Unit Hosting Guide for iOS . For reference
documentation, see Audio Unit Framework Reference and Audio Unit Processing Graph Services Reference . The
iOS Dev Center provides two sample-code projects that demonstrate use of system audio units: aurioTouch
and iPhoneMultichannelMixerTest.
Best Practices for iOS Audio
This section lists some important tips for using audio in iOS and describes the best audio data formats for
various uses.
Tips for Using Audio
Table 1-6 lists some important tips to keep in mind when using audio in iOS.
Table 1-6 Audio tips
Tip Action
For AAC, MP3, and ALAC (Apple Lossless) audio, decoding can take place
using hardware-assisted codecs. While efficient, this is limited to one audio
stream at a time. If you need to play multiple sounds simultaneously, store
those sounds using the IMA4 (compressed) or linear PCM (uncompressed)
format.
Use compressed audio
appropriately
The afconvert tool in Mac OS X lets you convert to a wide range of audio
data formats and file types. See “Preferred Audio Formats in iOS” (page 28)
and the afconvert man page.
Convert to the data
format and file format
you need
When playing sound with Audio Queue Services, you write a callback that
sends short segments of audio data to audio queue buffers. In some cases,
loading an entire sound file to memory for playback, which minimizes disk
access, is best. In other cases, loading just enough data at a time to keep
the buffers full is best. Test and evaluate which strategy works best for your
application.
Evaluate audiomemory
issues
Using Audio
Best Practices for iOS Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
27Tip Action
Sample rate and the number of bits per sample have a direct impact on the
size of your audio files. If you need to play many such sounds, or
long-duration sounds, consider reducing these valuesto reduce the memory
footprint of the audio data. For example, rather than use 44.1 kHz sampling
rate for sound effects, you could use a 32 kHz (or possibly lower) sample
rate and still provide reasonable quality.
Using monophonic (single-channel) audio instead of stereo (two channel)
reduces file size. For each sound asset, consider whether mono could suit
your needs.
Reduce audio file sizes
by limiting sample
rates, bit depths, and
channels
Use OpenAL when you want a convenient, high-level interface for positioning
sounds in a stereo field or when you need low latency playback. To parse
audio packetsfrom a file or a network stream, use Audio File Stream Services.
For simple playback of single or multiple sounds, use the AVAudioPlayer
class. For recording to a file, use the AVAudioRecorder class. For audio
chat, use the Voice Processing I/O unit. To play audio resources synced from
a user’s iTunes library, use iPod Library Access. When your sole audio need
is to play alerts and user-interface sound effects, use Core Audio’s System
Sound Services. For other audio applications, including playback ofstreamed
audio, precise synchronization, and access to packets of incoming audio,
use Audio Queue Services.
Pick the appropriate
technology
For the lowest possible playback latency, use OpenAL or use the I/O unit
directly.
Code for low latency
Preferred Audio Formats in iOS
For uncompressed (highest quality) audio, use 16-bit, little endian, linear PCM audio data packaged in a CAF
file. You can convert an audio file to this format in Mac OS X using the afconvert command-line tool, as
shown here:
/usr/bin/afconvert -f caff -d LEI16 {INPUT} {OUTPUT}
The afconvert tool lets you convert to a wide range of audio data formats and file types. See the afconvert
man page, and enter afconvert -h at a shell prompt, for more information.
For compressed audio when playing one sound at a time, and when you don’t need to play audio simultaneously
with the iPod application, use the AAC format packaged in a CAF or m4a file.
Using Audio
Best Practices for iOS Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
28For less memory usage when you need to play multiple sounds simultaneously, use IMA4 (IMA/ADPCM)
compression. This reduces file size but entails minimal CPU impact during decompression. As with linear PCM
data, package IMA4 data in a CAF file.
Using Audio
Best Practices for iOS Audio
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
29Important: This document contains information that used to be in iOS App Programming Guide . The
information in this document has not been updated specifically for iOS 4.0.
Recording and Editing Video
Starting in iOS 3.0, you can record video, with included audio, on supported devices. To display the video
recording interface, create and push a UIImagePickerController object, just asfor displaying the still-camera
interface.
To record video, you must first check that the camera source type
(UIImagePickerControllerSourceTypeCamera) is available and that the movie media type
(kUTTypeMovie) is available for the camera. Depending on the media types you assign to the mediaTypes
property, the picker can directly display the still camera or the video camera, or a selection interface that lets
the user choose.
Using the UIImagePickerControllerDelegate protocol, register as a delegate of the image picker. Your
delegate object receives a completed video recording by way of the
imagePickerController:didFinishPickingMediaWithInfo: method.
On supported devices, you can also pick previously-recorded videos from a user’s photo library.
For more information on using the image picker class, see UIImagePickerController Class Reference . For
information on trimming recorded videos, see UIVideoEditorController Class Reference and
UIVideoEditorControllerDelegate Protocol Reference .
In iOS 4.0 and later, you can record from a device’s camera and display the incoming data live on screen. You
use AVCaptureSession to manage data flow from inputs represented by AVCaptureInput objects (which
mediate input from an AVCaptureDevice) to outputs represented by AVCaptureOutput.
In iOS 4.0 and later, you can edit, assemble, and compose video using existing assets or with new raw materials.
Assets are represented by AVAsset, which you can inspect asynchronously for better performance. You use
AVMutableComposition to compose media from one or more sources, then AVAssetExportSession to
encode output of a composition for delivery.
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
30
Using VideoPlaying Video Files
Important: The information in this section currently reflects the usage of the Media Player framework in
iOS 3.1 and earlier. Please see the headers for information about changes to this framework in iOS 4.0.
iOS supportsthe ability to play back video files directly from your application using the Media Player framework,
described in Media Player Framework Reference . Video playback is supported in full screen mode only and can
be used by game developers who want to play short animations or by any developers who want to play media
files. When you start a video from your application, the media player interface takes over, fading the screen to
black and then fading in the video content. You can play a video with or without user controls for adjusting
playback. Enabling some or all of these controls (shown in Figure 2-1) gives the user the ability to change the
volume, change the playback point, or start and stop the video. If you disable all of these controls, the video
plays until completion.
Figure 2-1 Media player interface with transport controls
To initiate video playback, you must know the URL of the file you want to play. For files your application
provides, this would typically be a pointer to a file in your application’s bundle; however, it can also be a pointer
to a file on a remote server. Use this URL to instantiate a new instance of the MPMoviePlayerController
class. This class presides over the playback of your video file and manages user interactions, such as user taps
in the transport controls (if shown). To start playback, call the play method described in MPMediaPlayback
Protocol Reference .
Listing 2-1 shows a sample method that plays back the video at a specified URL. The play method is an
asynchronous call that returns control to the caller while the movie plays. The movie controller loadsthe movie
in a full-screen view, and animates the movie into place on top of the application’s existing content. When
playback is finished, the movie controller sends a notification received by the application controller object, which
releases the movie controller now that it is no longer needed.
Listing 2-1 Playing full-screen movies
-(void) playMovieAtURL: (NSURL*) theURL {
Using Video
Playing Video Files
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
31MPMoviePlayerController* theMovie =
[[MPMoviePlayerController alloc] initWithContentURL: theURL];
theMovie.scalingMode = MPMovieScalingModeAspectFill;
theMovie.movieControlMode = MPMovieControlModeHidden;
// Register for the playback finished notification
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(myMovieFinishedCallback:)
name: MPMoviePlayerPlaybackDidFinishNotification
object: theMovie];
// Movie playback is asynchronous, so this method returns immediately.
[theMovie play];
}
// When the movie is done, release the controller.
-(void) myMovieFinishedCallback: (NSNotification*) aNotification
{
MPMoviePlayerController* theMovie = [aNotification object];
[[NSNotificationCenter defaultCenter]
removeObserver: self
name: MPMoviePlayerPlaybackDidFinishNotification
object: theMovie];
// Release the movie instance created in playMovieAtURL:
[theMovie release];
}
For a list of supported video formats, see iOS Technology Overview.
Using Video
Playing Video Files
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
32In iOS 4.0 and later, you can play video using AVPlayer in conjunction with an AVPlayerLayer or an
AVSynchronizedLayer object. You can use AVAudioMix and AVVideoComposition to customize the
audio and video parts of playback respectively. You can also use AVCaptureVideoPreviewLayer to display
video as it is being captured by an input device.
Using Video
Playing Video Files
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
33This table describes the changes to Multimedia Programming Guide .
Date Notes
Clarified usage of AudioServicesPlaySystemSound function in “Playing
UI Sound Effects or Invoking Vibration Using System Sound Services” (page
12).
2010-09-01
Updated Table 1-2 (page 8) for iOS 4.0 by clarifying support for AAC
encoding.
2010-05-27
2010-09-01 | © 2010 Apple Inc. All Rights Reserved.
34
Document Revision HistoryApple Inc.
© 2010 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, iPad, iPhone, iPod, iPod
touch, iTunes, Mac, Mac OS, Objective-C, OS X,
and Xcode are trademarks of Apple Inc.,
registered in the U.S. and other countries.
NeXT is a trademark of NeXT Software, Inc.,
registered in the U.S. and other countries.
iOS is a trademark or registered trademark of
Cisco in the U.S. and other countries and is used
under license.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
Concepts in Objective-C
ProgrammingContents
About the Basic Programming Concepts for Cocoa and Cocoa Touch 7
At a Glance 7
How to Use This Document 7
Prerequisites 8
See Also 8
Class Clusters 9
Without Class Clusters: Simple Concept but Complex Interface 9
With Class Clusters: Simple Concept and Simple Interface 10
Creating Instances 10
Class Clusters with Multiple Public Superclasses 11
Creating Subclasses Within a Class Cluster 12
A True Subclass 12
True Subclasses: An Example 14
A Composite Object 16
A Composite Object: An Example 16
Class Factory Methods 20
Delegates and Data Sources 22
How Delegation Works 22
The Form of Delegation Messages 24
Delegation and the Application Frameworks 25
Becoming the Delegate of a Framework Class 26
Locating Objects Through the delegate Property 27
Data Sources 27
Implementing a Delegate for a Custom Class 27
Introspection 29
Evaluating Inheritance Relationships 29
Method Implementation and Protocol Conformance 30
Object Comparison 31
Object Allocation 34
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
2Object Initialization 35
The Form of Initializers 35
Issues with Initializers 36
Implementing an Initializer 38
Multiple Initializers and the Designated Initializer 40
Model-View-Controller 43
Roles and Relationships of MVC Objects 43
Model Objects Encapsulate Data and Basic Behaviors 43
View Objects Present Information to the User 44
Controller Objects Tie the Model to the View 44
Combining Roles 45
Types of Cocoa Controller Objects 45
MVC as a Compound Design Pattern 47
Design Guidelines for MVC Applications 50
Model-View-Controller in Cocoa (OS X) 52
Object Modeling 53
Entities 53
Attributes 54
Relationships 55
Relationship Cardinality and Ownership 56
Accessing Properties 57
Keys 57
Values 57
Key Paths 58
Object Mutability 60
Why Mutable and Immutable Object Variants? 60
Programming with Mutable Objects 62
Creating and Converting Mutable Objects 62
Storing and Returning Mutable Instance Variables 63
Receiving Mutable Objects 64
Mutable Objects in Collections 66
Outlets 67
Receptionist Pattern 68
The Receptionist Design Pattern in Practice 68
When to Use the Receptionist Pattern 71
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
3
ContentsTarget-Action 73
The Target 73
The Action 74
Target-Action in the AppKit Framework 75
Controls, Cells, and Menu Items 75
Setting the Target and Action 77
Actions Defined by AppKit 78
Target-Action in UIKit 78
Toll-Free Bridging 80
Document Revision History 83
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
4
ContentsFigures, Tables, and Listings
Class Clusters 9
Figure 1-1 A simple hierarchy for number classes 9
Figure 1-2 A more complete number class hierarchy 10
Figure 1-3 Class cluster architecture applied to number classes 10
Figure 1-4 An object that embeds a cluster object 16
Table 1-1 Class clusters and their public superclasses 11
Table 1-2 Derived methods and their possible implementations 13
Delegates and Data Sources 22
Figure 3-1 The mechanism of delegation 23
Figure 3-2 A more realistic sequence involving a delegate 23
Listing 3-1 Sample delegation methods with return values 24
Listing 3-2 Sample delegation methods returning void 24
Introspection 29
Listing 4-1 Using the class and superclass methods 29
Listing 4-2 Using isKindOfClass: 30
Listing 4-3 Using respondsToSelector: 31
Listing 4-4 Using conformsToProtocol: 31
Listing 4-5 Using isEqual: 32
Listing 4-6 Overriding isEqual: 32
Object Initialization 35
Figure 6-1 Initialization up the inheritance chain 39
Figure 6-2 Interactions of secondary and designated initializers 41
Model-View-Controller 43
Figure 7-1 Traditional version of MVC as a compound pattern 48
Figure 7-2 Cocoa version of MVC as a compound design pattern 48
Figure 7-3 Coordinating controller as the owner of a nib file 50
Object Modeling 53
Figure 8-1 Employee management application object diagram 54
Figure 8-2 Employees table view 55
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
5Figure 8-3 Relationships in the employee management application 56
Figure 8-4 Relationship cardinality 56
Figure 8-5 Object graph for the employee management application 58
Figure 8-6 Employees table view showing department name 59
Object Mutability 60
Listing 9-1 Returning an immutable copy of a mutable instance variable 63
Listing 9-2 Making a snapshot of a potentially mutable object 65
Receptionist Pattern 68
Figure 11-1 Bouncing KVO updates to the main operation queue 69
Listing 11-1 Declaring the receptionist class 69
Listing 11-2 The class factory method for creating a receptionist object 70
Listing 11-3 Handling the KVO notification 71
Listing 11-4 Creating a receptionist object 71
Target-Action 73
Figure 12-1 How the target-action mechanism works in the control-cell architecture 76
Toll-Free Bridging 80
Table 13-1 Data types that can be used interchangeably between Core Foundation and Foundation 81
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
6
Figures, Tables, and ListingsMany of the programmatic interfaces of the Cocoa and Cocoa Touch frameworks only make sense only if you
are aware of the concepts on which they are based. These concepts express the rationale for many of the core
designs of the frameworks. Knowledge of these concepts will illuminate your software-development practices.
Model layer View layer
Controller layer
Application
delegate
System frameworks
At a Glance
This document contains articles that explain central concepts, design patterns, and mechanisms of the Cocoa
and Cocoa Touch frameworks. The articles are arranged in alphabetical order.
How to Use This Document
If you read this document cover-to-cover, you learn important information about Cocoa and Cocoa Touch
application development. However, most readers come to the articles in this document in one of two ways:
● Other documents—especially those that are intended for novice iOS and OS X developers—which link to
these articles.
●
In-line mini-articles (which appear when you click a dash-underlined word or phrase) that have a link to
an article as a “Definitive Discussion.”
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
7
About the Basic Programming Concepts for Cocoa
and Cocoa TouchPrerequisites
Prior programming experience, especially with object-oriented languages, is recommended.
See Also
The Objective-C Programming Language offers further discussion of many of the language-related concepts
covered in this document.
About the Basic Programming Concepts for Cocoa and Cocoa Touch
Prerequisites
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
8Class clusters are a design pattern that the Foundation framework makes extensive use of. Class clusters group
a number of private concrete subclasses under a public abstract superclass. The grouping of classes in this way
simplifies the publicly visible architecture of an object-oriented framework without reducing its functional
richness. Class clusters are based on the Abstract Factory design pattern.
Without Class Clusters: Simple Concept but Complex Interface
To illustrate the class cluster architecture and its benefits, consider the problem of constructing a class hierarchy
that defines objects to store numbers of different types (char, int, float, double). Because numbers of
different types have many features in common (they can be converted from one type to another and can be
represented as strings, for example), they could be represented by a single class. However, their storage
requirements differ,so it’sinefficient to represent them all by the same class. Taking thisfact into consideration,
one could design the class architecture depicted in Figure 1-1 to solve the problem.
Figure 1-1 A simple hierarchy for number classes
Number is the abstract superclass that declares in its methods the operations common to its subclasses.
However, it doesn’t declare an instance variable to store a number. The subclasses declare such instance
variables and share in the programmatic interface declared by Number.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
9
Class ClustersSo far, this design is relatively simple. However, if the commonly used modifications of these basic C types are
taken into account, the class hierarchy diagram looks more like Figure 1-2.
Figure 1-2 A more complete number class hierarchy
The simple concept—creating a class to hold number values—can easily burgeon to over a dozen classes. The
class cluster architecture presents a design that reflects the simplicity of the concept.
With Class Clusters: Simple Concept and Simple Interface
Applying the class cluster design pattern to this problem yieldsthe class hierarchy in Figure 1-3 (private classes
are in gray).
Figure 1-3 Class cluster architecture applied to number classes
Users of this hierarchy see only one public class, Number, so how is it possible to allocate instances of the
proper subclass? The answer is in the way the abstract superclass handles instantiation.
Creating Instances
The abstract superclass in a class cluster must declare methods for creating instances of its private subclasses.
It’s the superclass’s responsibility to dispense an object of the proper subclass based on the creation method
that you invoke—you don’t, and can’t, choose the class of the instance.
In the Foundation framework, you generally create an object by invoking a +className... method or the
alloc... and init... methods. Taking the Foundation framework’s NSNumber class as an example, you
could send these messages to create number objects:
Class Clusters
With Class Clusters: Simple Concept and Simple Interface
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
10NSNumber *aChar = [NSNumber numberWithChar:’a’];
NSNumber *anInt = [NSNumber numberWithInt:1];
NSNumber *aFloat = [NSNumber numberWithFloat:1.0];
NSNumber *aDouble = [NSNumber numberWithDouble:1.0];
You are not responsible for releasing the objects returned from factory methods. Many classes also provide
the standard alloc... and init... methodsto create objectsthat require you to manage their deallocation.
Each object returned—aChar, anInt, aFloat, and aDouble—may belong to a different private subclass
(and in fact does). Although each object’s class membership is hidden, itsinterface is public, being the interface
declared by the abstract superclass, NSNumber. Although it is not precisely correct, it’s convenient to consider
the aChar, anInt, aFloat, and aDouble objects to be instances of the NSNumber class, because they’re
created by NSNumber class methods and accessed through instance methods declared by NSNumber.
Class Clusters with Multiple Public Superclasses
In the example above, one abstract public class declares the interface for multiple private subclasses. This is a
class cluster in the purest sense. It’s also possible, and often desirable, to have two (or possibly more) abstract
public classes that declare the interface for the cluster. This is evident in the Foundation framework, which
includes the clusters listed in Table 1-1.
Table 1-1 Class clusters and their public superclasses
Class cluster Public superclasses
NSData
NSData
NSMutableData
NSArray
NSArray
NSMutableArray
NSDictionary
NSDictionary
NSMutableDictionary
NSString
NSString
NSMutableString
Class Clusters
Class Clusters with Multiple Public Superclasses
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
11Other clusters of this type also exist, but these clearly illustrate how two abstract nodes cooperate in declaring
the programmatic interface to a class cluster. In each of these clusters, one public node declares methods that
all cluster objects can respond to, and the other node declares methods that are only appropriate for cluster
objects that allow their contents to be modified.
This factoring of the cluster’s interface helps make an object-oriented framework’s programmatic interface
more expressive. For example, imagine an object representing a book that declares this method:
- (NSString *)title;
The book object could return its own instance variable or create a new string object and return that—it doesn’t
matter. It’s clear from this declaration that the returned string can’t be modified. Any attempt to modify the
returned object will elicit a compiler warning.
Creating Subclasses Within a Class Cluster
The class cluster architecture involves a trade-off between simplicity and extensibility: Having a few public
classes stand in for a multitude of private ones makes it easier to learn and use the classes in a framework but
somewhat harder to create subclasses within any of the clusters. However, if it’s rarely necessary to create a
subclass, then the cluster architecture is clearly beneficial. Clusters are used in the Foundation framework in
just these situations.
If you find that a cluster doesn’t provide the functionality your program needs, then a subclass may be in order.
For example, imagine that you want to create an array object whose storage is file-based rather than
memory-based, as in the NSArray class cluster. Because you are changing the underlying storage mechanism
of the class, you’d have to create a subclass.
On the other hand, in some cases it might be sufficient (and easier) to define a class that embeds within it an
object from the cluster. Let’s say that your program needs to be alerted whenever some data is modified. In
this case, creating a simple class that wraps a data object that the Foundation framework defines may be the
best approach. An object of this class could intervene in messages that modify the data, intercepting the
messages, acting on them, and then forwarding them to the embedded data object.
In summary, if you need to manage your object’sstorage, create a true subclass. Otherwise, create a composite
object, one that embeds a standard Foundation framework object in an object of your own design. The following
sections give more detail on these two approaches.
A True Subclass
A new class that you create within a class cluster must:
Class Clusters
Creating Subclasses Within a Class Cluster
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
12● Be a subclass of the cluster’s abstract superclass
● Declare its own storage
● Override all initializer methods of the superclass
● Override the superclass’s primitive methods (described below)
Because the cluster’s abstract superclass is the only publicly visible node in the cluster’s hierarchy, the first
point is obvious. This implies that the new subclass will inherit the cluster’s interface but no instance variables,
because the abstract superclass declares none. Thus the second point: The subclass must declare any instance
variables it needs. Finally, the subclass must override any method it inherits that directly accesses an object’s
instance variables. Such methods are called primitive methods.
A class’s primitive methodsform the basisfor itsinterface. For example, take the NSArray class, which declares
the interface to objects that manage arrays of objects. In concept, an array stores a number of data items, each
of which is accessible by index. NSArray expresses this abstract notion through its two primitive methods,
count and objectAtIndex:. With these methods as a base, other methods—derived methods—can be
implemented; Table 1-2 gives two examples of derived methods.
Table 1-2 Derived methods and their possible implementations
Derived Method Possible Implementation
Find the last object by sending the array object this message: [self
objectAtIndex: ([self count] –1)].
lastObject
Find an object by repeatedly sending the array object an objectAtIndex:
message, each time incrementing the index until all objects in the array have
been tested.
containsObject:
The division of an interface between primitive and derived methods makes creating subclasses easier. Your
subclass must override inherited primitives, but having done so can be sure that all derived methods that it
inherits will operate properly.
The primitive-derived distinction applies to the interface of a fully initialized object. The question of how
init... methods should be handled in a subclass also needs to be addressed.
In general, a cluster’s abstract superclass declares a number of init... and + className methods. As
described in “Creating Instances” (page 10), the abstract class decides which concrete subclass to instantiate
based your choice of init... or + className method. You can consider that the abstract class declares
these methods for the convenience of the subclass. Since the abstract class has no instance variables, it has
no need of initialization methods.
Class Clusters
Creating Subclasses Within a Class Cluster
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
13Your subclass should declare its own init... (if it needs to initialize its instance variables) and possibly +
className methods. It should not rely on any of those that it inherits. To maintain its link in the initialization
chain, it should invoke its superclass’s designated initializer within its own designated initializer method. It
should also override all other inherited initializer methods and implement them to behave in a reasonable
manner. (See ““The Runtime System”“ in The Objective-C Programming Language for a discussion of designated
initializers.) Within a class cluster, the designated initializer of the abstract superclass is always init.
True Subclasses: An Example
Let’s say that you want to create a subclass of NSArray, named MonthArray, that returns the name of a
month given its index position. However, a MonthArray object won’t actually store the array of month names
as an instance variable. Instead, the method that returns a name given an index position (objectAtIndex:)
will return constantstrings. Thus, only twelve string objects will be allocated, no matter how many MonthArray
objects exist in an application.
The MonthArray class is declared as:
#import
@interface MonthArray : NSArray
{
}
+ monthArray;
- (unsigned)count;
- (id)objectAtIndex:(unsigned)index;
@end
Note that the MonthArray class doesn’t declare an init... method because it has no instance variables to
initialize. The count and objectAtIndex: methodssimply cover the inherited primitive methods, as described
above.
The implementation of the MonthArray class looks like this:
#import "MonthArray.h"
@implementation MonthArray
Class Clusters
Creating Subclasses Within a Class Cluster
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
14static MonthArray *sharedMonthArray = nil;
static NSString *months[] = { @"January", @"February", @"March",
@"April", @"May", @"June", @"July", @"August", @"September",
@"October", @"November", @"December" };
+ monthArray
{
if (!sharedMonthArray) {
sharedMonthArray = [[MonthArray alloc] init];
}
return sharedMonthArray;
}
- (unsigned)count
{
return 12;
}
- objectAtIndex:(unsigned)index
{
if (index >= [self count])
[NSException raise:NSRangeException format:@"***%s: index
(%d) beyond bounds (%d)", sel_getName(_cmd), index,
[self count] - 1];
else
return months[index];
}
@end
Because MonthArray overridesthe inherited primitive methods, the derived methodsthat it inherits will work
properly without being overridden. NSArray’s lastObject, containsObject:,
sortedArrayUsingSelector:, objectEnumerator, and other methods work without problems for
MonthArray objects.
Class Clusters
Creating Subclasses Within a Class Cluster
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
15A Composite Object
By embedding a private cluster object in an object of your own design, you create a composite object. This
composite object can rely on the cluster object for its basic functionality, only intercepting messages that the
composite object wants to handle in some particular way. This architecture reduces the amount of code you
must write and lets you take advantage of the tested code provided by the Foundation Framework. Figure 1-4
depicts this architecture.
Figure 1-4 An object that embeds a cluster object
The composite object must declare itself to be a subclass of the cluster’s abstract superclass. As a subclass, it
must override the superclass’s primitive methods. It can also override derived methods, but this isn’t necessary
because the derived methods work through the primitive ones.
The count method of the NSArray class is an example; the intervening object’s implementation of a method
it overrides can be as simple as:
- (unsigned)count {
return [embeddedObject count];
}
However, your object could put code for its own purposes in the implementation of any method it overrides.
A Composite Object: An Example
To illustrate the use of a composite object, imagine you want a mutable array object that tests changes against
some validation criteria before allowing any modification to the array’s contents. The example that follows
describes a class called ValidatingArray, which contains a standardmutable array object. ValidatingArray
overrides all of the primitive methods declared in its superclasses, NSArray and NSMutableArray. It also
declares the array, validatingArray, and init methods, which can be used to create and initialize an
instance:
#import
Class Clusters
Creating Subclasses Within a Class Cluster
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
16@interface ValidatingArray : NSMutableArray
{
NSMutableArray *embeddedArray;
}
+ validatingArray;
- init;
- (unsigned)count;
- objectAtIndex:(unsigned)index;
- (void)addObject:object;
- (void)replaceObjectAtIndex:(unsigned)index withObject:object;
- (void)removeLastObject;
- (void)insertObject:object atIndex:(unsigned)index;
- (void)removeObjectAtIndex:(unsigned)index;
@end
The implementation file shows how, in an init method of the ValidatingArrayclass, the embedded object
is created and assigned to the embeddedArray variable. Messages that simply access the array but don’t
modify its contents are relayed to the embedded object. Messagesthat could change the contents are scrutinized
(here in pseudocode) and relayed only if they pass the hypothetical validation test.
#import "ValidatingArray.h"
@implementation ValidatingArray
- init
{
self = [super init];
if (self) {
embeddedArray = [[NSMutableArray allocWithZone:[self zone]] init];
}
return self;
}
Class Clusters
Creating Subclasses Within a Class Cluster
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
17+ validatingArray
{
return [[[self alloc] init] autorelease];
}
- (unsigned)count
{
return [embeddedArray count];
}
- objectAtIndex:(unsigned)index
{
return [embeddedArray objectAtIndex:index];
}
- (void)addObject:object
{
if (/* modification is valid */) {
[embeddedArray addObject:object];
}
}
- (void)replaceObjectAtIndex:(unsigned)index withObject:object;
{
if (/* modification is valid */) {
[embeddedArray replaceObjectAtIndex:index withObject:object];
}
}
- (void)removeLastObject;
{
if (/* modification is valid */) {
[embeddedArray removeLastObject];
Class Clusters
Creating Subclasses Within a Class Cluster
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
18}
}
- (void)insertObject:object atIndex:(unsigned)index;
{
if (/* modification is valid */) {
[embeddedArray insertObject:object atIndex:index];
}
}
- (void)removeObjectAtIndex:(unsigned)index;
{
if (/* modification is valid */) {
[embeddedArray removeObjectAtIndex:index];
}
}
Class Clusters
Creating Subclasses Within a Class Cluster
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
19Class factory methods are implemented by a class as a convenience for clients. They combine allocation and
initialization in one step and return the created object. However, the client receiving this object does not own
the object and thus (per the object-ownership policy) is not responsible for releasing it. These methods are of
the form + (type)className... (where className excludes any prefix).
Cocoa provides plenty of examples, especially among the “value” classes. NSDate includes the following class
factory methods:
+ (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;
+ (id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)secs;
+ (id)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;
And NSData offers the following factory methods:
+ (id)dataWithBytes:(const void *)bytes length:(unsigned)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length
freeWhenDone:(BOOL)b;
+ (id)dataWithContentsOfFile:(NSString *)path;
+ (id)dataWithContentsOfURL:(NSURL *)url;
+ (id)dataWithContentsOfMappedFile:(NSString *)path;
Factory methods can be more than a simple convenience. They can not only combine allocation and initialization,
but the allocation can inform the initialization. As an example, let’s say you must initialize a collection object
from a property-list file that encodes any number of elements for the collection (NSString objects, NSData
objects, NSNumber objects, and so on). Before the factory method can know how much memory to allocate
for the collection, it must read the file and parse the property list to determine how many elements there are
and what object type these elements are.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
20
Class Factory MethodsAnother purpose for a classfactory method isto ensure that a certain class(NSWorkspace, for example) vends
a singleton instance. Although an init... method could verify that only one instance exists at any one time
in a program, it would require the prior allocation of a “raw” instance and then, in memory-managed code,
would have to release that instance. A factory method, on the other hand, gives you a way to avoid blindly
allocating memory for an object that you might not use, as in the following example:
static AccountManager *DefaultManager = nil;
+ (AccountManager *)defaultManager {
if (!DefaultManager) DefaultManager = [[self allocWithZone:NULL] init];
return DefaultManager;
}
Class Factory Methods
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
21A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters
an event in a program. The delegating object is often a responder object—that is, an object inheriting from
NSResponder in AppKit or UIResponder in UIKit—that is responding to a user event. The delegate is an
object that is delegated control of the user interface for that event, or is at least asked to interpret the event
in an application-specific manner.
To better appreciate the value of delegation, it helps to consider an off-the-shelf Cocoa object such as a text
field (an instance of NSTextField or UITextField) or a table view (an instance of NSTableView or
UITableView ). These objects are designed to fulfill a specific role in a generic fashion; a window object in
the AppKit framework, for example, responds to mouse manipulations of its controls and handles such things
as closing, resizing, and moving the physical window. This restricted and generic behavior necessarily limits
what the object can know about how an event affects (or will affect) something elsewhere in the application,
especially when the affected behavior isspecific to your application. Delegation provides a way for your custom
object to communicate application-specific behavior to the off-the-shelf object.
The programming mechanism of delegation gives objects a chance to coordinate their appearance and state
with changes occurring elsewhere in a program, changes usually brought about by user actions. More
importantly, delegation makes it possible for one object to alter the behavior of another object without the
need to inherit from it. The delegate is almost always one of your custom objects, and by definition it
incorporates application-specific logic that the generic and delegating object cannot possibly know itself.
How Delegation Works
The design of the delegation mechanism is simple—see Figure 3-1 (page 23). The delegating class has an
outlet or property, usually one that is named delegate; if it is an outlet, it includes methods for setting and
accessing the value of the outlet. It also declares, without implementing, one or more methods that constitute
a formal protocol or an informal protocol. A formal protocol that uses optional methods—a feature ofObjective-C
2.0—is the preferred approach, but both kinds of protocols are used by the Cocoa frameworks for delegation.
In the informal protocol approach, the delegating class declares methods on a category of NSObject, and the
delegate implements only those methods in which it has an interest in coordinating itself with the delegating
object or affecting that object’s default behavior. If the delegating class declares a formal protocol, the delegate
may choose to implement those methods marked optional, but it must implement the required ones.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
22
Delegates and Data SourcesDelegation follows a common design, illustrated by Figure 3-1.
Figure 3-1 The mechanism of delegation
User just clicked close button;
should window close?
No
windowShouldClose:
Don't close. The window
has unsaved data.
windowDelegate
The methods of the protocol mark significant events handled or anticipated by the delegating object. This
object wants either to communicate these events to the delegate or, for impending events, to request input
or approval from the delegate. For example, when a user clicks the close button of a window in OS X, the
window object sends the windowShouldClose: message to its delegate; this gives the delegate the
opportunity to veto or defer the closing of the window if, for example, the window has associated data that
must be saved (see Figure 3-2).
Figure 3-2 A more realistic sequence involving a delegate
Yes
windowShouldClose:
➌
➊
➍ ➋
aWindow aDelegate
The delegating object sends a message only if the delegate implements the method. It makes this discovery
by invoking the NSObjectmethod respondsToSelector: in the delegate first.
Delegates and Data Sources
How Delegation Works
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
23The Form of Delegation Messages
Delegation methods have a conventional form. They begin with the name of the AppKit or UIKit object doing
the delegating—application, window, control, and so on; this name is in lower-case and without the “NS” or
“UI” prefix. Usually (but not always) this object name is followed by an auxiliary verb indicative of the temporal
status of the reported event. This verb, in other words, indicates whether the event is about to occur (“Should”
or “Will”) or whether it has just occurred (“Did” or “Has”). This temporal distinction helps to categorize those
messagesthat expect a return value and those that don’t. Listing 3-1 includes a few AppKit delegation methods
that expect a return value.
Listing 3-1 Sample delegation methods with return values
- (BOOL)application:(NSApplication *)sender
openFile:(NSString *)filename; // NSApplication
- (BOOL)application:(UIApplication *)application
handleOpenURL:(NSURL *)url; // UIApplicationDelegate
- (UITableRowIndexSet *)tableView:(NSTableView *)tableView
willSelectRows:(UITableRowIndexSet *)selection; // UITableViewDelegate
- (NSRect)windowWillUseStandardFrame:(NSWindow *)window
defaultFrame:(NSRect)newFrame; // NSWindow
The delegate that implements these methods can block the impending event (by returning NO in the first two
methods) or alter a suggested value (the index set and the frame rectangle in the last two methods). It can
even defer an impending event; for example, the delegate implementing the
applicationShouldTerminate:method can delay application termination by returning NSTerminateLater.
Other delegation methods are invoked by messagesthat don’t expect a return value and so are typed to return
void. These messages are purely informational, and the method names often contain “Did”, “Will”, or some
other indication of a transpired or impending event. Listing 3-2 shows a few examples of these kinds of
delegation method.
Listing 3-2 Sample delegation methods returning void
- (void) tableView:(NSTableView*)tableView
mouseDownInHeaderOfTableColumn:(NSTableColumn *)tableColumn; // NSTableView
- (void)windowDidMove:(NSNotification *)notification; // NSWindow
- (void)application:(UIApplication *)application
willChangeStatusBarFrame:(CGRect)newStatusBarFrame; //
UIApplication
Delegates and Data Sources
The Form of Delegation Messages
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
24- (void)applicationWillBecomeActive:(NSNotification *)notification; //
NSApplication
There are a couple of things to note about this last group of methods. The first is that an auxiliary verb of “Will”
(as in the third method) does not necessarily mean that a return value is expected. In this case, the event is
imminent and cannot be blocked, but the message gives the delegate an opportunity to prepare the program
for the event.
The other point of interest concernsthe second and last method declarationsin Listing 3-2 . The sole parameter
of each of these methods is an NSNotification object, which means that these methods are invoked as the
result of the posting of a particular notification. For example, the windowDidMove: method is associated with
the NSWindow notification NSWindowDidMoveNotification. It’s important to understand the relationship
of notifications to delegation messages in AppKit. The delegating object automatically makes its delegate an
observer of all notifications it posts. All the delegate needs to do is implement the associated method to get
the notification.
To make an instance of your custom class the delegate of an AppKit object, simply connect the instance to the
delegate outlet or property in Interface Builder. Or you can set it programmatically through the delegating
object’s setDelegate: method or delegate property, preferably early on, such as in the awakeFromNib
or applicationDidFinishLaunching: method.
Delegation and the Application Frameworks
The delegating object in a Cocoa or Cocoa Touch application is often a responder object such as a
UIApplication, NSWindow, or NSTableView object. The delegate object itself istypically, but not necessarily,
an object, often a custom object, that controls some part of the application (that is, a coordinating controller
object). The following AppKit classes define a delegate:
● NSApplication
● NSBrowser
● NSControl
● NSDrawer
● NSFontManager
● NSFontPanel
● NSMatrix
● NSOutlineView
● NSSplitView
Delegates and Data Sources
Delegation and the Application Frameworks
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
25● NSTableView
● NSTabView
● NSText
● NSTextField
● NSTextView
● NSWindow
The UIKit framework also uses delegation extensively and always implements it using formal protocols. The
application delegate is extremely important in an application running in iOS because it must respond to
application-launch, application-quit, low-memory, and other messages from the application object. The
application delegate must adopt the UIApplicationDelegate protocol.
Delegating objects do not (and should not) retain their delegates. However, clients of delegating objects
(applications, usually) are responsible for ensuring that their delegates are around to receive delegation
messages. To do this, they may have to retain the delegate in memory-managed code. This precaution applies
equally to data sources, notification observers, and targets of action messages. Note that in a garbage-collection
environment, the reference to the delegate is strong because the retain-cycle problem does not apply.
Some AppKit classes have a more restricted type of delegate called a modal delegate . Objects of these classes
(NSOpenPanel, for example) run modal dialogs that invoke a handler method in the designated delegate
when the user clicksthe dialog’s OK button. Modal delegates are limited in scope to the operation of the modal
dialog.
Becoming the Delegate of a Framework Class
A framework class or any other classthat implements delegation declares a delegate property and a protocol
(usually a formal protocol). The protocol liststhe required and optional methodsthat the delegate implements.
For an instance of your class to function as the delegate of a framework object, it must do the following:
● Set your object asthe delegate (by assigning it to the delegate property). You can do this programmatically
or through Interface Builder.
●
If the protocol is formal, declare that your class adopts the protocol in the class definition. For example:
@interface MyControllerClass : UIViewController {
●
Implement all required methods of the protocol and any optional methods that you want to participate
in.
Delegates and Data Sources
Delegation and the Application Frameworks
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
26Locating Objects Through the delegate Property
The existence of delegates has other programmatic uses. For example, with delegates it is easy for two
coordinating controllers in the same program to find and communicate with each other. For example, the
object controlling the application overall can find the controller of the application’sinspector window (assuming
it’s the current key window) using code similar to the following:
id winController = [[NSApp keyWindow] delegate];
And your code can find the application-controller object—by definition, the delegate of the global application
instance—by doing something similar to the following:
id appController = [NSApp delegate];
Data Sources
A data source is like a delegate except that, instead of being delegated control of the user interface, it is
delegated control of data. A data source is an outlet held by NSView and UIView objects such as table views
and outline views that require a source from which to populate their rows of visible data. The data source for
a view is usually the same object that acts as its delegate, but it can be any object. As with the delegate, the
data source must implement one or more methods of an informal protocol to supply the view with the data
it needs and, in more advanced implementations, to handle data that users directly edit in such views.
As with delegates, data sources are objectsthat must be present to receive messagesfrom the objectsrequesting
data. The application that uses them must ensure their persistence, retaining them if necessary in
memory-managed code.
Data sources are responsible for the persistence of the objectsthey hand out to user-interface objects. In other
words, they are responsible for the memory management of those objects. However, whenever a view object
such as an outline view or table view accesses the data from a data source, it retains the objects as long as it
uses the data. But it does not use the data for very long. Typically it holds on to the data only long enough to
display it.
Implementing a Delegate for a Custom Class
To implement a delegate for your custom class, complete the following steps:
● Declare the delegate accessor methods in your class header file.
Delegates and Data Sources
Data Sources
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
27- (id)delegate;
- (void)setDelegate:(id)newDelegate;
●
Implement the accessor methods. In a memory-managed program, to avoid retain cycles, the setter method
should not retain or copy your delegate.
- (id)delegate {
return delegate;
}
- (void)setDelegate:(id)newDelegate {
delegate = newDelegate;
}
In a garbage-collected environment, where retain cycles are not a problem, you should not make the
delegate a weak reference (by using the __weak type modifier). For more on retain cycles, see Advanced
MemoryManagement ProgrammingGuide . Formore on weak referencesin garbage collection,see “Garbage
Collection for Cocoa Essentials” in Garbage Collection Programming Guide .
● Declare a formal or informal protocol containing the programmatic interface for the delegate. Informal
protocols are categories on the NSObject class. If you declare a formal protocol for your delegate, make
sure you mark groups of optional methods with the @optional directive.
“The Form of Delegation Messages” (page 24) gives advice for naming your own delegation methods.
● Before invoking a delegation method, make sure the delegate implements it by sending it a
respondsToSelector: message.
- (void)someMethod {
if ( [delegate respondsToSelector:@selector(operationShouldProceed)]
) {
if ( [delegate operationShouldProceed] ) {
// do something appropriate
}
}
}
The precaution is necessary only for optional methods in a formal protocol or methods of an informal
protocol.
Delegates and Data Sources
Implementing a Delegate for a Custom Class
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
28Introspection is a powerful feature of object-oriented languages and environments, and introspection in
Objective-C and Cocoa is no exception. Introspection refersto the capability of objectsto divulge details about
themselves as objects at runtime. Such details include an object’s place in the inheritance tree, whether it
conforms to a specific protocol, and whether it responds to a certain message. The NSObject protocol and
class define many introspection methodsthat you can use to query the runtime in order to characterize objects.
Used judiciously, introspection makes an object-oriented program more efficient and robust. It can help you
avoid message-dispatch errors, erroneous assumptions of object equality, and similar problems. The following
sections show how you might effectively use the NSObject introspection methods in your code.
Evaluating Inheritance Relationships
Once you know the class an object belongs to, you probably know quite a bit about the object. You might
know what its capabilities are, what attributes it represents, and what kinds of messages it can respond to.
Even if after introspection you are unfamiliar with the classto which an object belongs, you now know enough
to not send it certain messages.
The NSObject protocol declares several methods for determining an object’s position in the class hierarchy.
These methods operate at different granularities. The class and superclass instance methods, for example,
return the Class objects representing the class and superclass, respectively, of the receiver. These methods
require you to compare one Class object with another. Listing 4-1 gives a simple (one might say trivial)
example of their use.
Listing 4-1 Using the class and superclass methods
// ...
while ( id anObject = [objectEnumerator nextObject] ) {
if ( [self class] == [anObject superclass] ) {
// do something appropriate...
}
}
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
29
IntrospectionNote: Sometimes you use the class or superclass methods to obtain an appropriate receiver
for a class message.
More commonly, to check an object’s class affiliation, you would send it a isKindOfClass: or
isMemberOfClass: message. The former method returns whether the receiver is an instance of a given class
or an instance of any class that inherits from that class. A isMemberOfClass: message, on the other hand,
tells you if the receiver is an instance of the specified class. The isKindOfClass: method is generally more
useful because from it you can know at once the complete range of messages you can send to an object.
Consider the code snippet in Listing 4-2.
Listing 4-2 Using isKindOfClass:
if ([item isKindOfClass:[NSData class]]) {
const unsigned char *bytes = [item bytes];
unsigned int length = [item length];
// ...
}
By learning that the object item inheritsfrom the NSData class, this code knowsit can send it the NSDatabytes
and lengthmessages. The difference between isKindOfClass: and isMemberOfClass: becomes apparent
if you assume that item is an instance of NSMutableData. If you use isMemberOfClass: instead of
isKindOfClass:, the code in the conditionalized block is never executed because item is not an instance
of NSData but rather of NSMutableData, a subclass of NSData.
Method Implementation and Protocol Conformance
Two of the more powerful introspection methods of NSObject are respondsToSelector: and
conformsToProtocol:. These methodstell you, respectively, whether an object implements a certain method
and whether an object conforms to a specified formal protocol (that is, adopts the protocol, if necessary, and
implements all the methods of the protocol).
You use these methodsin a similarsituation in your code. They enable you to discover whethersome potentially
anonymous object can respond appropriately to a particular message or set of messages before you send it
any of those messages. By making this check before sending a message, you can avoid the risk of runtime
exceptionsresulting from unrecognized selectors. The AppKit framework implementsinformal protocols—the
basis of delegation—by checking whether delegates implement a delegation method (using
respondsToSelector:) prior to invoking that method.
Introspection
Method Implementation and Protocol Conformance
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
30Listing 4-3 illustrates how you might use the respondsToSelector: method in your code.
Listing 4-3 Using respondsToSelector:
- (void)doCommandBySelector:(SEL)aSelector {
if ([self respondsToSelector:aSelector]) {
[self performSelector:aSelector withObject:nil];
} else {
[_client doCommandBySelector:aSelector];
}
}
Listing 4-4 illustrates how you might use the conformsToProtocol: method in your code.
Listing 4-4 Using conformsToProtocol:
// ...
if (!([((id)testObject) conformsToProtocol:@protocol(NSMenuItem)])) {
NSLog(@"Custom MenuItem, '%@', not loaded; it must conform to the
'NSMenuItem' protocol.\n", [testObject class]);
[testObject release];
testObject = nil;
}
Object Comparison
Although they are not strictly introspection methods, the hash and isEqual: methods fulfill a similar role.
They are indispensable runtime toolsfor identifying and comparing objects. But instead of querying the runtime
for information about an object, they rely on class-specific comparison logic.
The hash and isEqual: methods, both declared by the NSObject protocol, are closely related. The hash
method must be implemented to return an integer that can be used as a table addressin a hash table structure.
If two objects are equal (as determined by the isEqual: method), they must have the same hash value. If
your object could be included in collections such as NSSet objects, you need to define hash and verify the
invariant that if two objects are equal, they return the same hash value. The default NSObject implementation
of isEqual: simply checks for pointer equality.
Introspection
Object Comparison
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
31Using the isEqual: method is straightforward; it compares the receiver against the object supplied as a
parameter. Object comparison frequently informsruntime decisions about whatshould be done with an object.
As Listing 4-5 illustrates, you can use isEqual: to decide whether to perform an action, in this case to save
user preferences that have been modified.
Listing 4-5 Using isEqual:
- (void)saveDefaults {
NSDictionary *prefs = [self preferences];
if (![origValues isEqual:prefs])
[Preferences savePreferencesToDefaults:prefs];
}
If you are creating a subclass, you might need to override isEqual: to add further checksfor points of equality.
The subclass might define an extra attribute that has to be the same value in two instances for them to be
considered equal. For example, say you create a subclass of NSObject called MyWidget that contains two
instance variables, name and data. Both of these must be the same value for two instances of MyWidget to
be considered equal. Listing 4-6 illustrates how you might implement isEqual: for the MyWidget class.
Listing 4-6 Overriding isEqual:
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return [self isEqualToWidget:other];
}
- (BOOL)isEqualToWidget:(MyWidget *)aWidget {
if (self == aWidget)
return YES;
if (![(id)[self name] isEqual:[aWidget name]])
return NO;
if (![[self data] isEqualToData:[aWidget data]])
return NO;
return YES;
Introspection
Object Comparison
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
32}
This isEqual: method first checks for pointer equality, then class equality, and finally invokes an object
comparator whose name indicates the class of object involved in the comparison. This type of comparator,
which forcestype checking of the object passed in, is a common convention in Cocoa; the isEqualToString:
method of the NSString class and the isEqualToTimeZone: method of the NSTimeZone class are but two
examples. The class-specific comparator—isEqualToWidget: in this case—performs the checks for name
and data equality.
In all isEqualToType: methods of the Cocoa frameworks, nil is not a valid parameter and implementations
of these methods may raise an exception upon receiving a nil. However, for backward compatibility, isEqual:
methods of the Cocoa frameworks do accept nil, returning NO.
Introspection
Object Comparison
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
33When you allocate an object, part of what happens is what you might expect, given the term. Cocoa allocates
enough memory for the object from a region of application virtual memory. To calculate how much memory
to allocate, it takes the object’s instance variables into account—including their types and order—as specified
by the object’s class.
To allocate an object, you send the message alloc or allocWithZone: to the object’s class. In return, you
get a “raw” (uninitialized) instance of the class. The alloc variant of the method uses the application’s default
zone. A zone is a page-aligned area of memory for holding related objects and data allocated by an application.
See Advanced Memory Management Programming Guide for more information on zones.
An allocation message does other important things besides allocating memory:
●
It sets the object’s retain count to one.
●
It initializes the object’s isainstance variable to point to the object’s class, a runtime object in its own
right that is compiled from the class definition.
●
It initializes all other instance variables to zero (or to the equivalent type for zero, such as nil, NULL, and
0.0).
An object’s isa instance variable is inherited from NSObject, so it is common to all Cocoa objects. After
allocation sets isa to the object’s class, the object is integrated into the runtime’s view of the inheritance
hierarchy and the current network of objects (class and instance) that constitute a program. Consequently an
object can find whatever information it needs at runtime, such as another object’s place in the inheritance
hierarchy, the protocols that other objects conform to, and the location of the method implementations it can
perform in response to messages.
In summary, allocation not only allocates memory for an object but initializes two small but very important
attributes of any object: its isa instance variable and itsretain count. It also sets all remaining instance variables
to zero. But the resulting object is not yet usable. Initializing methods such as init must yet initialize objects
with their particular characteristics and return a functional object.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
34
Object AllocationInitialization sets the instance variables of an object to reasonable and useful initial values. It can also allocate
and prepare other global resources needed by the object, loading them if necessary from an external source
such as a file. Every object that declares instance variables should implement an initializing method—unless
the defaultset-everything-to-zero initialization issufficient. If an object does not implement an initializer, Cocoa
invokes the initializer of the nearest ancestor instead.
The Form of Initializers
NSObject declares the init prototype for initializers; it is an instance method typed to return an object of
type id. Overriding init is fine for subclasses that require no additional data to initialize their objects. But
often initialization depends on external data to set an object to a reasonable initial state. For example, say you
have an Account class; to initialize an Account object appropriately requires a unique account number, and
this must be supplied to the initializer. Thus initializers can take one or more parameters; the only requirement
is that the initializing method begins with the letters “init”. (The stylistic convention init... is sometimes
used to refer to initializers.)
Note: Instead of implementing an initializer with parameters, a subclass can implement only a
simple init method and then use “set” accessor methods immediately after initialization to set the
object to a useful initial state. (Accessor methods enforce encapsulation of object data by setting
and getting the values of instance variables.) Or, if the subclass uses properties and the related access
syntax, it may assign values to the properties immediately after initialization.
Cocoa has plenty of examples of initializers with parameters. Here are a few (with the defining class in
parentheses):
- (id)initWithArray:(NSArray *)array; (from NSSet)
- (id)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate
*)anotherDate; (from NSDate)
- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)aStyle
backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag; (from NSWindow)
- (id)initWithFrame:(NSRect)frameRect; (from NSControl and NSView)
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
35
Object InitializationThese initializers are instance methods that begin with “init” and return an object of the dynamic type id.
Other than that, they follow the Cocoa conventions for multiparameter methods, often using WithType: or
FromSource: before the first and most important parameter.
Issues with Initializers
Although init... methods are required by their method signature to return an object, that object is not
necessarily the one that was most recently allocated—the receiver of the init... message. In other words,
the object you get back from an initializer might not be the one you thought was being initialized.
Two conditions prompt the return of something other than the just-allocated object. The first involves two
related situations: when there must be a singleton instance or when the defining attribute of an object must
be unique. Some Cocoa classes—NSWorkspace, for instance—allow only one instance in a program; a class
in such a case must ensure (in an initializer or, more likely, in a class factory method) that only one instance is
created, returning this instance if there is any further request for a new one.
A similar situation arises when an object is required to have an attribute that makes it unique. Recall the
hypothetical Account class mentioned earlier. An account of any sort must have a unique identifier. If the
initializer for this class—say, initWithAccountID:—is passed an identifier that has already been associated
with an object, it must do two things:
● Release the newly allocated object (in memory-managed code)
● Return the Account object previously initialized with this unique identifier
By doing this, the initializer ensures the uniqueness of the identifier while providing what was asked for: an
Account instance with the requested identifier.
Sometimes an init... method cannot perform the initialization requested. For example, an initFromFile:
method expects to initialize an object from the contents of a file, the path to which is passed as a parameter.
But if no file exists at that location, the object cannot be initialized. A similar problem happens if an
initWithArray: initializer is passed an NSDictionary object instead of an NSArray object. When an
init... method cannot initialize an object, it should:
● Release the newly allocated object (in memory-managed code)
● Return nil
Returning nil from an initializer indicates that the requested object cannot be created. When you create an
object, you should generally check whether the returned value is nil before proceeding:
Object Initialization
Issues with Initializers
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
36id anObject = [[MyClass alloc] init];
if (anObject) {
[anObject doSomething];
// more messages...
} else {
// handle error
}
Because an init... method might return nil or an object other than the one explicitly allocated, it is
dangerous to use the instance returned by alloc or allocWithZone: instead of the one returned by the
initializer. Consider the following code:
id myObject = [MyClass alloc];
[myObject init];
[myObject doSomething];
The init method in this example could have returned nil or could have substituted a different object. Because
you can send a message to nil without raising an exception, nothing would happen in the former case except
(perhaps) a debugging headache. But you should always rely on the initialized instance instead of the “raw”
just-allocated one. Therefore, you should nest the allocation message inside the initialization message and
test the object returned from the initializer before proceeding.
id myObject = [[MyClass alloc] init];
if ( myObject ) {
[myObject doSomething];
} else {
// error recovery...
}
Once an object is initialized, you should not initialize it again. If you attempt a reinitialization, the framework
class of the instantiated object often raises an exception. For example, the second initialization in this example
would result in NSInvalidArgumentException being raised.
NSString *aStr = [[NSString alloc] initWithString:@"Foo"];
aStr = [aStr initWithString:@"Bar"];
Object Initialization
Issues with Initializers
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
37Implementing an Initializer
There are several critical rules to follow when implementing an init... method that serves as a class’s sole
initializer or, if there are multiple initializers, its designated initializer (described in “Multiple Initializers and the
Designated Initializer” (page 40)):
● Always invoke the superclass (super) initializer first.
● Check the object returned by the superclass. If it is nil, then initialization cannot proceed; return nil to
the receiver.
● When initializing instance variables that are references to objects, retain or copy the object as necessary
(in memory-managed code).
● After setting instance variables to valid initial values, return self unless:
●
It was necessary to return a substituted object, in which case release the freshly allocated object first
(in memory-managed code).
● A problem prevented initialization from succeeding, in which case return nil.
- (id)initWithAccountID:(NSString *)identifier {
if ( self = [super init] ) {
Account *ac = [accountDictionary objectForKey:identifier];
if (ac) { // object with that ID already exists
[self release];
return [ac retain];
}
if (identifier) {
accountID = [identifier copy]; // accountID is instance variable
[accountDictionary setObject:self forKey:identifier];
return self;
} else {
[self release];
return nil;
}
} else
return nil;
}
Object Initialization
Implementing an Initializer
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
38Note: Although, for the sake of simplicity, this example returns nil if the parameter is nil, the
better Cocoa practice is to raise an exception.
It isn’t necessary to initialize all instance variables of an object explicitly, just those that are necessary to make
the object functional. The default set-to-zero initialization performed on an instance variable during allocation
is often sufficient. Make sure that you retain or copy instance variables, as required for memory management.
The requirement to invoke the superclass’s initializer as the first action is important. Recall that an object
encapsulates not only the instance variables defined by its class but the instance variables defined by all of its
ancestor classes. By invoking the initializer of super first, you help to ensure that the instance variables defined
by classes up the inheritance chain are initialized first. The immediate superclass, in its initializer, invokes the
initializer of its superclass, which invokes the main init... method of its superclass, and so on (see Figure
6-1). The proper order of initialization is critical because the later initializations of subclasses may depend on
superclass-defined instance variables being initialized to reasonable values.
Figure 6-1 Initialization up the inheritance chain
super
super
Class A
Class B
Class C
inherits from
inherits from
self
- (id)initWithName:birthday:
- (id)initWithName:
Instance variables:
NSString *name:
Instance variables:
NSString *name:
NSDate *dob:
- (id)initWithName:birthday:ssn:
Instance variables:
NSString *name:
NSDate *dob:
NSNumber *ssn:
sets
sets
sets
Object Initialization
Implementing an Initializer
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
39Inherited initializers are a concern when you create a subclass. Sometimes a superclass init... method
sufficiently initializes instances of your class. But because it is more likely it won’t, you should override the
superclass’s initializer. If you don’t, the superclass’s implementation is invoked, and because the superclass
knows nothing about your class, your instances may not be correctly initialized.
Multiple Initializers and the Designated Initializer
A class can define more than one initializer. Sometimes multiple initializers let clients of the class provide the
input for the same initialization in different forms. The NSSet class, for example, offers clientsseveral initializers
that accept the same data in different forms; one takes an NSArray object, another a counted list of elements,
and another a nil-terminated list of elements:
- (id)initWithArray:(NSArray *)array;
- (id)initWithObjects:(id *)objects count:(unsigned)count;
- (id)initWithObjects:(id)firstObj, ...;
Some subclasses provide convenience initializers that supply default values to an initializer that takes the full
complement of initialization parameters. Thisinitializer is usually the designated initializer, the most important
initializer of a class. For example, assume there is a Task class and it declares a designated initializer with this
signature:
- (id)initWithTitle:(NSString *)aTitle date:(NSDate *)aDate;
The Task class might include secondary, or convenience, initializersthatsimply invoke the designated initializer,
passing it default values for those parameters the secondary initializer doesn’t explicitly request. This example
shows a designated initializer and a secondary initializer.
- (id)initWithTitle:(NSString *)aTitle {
return [self initWithTitle:aTitle date:[NSDate date]];
}
- (id)init {
return [self initWithTitle:@"Task"];
}
Object Initialization
Multiple Initializers and the Designated Initializer
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
40The designated initializer plays an important role for a class. It ensures that inherited instance variables are
initialized by invoking the designated initializer of the superclass. It is typically the init... method that has
the most parameters and that does most of the initialization work, and it is the initializer that secondary
initializers of the class invoke with messages to self.
When you define a subclass, you must be able to identify the designated initializer of the superclass and invoke
it in your subclass’s designated initializer through a message to super. You must also make sure that inherited
initializers are covered in some way. And you may provide as many convenience initializers as you deem
necessary. When designing the initializers of your class, keep in mind that designated initializers are chained
to each other through messages to super; whereas other initializers are chained to the designated initializer
of their class through messages to self.
An example will make this clearer. Let’s say there are three classes, A, B, and C; class B inherits from class A,
and class C inherits from class B. Each subclass adds an attribute as an instance variable and implements an
init... method—the designated initializer—to initialize this instance variable. They also define secondary
initializers and ensure that inherited initializers are overridden, if necessary. Figure 6-2 illustrates the initializers
of all three classes and their relationships.
Figure 6-2 Interactions of secondary and designated initializers
- (id)init
super
super
Class A
Class B
Class C
inherits from
inherits from
self
- (id)init
- (id)initWithTitle:
self
- (id)initWithTitle:
- (id)initWithTitle:date:
Object Initialization
Multiple Initializers and the Designated Initializer
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
41The designated initializer for each class is the initializer with the most coverage; it is the method that initializes
the attribute added by the subclass. The designated initializer is also the init... method that invokes the
designated initializer of the superclass in a message to super. In this example, the designated initializer of
class C, initWithTitle:date:, invokesthe designated initializer of itssuperclass, initWithTitle:, which
in turn invokes the init method of class A. When creating a subclass, it’s always important to know the
designated initializer of the superclass.
Although designated initializers are thus connected up the inheritance chain through messages to super,
secondary initializers are connected to their class’s designated initializer through messagesto self. Secondary
initializers (as in this example) are frequently overridden versions of inherited initializers. Class C overrides
initWithTitle: to invoke its designated initializer, passing it a default date. This designated initializer, in
turn, invokes the designated initializer of class B, which is the overridden method, initWithTitle:. If you
sent an initWithTitle: message to objects of class B and class C, you’d be invoking different method
implementations. On the other hand, if class C did not override initWithTitle: and you sent the message
to an instance of class C, the class B implementation would be invoked. Consequently, the C instance would
be incompletely initialized (since it would lack a date). When creating a subclass, it’s important to make sure
that all inherited initializers are adequately covered.
Sometimes the designated initializer of a superclass may be sufficient for the subclass, and so there is no need
for the subclass to implement its own designated initializer. Other times, a class’s designated initializer may
be an overridden version of its superclass's designated initializer. This is frequently the case when the subclass
needs to supplement the work performed by the superclass’s designated initializer, even though the subclass
does not add any instance variables of its own (or the instance variables it does add don’t require explicit
initialization).
Object Initialization
Multiple Initializers and the Designated Initializer
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
42The Model-View-Controller design pattern (MVC) is quite old. Variations of it have been around at least since
the early days of Smalltalk. It is a high-level pattern in that it concerns itself with the global architecture of an
application and classifies objects according to the general rolesthey play in an application. It is also a compound
pattern in that it comprises several, more elemental patterns.
Object-oriented programs benefit in several ways by adapting the MVC design pattern for their designs. Many
objectsin these programstend to be more reusable and their interfacestend to be better defined. The programs
overall are more adaptable to changing requirements—in other words, they are more easily extensible than
programs that are not based on MVC. Moreover, many technologies and architectures in Cocoa—such as
bindings, the document architecture, and scriptability—are based on MVC and require that your custom objects
play one of the roles defined by MVC.
Roles and Relationships of MVC Objects
The MVC design pattern considersthere to be three types of objects: model objects, view objects, and controller
objects. The MVC pattern defines the roles that these types of objects play in the application and their lines
of communication. When designing an application, a major step is choosing—or creating custom classes
for—objects that fall into one of these three groups. Each of the three types of objects is separated from the
others by abstract boundaries and communicates with objects of the other types across those boundaries.
Model Objects Encapsulate Data and Basic Behaviors
Model objects represent special knowledge and expertise. They hold an application’s data and define the logic
that manipulates that data. A well-designed MVC application has all its important data encapsulated in model
objects. Any data that is part of the persistent state of the application (whether that persistent state is stored
in files or databases) should reside in the model objects once the data is loaded into the application. Because
they represent knowledge and expertise related to a specific problem domain, they tend to be reusable.
Ideally, a model object has no explicit connection to the user interface used to present and edit it. For example,
if you have a model object that represents a person (say you are writing an address book), you might want to
store a birthdate. That’s a good thing to store in your Person model object. However, storing a date format
string or other information on how that date is to be presented is probably better off somewhere else.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
43
Model-View-ControllerIn practice, thisseparation is not alwaysthe best thing, and there issome room for flexibility here, but in general
a model object should not be concerned with interface and presentation issues. One example where a bit of
an exception isreasonable is a drawing application that has model objectsthat represent the graphics displayed.
It makes sense for the graphic objects to know how to draw themselves because the main reason for their
existence is to define a visual thing. But even in this case, the graphic objects should not rely on living in a
particular view or any view at all, and they should not be in charge of knowing when to draw themselves. They
should be asked to draw themselves by the view object that wants to present them.
View Objects Present Information to the User
A view object knows how to display, and might allow users to edit, the data from the application’s model. The
view should not be responsible forstoring the data it is displaying. (This does not mean the view never actually
stores data it’s displaying, of course. A view can cache data or do similar tricks for performance reasons). A
view object can be in charge of displaying just one part of a model object, or a whole model object, or even
many different model objects. Views come in many different varieties.
View objects tend to be reusable and configurable, and they provide consistency between applications. In
Cocoa, the AppKit framework defines a large number of view objects and provides many of them in the Interface
Builder library. By reusing the AppKit’s view objects, such as NSButton objects, you guarantee that buttons
in your application behave just like buttonsin any other Cocoa application, assuring a high level of consistency
in appearance and behavior across applications.
A view should ensure it is displaying the model correctly. Consequently, it usually needsto know about changes
to the model. Because model objects should not be tied to specific view objects, they need a generic way of
indicating that they have changed.
Controller Objects Tie the Model to the View
A controller object acts as the intermediary between the application's view objects and its model objects.
Controllers are often in charge of making sure the views have access to the model objects they need to display
and act as the conduit through which views learn about changes to the model. Controller objects can also
perform set-up and coordinating tasks for an application and manage the life cycles of other objects.
In a typical Cocoa MVC design, when users enter a value or indicate a choice through a view object, that value
or choice is communicated to a controller object. The controller object might interpret the user input in some
application-specific way and then either may tell a model object what to do with thisinput—for example, "add
a new value" or "delete the current record"—or it may have the model object reflect a changed value in one
of its properties. Based on this same user input, some controller objects might also tell a view object to change
an aspect of its appearance or behavior, such as telling a button to disable itself. Conversely, when a model
object changes—say, a new data source is accessed—the model object usually communicates that change to
a controller object, which then requests one or more view objects to update themselves accordingly.
Model-View-Controller
Roles and Relationships of MVC Objects
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
44Controller objects can be either reusable or nonreusable, depending on their general type. “Types of Cocoa
Controller Objects” (page 45) describes the different types of controller objects in Cocoa.
Combining Roles
One can merge the MVC roles played by an object, making an object, for example, fulfill both the controller
and view roles—in which case, it would be called a view controller. In the same way, you can also have
model-controller objects. For some applications, combining roles like this is an acceptable design.
A model controller is a controller that concerns itself mostly with the model layer. It “owns” the model; its
primary responsibilities are to manage the model and communicate with view objects. Action methods that
apply to the model as a whole are typically implemented in a model controller. The document architecture
provides a number of these methods for you; for example, an NSDocument object (which is a central part of
the document architecture) automatically handles action methods related to saving files.
A view controller is a controller that concerns itself mostly with the view layer. It “owns” the interface (the
views); its primary responsibilities are to manage the interface and communicate with the model. Action
methods concerned with data displayed in a view are typically implemented in a view controller. An
NSWindowController object (also part of the document architecture) is an example of a view controller.
“Design Guidelinesfor MVC Applications” (page 50) offerssome design advice concerning objects with merged
MVC roles.
FurtherReading: Document-BasedApplicationsOverview discussesthedistinctionbetweenamodel
controller and a view controller from another perspective.
Types of Cocoa Controller Objects
“Controller Objects Tie the Model to the View” (page 44) sketches the abstract outline of a controller object,
but in practice the picture is far more complex. In Cocoa there are two general kinds of controller objects:
mediating controllers and coordinating controllers. Each kind of controller object is associated with a different
set of classes and each provides a different range of behaviors.
A mediating controller is typically an object that inherits from the NSControllerclass. Mediating controller
objects are used in the Cocoa bindings technology. They facilitate—or mediate—the flow of data between
view objects and model objects.
Model-View-Controller
Types of Cocoa Controller Objects
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
45iOS Note: AppKit implements the NSController class and its subclasses. These classes and the
bindings technology are not available in iOS.
Mediating controllers are typically ready-made objects that you drag from the Interface Builder library. You
can configure these objects to establish the bindings between properties of view objects and properties of
the controller object, and then between those controller properties and specific properties of a model object.
As a result, when users change a value displayed in a view object, the new value is automatically communicated
to a model object forstorage—via the mediating controller; and when a property of a model changesits value,
that change is communicated to a view for display. The abstract NSController class and its concrete
subclasses—NSObjectController, NSArrayController, NSUserDefaultsController, and
NSTreeController—provide supporting features such as the ability to commit and discard changes and
the management of selections and placeholder values.
A coordinating controller istypically an NSWindowController or NSDocumentControllerobject (available
only in AppKit), or an instance of a custom subclass of NSObject. Its role in an application is to oversee—or
coordinate—the functioning of the entire application or of part of the application,such asthe objects unarchived
from a nib file. A coordinating controller provides services such as:
● Responding to delegation messages and observing notifications
● Responding to action messages
● Managing the life cycle of owned objects (for example, releasing them at the proper time)
● Establishing connections between objects and performing other set-up tasks
NSWindowController and NSDocumentController are classes that are part of the Cocoa architecture for
document-based applications. Instances of these classes provide default implementations for several of the
services listed above, and you can create subclasses of them to implement more application-specific behavior.
You can even use NSWindowController objects to manage windows in an application that is not based on
the document architecture.
A coordinating controller frequently owns the objects archived in a nib file. As File’s Owner, the coordinating
controller is external to the objects in the nib file and manages those objects. These owned objects include
mediating controllers as well as window objects and view objects. See “MVC as a Compound Design
Pattern” (page 47) for more on coordinating controllers as File's Owner.
Instances of custom NSObject subclasses can be entirely suitable as coordinating controllers. These kinds of
controller objects combine both mediating and coordinating functions. For their mediating behavior, they
make use of mechanismssuch astarget-action, outlets, delegation, and notificationsto facilitate the movement
of data between view objects and model objects. They tend to contain a lot of glue code and, because that
code is exclusively application-specific, they are the least reusable kind of object in an application.
Model-View-Controller
Types of Cocoa Controller Objects
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
46Further Reading: For more on the Cocoa bindings technology, see Cocoa Bindings Programming
Topics.
MVC as a Compound Design Pattern
Model-View-Controller is a design pattern that is composed of several more basic design patterns. These basic
patterns work together to define the functional separation and paths of communication that are characteristic
of an MVC application. However, the traditional notion of MVC assigns a set of basic patterns different from
those that Cocoa assigns. The difference primarily lies in the roles given to the controller and view objects of
an application.
In the original (Smalltalk) conception, MVC is made up of the Composite, Strategy, and Observer patterns.
● Composite—The view objectsin an application are actually a composite of nested viewsthat work together
in a coordinated fashion (that is, the view hierarchy). These display components range from a window to
compound views, such as a table view, to individual views, such as buttons. User input and display can
take place at any level of the composite structure.
● Strategy—A controller object implements the strategy for one or more view objects. The view object
confines itself to maintaining its visual aspects, and it delegates to the controller all decisions about the
application-specific meaning of the interface behavior.
● Observer—A model object keeps interested objects in an application—usually view objects—advised of
changes in its state.
The traditional way the Composite, Strategy, and Observer patterns work together is depicted by Figure 7-1:
The user manipulates a view at some level of the composite structure and, as a result, an event is generated.
A controller object receives the event and interprets it in an application-specific way—that is, it applies a
strategy. This strategy can be to request (via message) a model object to change its state or to request a view
Model-View-Controller
MVC as a Compound Design Pattern
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
47object (at some level of the composite structure) to change its behavior or appearance. The model object, in
turn, notifies all objects who have registered as observers when its state changes; if the observer is a view
object, it may update its appearance accordingly.
Figure 7-1 Traditional version of MVC as a compound pattern
User action
Update
Get changed state
Update
Strategy
Controller
Composite
View
Notify
Observer
Model
The Cocoa version of MVC as a compound pattern has some similarities to the traditional version, and in fact
it is quite possible to construct a working application based on the diagram in Figure 7-1. By using the bindings
technology, you can easily create a Cocoa MVC application whose views directly observe model objects to
receive notifications of state changes. However, there is a theoretical problem with this design. View objects
and model objects should be the most reusable objects in an application. View objects represent the "look
and feel" of an operating system and the applications that system supports; consistency in appearance and
behavior is essential, and that requires highly reusable objects. Model objects by definition encapsulate the
data associated with a problem domain and perform operations on that data. Design-wise, it's best to keep
model and view objects separate from each other, because that enhances their reusability.
In most Cocoa applications, notifications of state changes in model objects are communicated to view objects
through controller objects. Figure 7-2 shows this different configuration, which appears much cleaner despite
the involvement of two more basic design patterns.
Figure 7-2 Cocoa version of MVC as a compound design pattern
User action
Update
Update
Notify
Mediator
Strategy
Controller
View Model
Command
Composite
Observer
Model-View-Controller
MVC as a Compound Design Pattern
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
48The controller object in this compound design pattern incorporatesthe Mediator pattern as well asthe Strategy
pattern; it mediates the flow of data between model and view objects in both directions. Changes in model
state are communicated to view objects through the controller objects of an application. In addition, view
objects incorporate the Command pattern through their implementation of the target-action mechanism.
Note: The target-action mechanism, which enables view objects to communicate user input and
choices, can be implemented in both coordinating and mediating controller objects. However, the
design of the mechanism differs in each controller type. For coordinating controllers, you connect
the view object to its target (the controller object) in Interface Builder and specify an action selector
that must conform to a certain signature. Coordinating controllers, by virtue of being delegates of
windows and the global application object, can also be in the responder chain. The bindings
mechanism used by mediating controllers also connects view objects to targets and allows action
signatures with a variable number of parameters of arbitrary types. Mediating controllers, however,
aren’t in the responder chain.
There are practical reasons as well as theoretical ones for the revised compound design pattern depicted in
Figure 7-2, especially when it comesto the Mediator design pattern. Mediating controllers derive from concrete
subclasses of NSController, and these classes, besides implementing the Mediator pattern, offer many
features that applications should take advantage of, such as the management of selections and placeholder
values. And if you opt not to use the bindings technology, your view object could use a mechanism such as
the Cocoa notification center to receive notifications from a model object. But this would require you to create
a custom view subclass to add the knowledge of the notifications posted by the model object.
Model-View-Controller
MVC as a Compound Design Pattern
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
49In a well-designed Cocoa MVC application, coordinating controller objects often own mediating controllers,
which are archived in nib files. Figure 7-3 shows the relationships between the two types of controller objects.
Figure 7-3 Coordinating controller as the owner of a nib file
Owns
Coordinating
Controller
Nib file
Data flow
Data flow
View
Mediating
Controller
Model
Design Guidelines for MVC Applications
The following guidelines apply to Model-View-Controller considerations in the design of applications:
● Although you can use an instance of a custom subclass of NSObject as a mediating controller, there's no
reason to go through all the work required to make it one. Use instead one of the ready-made
NSController objects designed for the Cocoa bindings technology; that is, use an instance of
NSObjectController, NSArrayController, NSUserDefaultsController, or
NSTreeController—or a custom subclass of one of these concrete NSController subclasses.
However, if the application is very simple and you feel more comfortable writing the glue code needed
to implement mediating behavior using outlets and target-action, feel free to use an instance of a custom
NSObject subclass as a mediating controller. In a custom NSObject subclass, you can also implement a
mediating controller in the NSController sense, using key-value coding, key-value observing, and the
editor protocols.
Model-View-Controller
Design Guidelines for MVC Applications
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
50● Although you can combine MVC roles in an object, the best overall strategy is to keep the separation
between roles. This separation enhances the reusability of objects and the extensibility of the program
they're used in. If you are going to merge MVC roles in a class, pick a predominant role for that class and
then (for maintenance purposes) use categories in the same implementation file to extend the class to
play other roles.
● A goal of a well-designed MVC application should be to use as many objects as possible that are
(theoretically, at least) reusable. In particular, view objects and model objects should be highly reusable.
(The ready-made mediating controller objects, of course, are reusable.) Application-specific behavior is
frequently concentrated as much as possible in controller objects.
● Although it is possible to have views directly observe models to detect changes in state, it is best not to
do so. A view object should always go through a mediating controller object to learn about changes in
an model object. The reason is two-fold:
●
If you use the bindings mechanism to have view objects directly observe the properties of model
objects, you bypass all the advantages that NSController and its subclasses give your application:
selection and placeholder management as well as the ability to commit and discard changes.
●
If you don't use the bindings mechanism, you have to subclass an existing view classto add the ability
to observe change notifications posted by a model object.
● Strive to limit code dependency in the classes of your application. The greater the dependency a class has
on another class, the less reusable it is. Specific recommendations vary by the MVC roles of the two classes
involved:
● A view classshouldn't depend on a model class(although this may be unavoidable with some custom
views).
● A view class shouldn't have to depend on a mediating controller class.
● A model class shouldn't depend on anything other than other model classes.
● A mediating controller class shouldn’t depend on a model class (although, like views, this may be
necessary if it's a custom controller class).
● A mediating controller class shouldn't depend on view classes or on coordinating controller classes.
● A coordinating controller class depends on classes of all MVC role types.
●
If Cocoa offers an architecture that solves a programming problem, and this architecture assigns MVC
roles to objects of specific types, use that architecture. It will be much easier to put your project together
if you do. The document architecture, for example, includes an Xcode project template that configures
an NSDocument object (per-nib model controller) as File's Owner.
Model-View-Controller
Design Guidelines for MVC Applications
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
51Model-View-Controller in Cocoa (OS X)
The Model-View-Controller design pattern is fundamental to many Cocoa mechanisms and technologies. As
a consequence, the importance of using MVC in object-oriented design goes beyond attaining greater reusability
and extensibility for your own applications. If your application is to incorporate a Cocoa technology that is
MVC-based, your application will work best if its design also follows the MVC pattern. It should be relatively
painless to use these technologies if your application has a good MVC separation, but it will take more effort
to use such a technology if you don’t have a good separation.
Cocoa in OS X includes the following architectures, mechanisms, and technologies that are based on
Model-View-Controller:
● Document architecture. In this architecture, a document-based application consists of a controller object
for the entire application (NSDocumentController), a controller object for each document window
(NSWindowController), and an object that combines controller and model roles for each document
(NSDocument).
● Bindings. MVC is central to the bindings technology of Cocoa. The concrete subclasses of the abstract
NSController provide ready-made controller objects that you can configure to establish bindings
between view objects and properly designed model objects.
● Application scriptability. When designing an application to make it scriptable, it is essential not only that
it follow the MVC design pattern but that your application’s model objects are properly designed. Scripting
commandsthat access application state and request application behaviorshould usually be sent to model
objects or controller objects.
● Core Data. The Core Data framework manages graphs of model objects and ensures the persistence of
those objects by saving them to (and retrieving them from) a persistentstore. Core Data istightly integrated
with the Cocoa bindings technology. The MVC and object modeling design patterns are essential
determinants of the Core Data architecture.
● Undo. In the undo architecture, model objects once again play a central role. The primitive methods of
model objects (which are usually its accessor methods) are often where you implement undo and redo
operations. The view and controller objects of an action may also be involved in these operations; for
example, you might have such objects give specific titles to the undo and redo menu items, or you might
have them undo selections in a text view.
Model-View-Controller
Model-View-Controller in Cocoa (OS X)
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
52This section defines terms and presents examples of object modeling and key-value coding that are specific
to Cocoa bindings and the Core Data framework. Understanding terms such as key paths is fundamental to
using these technologies effectively. This section is recommended reading if you are new to object-oriented
design or key-value coding.
When using the Core Data framework, you need a way to describe your model objects that does not depend
on views and controllers. In a good reusable design, views and controllers need a way to access model properties
without imposing dependencies between them. The Core Data framework solves this problem by borrowing
concepts and terms from database technology—specifically, the entity-relationship model.
Entity-relationship modeling is a way of representing objects typically used to describe a data source’s data
structures in a way that allows those data structures to be mapped to objects in an object-oriented system.
Note that entity-relationship modeling isn’t unique to Cocoa; it’s a popular discipline with a set of rules and
terms that are documented in database literature. It is a representation that facilitates storage and retrieval of
objects in a data source. A data source can be a database, a file, a web service, or any other persistent store.
Because it is not dependent on any type of data source it can also be used to represent any kind of object and
its relationship to other objects.
In the entity-relationship model, the objects that hold data are called entities, the components of an entity are
called attributes, and the referencesto other data-bearing objects are called relationships. Together, attributes
and relationships are known as properties. With these three simple components (entities, attributes, and
relationships), you can model systems of any complexity.
Cocoa uses a modified version of the traditional rules of entity-relationship modeling referred to in this document
as object modeling . Object modeling is particularly useful in representing model objects in the
Model-View-Controller (MVC) design pattern. Thisis notsurprising because even in a simple Cocoa application,
models are typically persistent—that is, they are stored in a data container such as a file.
Entities
Entities are model objects. In the MVC design pattern, model objects are the objects in your application that
encapsulate specified data and provide methods that operate on that data. They are usually persistent but
more importantly, model objects are not dependent on how the data is displayed to the user.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
53
Object ModelingFor example, a structured collection of model objects (an object model) can be used to represent a company’s
customer base, a library of books, or a network of computers. A library book has attributes—such as the book
title, ISBN number, and copyright date—and relationships to other objects—such as the author and library
member. In theory, if the parts of a system can be identified, the system can be expressed as an object model.
Figure 8-1 shows an example object model used in an employee management application. In this model,
Department models a department and Employee models an employee.
Figure 8-1 Employee management application object diagram
Department
name
budget
Employee
firstName
lastName
salary
Attributes
Attributes represent structures that contain data. An attribute of an object may be a simple value, such as a
scalar (for example, an integer, float, or double value), but can also be a C structure (for example an array
of char values or an NSPoint structure) or an instance of a primitive class (such as, NSNumber, NSData, or
NSColor in Cocoa). Immutable objects such as NSColor are usually considered attributes too. (Note that Core
Data natively supports only a specific set of attribute types, as described in NSAttributeDescription Class
Reference . You can, however, use additional attribute types, as described in “Non-Standard Persistent Attributes”
in Core Data Programming Guide .)
Object Modeling
Attributes
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
54In Cocoa, an attribute typically corresponds to a model’s instance variable or accessor method. For example,
Employee has firstName, lastName, and salary instance variables. In an employee management application,
you might implement a table view to display a collection of Employee objects and some of their attributes, as
shown in Figure 8-2. Each row in the table correspondsto an instance of Employee, and each column corresponds
to an attribute of Employee.
Figure 8-2 Employees table view
Relationships
Not all properties of a model are attributes—some properties are relationshipsto other objects. Your application
is typically modeled by multiple classes. At runtime, your object model is a collection of related objects that
make up an object graph. These are typically the persistent objects that your users create and save to some
data container or file before terminating the application (asin a document-based application). The relationships
between these model objects can be traversed at runtime to access the properties of the related objects.
For example, in the employee management application, there are relationships between an employee and the
department in which the employee works, and between an employee and the employee’s manager. Because
a manager is also an employee, the employee–manager relationship is an example of a reflexive relationship—a
relationship from an entity to itself.
Relationships are inherently bidirectional, so conceptually at least there are also relationships between a
department and the employees that work in the department, and an employee and the employee’s direct
reports. Figure 8-3 (page 56) illustrates the relationships between a Department and an Employee entity, and
the Employee reflexive relationship. In this example, the Department entity’s “employees” relationship is the
inverse of the Employee entity’s “department” relationship. It is possible, however, for relationships to be
navigable in only one direction—for there to be no inverse relationship. If, for example, you are never interested
in finding out from a department object what employees are associated with it, then you do not have to model
Object Modeling
Relationships
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
55that relationship. (Note that although thisistrue in the general case, Core Data may impose additional constraints
over general Cocoa object modeling—not modeling the inverse should be considered an extremely advanced
option.)
Figure 8-3 Relationships in the employee management application
Department
name
budget
Employee
firstName
lastName
salary
department employees manager
directReports
Relationship Cardinality and Ownership
Every relationship has a cardinality ; the cardinality tells you how many destination objects can (potentially)
resolve the relationship. If the destination object is a single object, then the relationship is called a to-one
relationship . If there may be more than one object in the destination, then the relationship is called a to-many
relationship .
Relationships can be mandatory or optional. A mandatory relationship is one where the destination is
required—for example, every employee must be associated with a department. An optional relationship is, as
the name suggests, optional—for example, not every employee has direct reports. So the directReports
relationship depicted in Figure 8-4 (page 56) is optional.
It is also possible to specify a range for the cardinality. An optional to-one relationship has a range 0-1. An
employee may have any number of direct reports, or a range that specifies a minimum and a maximum, for
example, 0-15, which also illustrates an optional to-many relationship.
Figure 8-4 illustrates the cardinalities in the employee management application. The relationship between an
Employee object and a Department object is a mandatory to-one relationship—an employee must belong to
one, and only one, department. The relationship between a Department and its Employee objectsis an optional
to-many relationship (represented by a “*”). The relationship between an employee and a manager is an
optional to-one relationship (denoted by the range 0-1)—top-ranking employees do not have managers.
Figure 8-4 Relationship cardinality
1 department employees * 0..1 manager
* directReports
Department
name
budget
Employee
firstName
lastName
salary
Note also that destination objects of relationships are sometimes owned and sometimes shared.
Object Modeling
Relationships
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
56Accessing Properties
In order for models, views, and controllers to be independent of each other, you need to be able to access
properties in a way that is independent of a model’s implementation. This is accomplished by using key-value
pairs.
Keys
You specify properties of a model using a simple key, often a string. The corresponding view or controller uses
the key to look up the corresponding attribute value. This design enforces the notion that the attribute itself
doesn’t necessarily contain the data—the value can be indirectly obtained or derived.
Key-value coding is used to perform thislookup; it is a mechanism for accessing an object’s propertiesindirectly
and, in certain contexts, automatically. Key-value coding works by using the names of the object’s
properties—typically itsinstance variables or accessor methods—as keysto accessthe values of those properties.
For example, you might obtain the name of a Department object using a name key. If the Department object
either has an instance variable or a method called name then a value for the key can be returned (if it doesn’t
have either, an error is returned). Similarly, you might obtain Employee attributes using the firstName,
lastName, and salary keys.
Values
All values for a particular attribute of a given entity are of the same data type. The data type of an attribute is
specified in the declaration of its corresponding instance variable or in the return value of its accessor method.
For example, the data type of the Department object name attribute may be an NSString object in Objective-C.
Note that key-value coding returns only object values. If the return type or the data type for the specific accessor
method or instance variable used to supply the value for a specified key is not an object, then an NSNumber
or NSValue object is created for that value and returned in its place. If the name attribute of Department is of
type NSString, then, using key-value coding, the value returned for the name key of a Department object is
an NSString object. If the budget attribute of Department is of type float, then, using key-value coding,
the value returned for the budget key of a Department object is an NSNumber object.
Similarly, when you set a value using key-value coding, if the data type required by the appropriate accessor
or instance variable for the specified key is not an object, then the value is extracted from the passed object
using the appropriate -typeValue method.
The value of a to-one relationship is simply the destination object of that relationship. For example, the value
of the department property of an Employee object is a Department object. The value of a to-many relationship
is the collection object. The collection can be a set or an array. If you use Core Data it is a set; otherwise, it is
Object Modeling
Accessing Properties
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
57typically an array) that contains the destination objects of that relationship. For example, the value of the
employees property of an Department object is a collection containing Employee objects. Figure 8-5 shows
an example object graph for the employee management application.
Figure 8-5 Object graph for the employee management application
Department
name: "Marketing"
budget: 2000000
employees
Collection
Collection
Employee
firstName: "Toni"
lastName: "Lau"
salary: 7000
manager
department
directReports
Employee
firstName: "Joe"
lastName: "Jackson"
salary: 5000
manager
department
directReports
Key Paths
A key path is a string of dot-separated keysthatspecify a sequence of object propertiesto traverse. The property
of the first key is determined by, and each subsequent key is evaluated relative to, the previous property. Key
paths allow you to specify the properties of related objects in a way that is independent of the model
implementation. Using key paths you can specify the path through an object graph, of whatever depth, to a
specific attribute of a related object.
The key-value coding mechanism implements the lookup of a value given a key path similar to key-value pairs.
For example, in the employee-management application you might access the name of a Department via an
Employee object using the department.name key path where department is a relationship of Employee
and name is an attribute of Department. Key paths are useful if you want to display an attribute of a destination
Object Modeling
Accessing Properties
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
58entity. For example, the employee table view in Figure 8-6 is configured to display the name of the employee’s
department object, not the department object itself. Using Cocoa bindings, the value of the Department
column is bound to department.name of the Employee objects in the displayed array.
Figure 8-6 Employees table view showing department name
Not every relationship in a key path necessarily has a value. For example, the manager relationship can be
nil if the employee is the CEO. In this case, the key-value coding mechanism does not break—it simply stops
traversing the path and returns an appropriate value, such as nil.
Object Modeling
Accessing Properties
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
59Cocoa objects are either mutable or immutable. You cannot change the encapsulated values of immutable
objects; once such an object is created, the value it represents remains the same throughout the object’s life.
But you can change the encapsulated value of a mutable object at any time. The following sections explain
the reasons for having mutable and immutable variants of an object type, describe the characteristics and
side-effects of object mutability, and recommend how best to handle objects when their mutability is an issue.
Why Mutable and Immutable Object Variants?
Objects by default are mutable. Most objects allow you to change their encapsulated data through setter
accessor methods. For example, you can change the size, positioning, title, buffering behavior, and other
characteristics of an NSWindow object. A well-designed model object—say, an object representing a customer
record—requires setter methods to change its instance data.
The Foundation framework adds some nuance to this picture by introducing classes that have mutable and
immutable variants. The mutable subclasses are typically subclasses of their immutable superclass and have
“Mutable” embedded in the class name. These classes include the following:
NSMutableArray
NSMutableDictionary
NSMutableSet
NSMutableIndexSet
NSMutableCharacterSet
NSMutableData
NSMutableString
NSMutableAttributedString
NSMutableURLRequest
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
60
Object MutabilityNote: Except for NSMutableParagraphStyle in the AppKit framework, the Foundation framework
currently defines all explicitly named mutable classes. However, any Cocoa framework can potentially
have its own mutable and immutable class variants.
Although these classes have atypical names, they are closer to the mutable norm than their immutable
counterparts. Why this complexity? What purpose does having an immutable variant of a mutable objectserve?
Consider a scenario where all objects are capable of being mutated. In your application you invoke a method
and are handed back a reference to an object representing a string. You use this string in your user interface
to identify a particular piece of data. Now another subsystem in your application gets its own reference to that
same string and decidesto mutate it. Suddenly your label has changed out from under you. Things can become
even more dire if, for instance, you get a reference to an array that you use to populate a table view. The user
selects a row corresponding to an object in the array that has been removed by some code elsewhere in the
program, and problems ensue. Immutability is a guarantee that an object won’t unexpectedly change in value
while you’re using it.
Objects that are good candidates for immutability are ones that encapsulate collections of discrete values or
contain values that are stored in buffers (which are themselves kinds of collections, either of characters or
bytes). But not all such value objects necessarily benefit from having mutable versions. Objects that contain a
single simple value, such as instances of NSNumber or NSDate, are not good candidates for mutability. When
the represented value changes in these cases, it makes more sense to replace the old instance with a new
instance.
Performance is also a reason for immutable versions of objects representing things such as strings and
dictionaries. Mutable objectsfor basic entitiessuch asstrings and dictionaries bring some overhead with them.
Because they must dynamically manage a changeable backing store—allocating and deallocating chunks of
memory as needed—mutable objects can be less efficient than their immutable counterparts.
Although in theory immutability guarantees that an object’s value is stable, in practice this guarantee isn’t
always assured. A method may choose to hand out a mutable object under the return type of its immutable
variant; later, it may decide to mutate the object, possibly violating assumptions and choices the recipient has
made based on the earlier value. The mutability of an object itself may change as it undergoes various
transformations. For example, serializing a property list (using the NSPropertyListSerialization class)
does not preserve the mutability aspect of objects, only their general kind—a dictionary, an array, and so on.
Thus, when you deserialize this property list, the resulting objects might not be of the same class asthe original
objects. For instance, what was once an NSMutableDictionary object might now be a NSDictionary
object.
Object Mutability
Why Mutable and Immutable Object Variants?
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
61Programming with Mutable Objects
When the mutability of objects is an issue, it’s best to adopt some defensive programming practices. Here are
a few general rules or guidelines:
● Use a mutable variant of an object when you need to modify its contents frequently and incrementally
after it has been created.
● Sometimes it’s preferable to replace one immutable object with another; for example, most instance
variables that hold string values should be assigned immutable NSString objects that are replaced with
setter methods.
● Rely on the return type for indications of mutability.
●
If you have any doubts about whether an object is, or should be, mutable, go with immutable.
This section explores the gray areas in these guidelines, discussing typical choices you have to make when
programming with mutable objects. It also gives an overview of methods in the Foundation framework for
creating mutable objects and for converting between mutable and immutable object variants.
Creating and Converting Mutable Objects
You can create a mutable object through the standard nested alloc-init message—for example:
NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] init];
However, many mutable classes offer initializers and factory methodsthat let you specify the initial or probable
capacity of the object, such as the arrayWithCapacity: class method of NSMutableArray:
NSMutableArray *mutArray = [NSMutableArray arrayWithCapacity:[timeZones count]];
The capacity hint enables more efficient storage of the mutable object’s data. (Because the convention for
class factory methods is to return autoreleased instances, be sure to retain the object if you wish to keep it
viable in your code.)
You can also create a mutable object by making a mutable copy of an existing object of that general type. To
do so, invoke the mutableCopy method that each immutable super class of a Foundation mutable class
implements:
NSMutableSet *mutSet = [aSet mutableCopy];
Object Mutability
Programming with Mutable Objects
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
62In the other direction, you can send copy to a mutable object to make an immutable copy of the object.
Many Foundation classes with immutable and mutable variants include methods for converting between the
variants, including:
●
typeWithType:—for example, arrayWithArray:
● setType:—for example, setString: (mutable classes only)
● initWithType:copyItems:—for example, initWithDictionary:copyItems:
Storing and Returning Mutable Instance Variables
In Cocoa development you often have to decide whether to make an instance variable mutable or immutable.
For an instance variable whose value can change, such as a dictionary or string, when is it appropriate to make
the object mutable? And when is it better to make the object immutable and replace it with another object
when its represented value changes?
Generally, when you have an object whose contents change wholesale, it’s better to use an immutable object.
Strings (NSString) and data objects (NSData) usually fall into this category. If an object is likely to change
incrementally, it is a reasonable approach to make it mutable. Collections such as arrays and dictionaries fall
into this category. However, the frequency of changes and the size of the collection should be factors in this
decision. For example, if you have a small array that seldom changes, it’s better to make it immutable.
There are a couple of other considerations when deciding on the mutability of a collection held as an instance
variable:
●
If you have a mutable collection that is frequently changed and that you frequently hand out to clients
(that is, you return it directly in a getter accessor method), you run the risk of mutating something that
your clients might have a reference to. If this risk is probable, the instance variable should be immutable.
●
If the value of the instance variable frequently changes but you rarely return it to clientsin getter methods,
you can make the instance variable mutable but return an immutable copy of it in your accessor method;
in memory-managed programs, this object would be autoreleased (Listing 9-1).
Listing 9-1 Returning an immutable copy of a mutable instance variable
@interface MyClass : NSObject {
// ...
NSMutableSet *widgets;
}
// ...
@end
Object Mutability
Programming with Mutable Objects
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
63@implementation MyClass
- (NSSet *)widgets {
return (NSSet *)[[widgets copy] autorelease];
}
One sophisticated approach for handling mutable collections that are returned to clients is to maintain a flag
that records whether the object is currently mutable or immutable. If there is a change, make the object mutable
and apply the change. When handing out the collection, make the object immutable (if necessary) before
returning it.
Receiving Mutable Objects
The invoker of a method is interested in the mutability of a returned object for two reasons:
●
It wants to know if it can change the object’s value.
●
It wants to know if the object’s value will change unexpectedly while it has a reference to it.
Use Return Type, Not Introspection
To determine whether it can change a received object, the receiver of a message must rely on the formal type
of the return value. If it receives, for example, an array object typed as immutable, it should not attempt to
mutate it. It is not an acceptable programming practice to determine if an object is mutable based on its class
membership—for example:
if ( [anArray isKindOfClass:[NSMutableArray class]] ) {
// add, remove objects from anArray
}
For reasons related to implementation, what isKindOfClass: returns in this case may not be accurate. But
for reasons other than this, you should not make assumptions about whether an object is mutable based on
class membership. Your decision should be guided solely by what the signature of the method vending the
object says about its mutability. If you are not sure whether an object is mutable or immutable, assume it’s
immutable.
A couple of examples might help clarify why this guideline is important:
Object Mutability
Programming with Mutable Objects
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
64● You read a property list from a file. When the Foundation framework processes the list, it notices that
various subsets of the property list are identical, so it creates a set of objects that it shares among all those
subsets. Afterward you look at the created property list objects and decide to mutate one subset. Suddenly,
and without being aware of it, you’ve changed the tree in multiple places.
● You ask NSView for its subviews (with the subviews method) and it returns an object that is declared to
be an NSArray but which could be an NSMutableArray internally. Then you pass that array to some
other code that, through introspection, determinesit to be mutable and changesit. By changing this array,
the code is mutating internal data structures of the NSView class.
So don’t make an assumption about object mutability based on what introspection tells you about an object.
Treat objects as mutable or not based on what you are handed at the API boundaries (that is, based on the
return type). If you need to unambiguously mark an object as mutable or immutable when you passit to clients,
pass that information as a flag along with the object.
Make Snapshots of Received Objects
If you want to ensure that a supposedly immutable object received from a method does not mutate without
your knowing about it, you can make snapshots of the object by copying it locally. Then occasionally compare
the stored version of the object with the most recent version. If the object has mutated, you can adjust anything
in your program that is dependent on the previous version of the object. Listing 9-2 shows a possible
implementation of this technique.
Listing 9-2 Making a snapshot of a potentially mutable object
static NSArray *snapshot = nil;
- (void)myFunction {
NSArray *thingArray = [otherObj things];
if (snapshot) {
if ( ![thingArray isEqualToArray:snapshot] ) {
[self updateStateWith:thingArray];
}
}
snapshot = [thingArray copy];
}
A problem with making snapshots of objects for later comparison is that it is expensive. You’re required to
make multiple copies of the same object. A more efficient alternative isto use key-value observing. See Key-Value
Observing Programming Guide for a description of this protocol.
Object Mutability
Programming with Mutable Objects
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
65Mutable Objects in Collections
Storing mutable objects in collection objects can cause problems. Certain collections can become invalid or
even corrupt if objects they contain mutate because, by mutating, these objects can affect the way they are
placed in the collection. First, the properties of objects that are keys in hashing collections such as
NSDictionary objects or NSSet objects will, if changed, corrupt the collection if the changed properties
affect the results of the object’s hash or isEqual: methods. (If the hash method of the objectsin the collection
does not depend on their internal state, corruption is less likely.) Second, if an object in an ordered collection
such as a sorted array has its properties changed, this might affect how the object compares to other objects
in the array, thus rendering the ordering invalid.
Object Mutability
Programming with Mutable Objects
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
66An outlet is a property of an object that references another object. The reference is archived through Interface
Builder. The connections between the containing object and its outlets are reestablished every time the
containing object is unarchived from its nib file. The containing object holds an outlet declared as a property
with the type qualifier of IBOutlet and a weak option. For example:
@interface AppController : NSObject
{
}
@property (weak) IBOutlet NSArray *keywords;
Because it is a property, an outlet becomes part of an object’s encapsulated data and is backed by an instance
variable. But an outlet is more than a simple property. The connection between an object and its outlets is
archived in a nib file; when the nib file is loaded, each connection is unarchived and reestablished, and is thus
always available whenever it becomes necessary to send messages to the other object. The type qualifier
IBOutlet is a tag applied to an property declaration so that the Interface Builder application can recognize
the property as an outlet and synchronize the display and connection of it with Xcode.
An outlet is declared as a weak reference (weak) to prevent strong reference cycles.
You create and connect an outlet in the Interface Builder feature of Xcode.The property declaration for the outlet
must be tagged with the IBOutlet qualifier.
An application typically sets outlet connections between its custom controller objects and objects on the user
interface, but they can be made between any objects that can be represented as instances in Interface Builder,
even between two custom objects. As with any item of object state, you should be able to justify its inclusion
in a class; the more outlets an object has, the more memory it takes up. If there are other ways to obtain a
reference to an object, such as finding it through its index position in a matrix, or through its inclusion as a
function parameter, or through use of a tag (an assigned numeric identifier), you should do that instead.
Outlets are a form of object composition, which is a dynamic pattern that requires an object to somehow
acquire referencesto its constituent objectsso that it can send messagesto them. It typically holdsthese other
objects as properties backed by instance variables. These variables must be initialized with the appropriate
references at some point during the execution of the program.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
67
OutletsThe Receptionist design pattern addresses the general problem of redirecting an event occurring in one
execution context of an application to another execution context for handling. It is a hybrid pattern. Although
it doesn’t appear in the “Gang of Four” book, it combines elements of the Command, Memo, and Proxy design
patterns described in that book. It is also a variant of the Trampoline pattern (which also doesn’t appear in the
book); in this pattern, an event initially is received by a trampoline object, so-called because it immediately
bounces, or redirects, the event to a target object for handling.
The Receptionist Design Pattern in Practice
A KVO notification invokes the observeValueForKeyPath:ofObject:change:context: method
implemented by an observer. If the change to the property occurs on a secondary thread, the
observeValueForKeyPath:ofObject:change:context: code executes on that same thread. There the
central object in this pattern, the receptionist, acts as a thread intermediary. As Figure 11-1 illustrates, a
receptionist object is assigned as the observer of a model object’s property. The receptionist implements
observeValueForKeyPath:ofObject:change:context: to redirect the notification received on a
secondary thread to another execution context—the main operation queue, in this case. When the property
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
68
Receptionist Patternchanges, the receptionist receives a KVO notification. The receptionist immediately adds a block operation to
the main operation queue; the block contains code—specified by the client—that updates the user interface
appropriately.
Figure 11-1 Bouncing KVO updates to the main operation queue
Main
thread
self.value = newValue;
observeValueForKeyPath:
ofObject:change:context:
addOperation:
modelObject
receptionist
Secondary Main operation queue
thread
task
You define a receptionist class so that it has the elements it needs to add itself as an observer of a property
and then convert a KVO notification into an update task. Thus it must know what object it’s observing, the
property of the object that it’s observing, what update task to execute, and what queue to execute it on. Listing
11-1 shows the initial declaration of the RCReceptionist class and its instance variables.
Listing 11-1 Declaring the receptionist class
@interface RCReceptionist : NSObject {
id observedObject;
NSString *observedKeyPath;
RCTaskBlock task;
NSOperationQueue *queue;
}
Receptionist Pattern
The Receptionist Design Pattern in Practice
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
69The RCTaskBlock instance variable is a block object of the following declared type:
typedef void (^RCTaskBlock)(NSString *keyPath, id object, NSDictionary *change);
These parameters are similar to those of the observeValueForKeyPath:ofObject:change:context:
method. Next, the parameter class declares a single class factory method in which an RCTaskBlock object is
a parameter:
+ (id)receptionistForKeyPath:(NSString *)path
object:(id)obj
queue:(NSOperationQueue *)queue
task:(RCTaskBlock)task;
It implementsthis method to assign the passed-in value to instance variables of the created receptionist object
and to add that object as an observer of the model object’s property, as shown in Listing 11-2.
Listing 11-2 The class factory method for creating a receptionist object
+ (id)receptionistForKeyPath:(NSString *)path object:(id)obj queue:(NSOperationQueue
*)queue task:(RCTaskBlock)task {
RCReceptionist *receptionist = [RCReceptionist new];
receptionist->task = [task copy];
receptionist->observedKeyPath = [path copy];
receptionist->observedObject = [obj retain];
receptionist->queue = [queue retain];
[obj addObserver:receptionist forKeyPath:path
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:0];
return [receptionist autorelease];
}
Note that the code copies the block object instead of retaining it. Because the block was probably created on
the stack, it must be copied to the heap so it exists in memory when the KVO notification is delivered.
Finally, the parameter class implements the observeValueForKeyPath:ofObject:change:context:
method. The implementation (see Listing 11-3) is simple.
Receptionist Pattern
The Receptionist Design Pattern in Practice
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
70Listing 11-3 Handling the KVO notification
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
[queue addOperationWithBlock:^{
task(keyPath, object, change);
}];
}
This code simply enqueues the task onto the given operation queue, passing the task block the observed
object, the key path for the changed property, and the dictionary containing the new value. The task is
encapsulated in an NSBlockOperation object that executes the task on the queue.
The client object supplies the block code that updates the user interface when it creates a receptionist object,
as shown in Listing 11-4. Note that when it creates the receptionist object, the client passes in the operation
queue on which the block is to be executed, in this case the main operation queue.
Listing 11-4 Creating a receptionist object
RCReceptionist *receptionist = [RCReceptionist
receptionistForKeyPath:@"value" object:model queue:mainQueue task:^(NSString
*keyPath, id object, NSDictionary *change) {
NSView *viewForModel = [modelToViewMap objectForKey:model];
NSColor *newColor = [change objectForKey:NSKeyValueChangeNewKey];
[[[viewForModel subviews] objectAtIndex:0] setFillColor:newColor];
}];
When to Use the Receptionist Pattern
You can adopt the Receptionist design pattern whenever you need to bounce off work to another execution
context for handling. When you observe a notification, or implement a block handler, or respond to an action
message and you want to ensure that your code executes in the appropriate execution context, you can
implement the Receptionist pattern to redirect the work that must be done to that execution context. With
the Receptionist pattern, you might even perform some filtering or coalescing of the incoming data before
you bounce off a task to processthe data. For example, you could collect data into batches, and then at intervals
dispatch those batches elsewhere for processing.
Receptionist Pattern
When to Use the Receptionist Pattern
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
71One common situation where the Receptionist pattern is useful is key-value observing. In key-value observing,
changes to the value of an model object’s property are communicated to observers via KVO notifications.
However, changes to a model object can occur on a background thread. This results in a thread mismatch,
because changes to a model object’s state typically result in updates to the user interface, and these must
occur on the main thread. In this case, you want to redirect the KVO notifications to the main thread. where
the updates to an application’s user interface can occur.
Receptionist Pattern
When to Use the Receptionist Pattern
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
72Although delegation, bindings, and notification are useful for handling certain forms of communication between
objects in a program, they are not particularly suitable for the most visible sort of communication. A typical
application’s user interface consists of a number of graphical objects, and perhaps the most common of these
objects are controls. A control is a graphical analog of a real-world or logical device (button, slider, checkboxes,
and so on); as with a real-world control, such as a radio tuner, you use it to convey your intent to some system
of which it is a part—that is, an application.
The role of a control on a user interface is simple: It interprets the intent of the user and instructs some other
object to carry out that request. When a user acts on the control by, say, clicking it or pressing the Return key,
the hardware device generates a raw event. The control accepts the event (as appropriately packaged for
Cocoa) and translates it into an instruction that is specific to the application. However, events by themselves
don't give much information about the user's intent; they merely tell you that the user clicked a mouse button
or pressed a key. So some mechanism must be called upon to provide the translation between event and
instruction. This mechanism is called target-action .
Cocoa uses the target-action mechanism for communication between a control and another object. This
mechanism allows the control and, in OS X its cell or cells, to encapsulate the information necessary to send
an application-specific instruction to the appropriate object. The receiving object—typically an instance of a
custom class—is called the target. The action is the message that the control sends to the target. The object
that is interested in the user event—the target—is the one that imparts significance to it, and this significance
is usually reflected in the name it gives to the action.
The Target
A target is a receiver of an action message. A control or, more frequently, its cell holds the target of its action
message as an outlet (see “Outlets” (page 67)). The target usually is an instance of one of your custom classes,
although it can be any Cocoa object whose class implements the appropriate action method.
You can also set a cell’s or control’s target outlet to nil and let the target object be determined at runtime.
When the targetis nil,the application object(NSApplication or UIApplication)searchesfor an appropriate
receiver in a prescribed order:
1. It begins with the first responder in the key window and follows nextResponder links up the responder
chain to the window object’s (NSWindow or UIWindow) content view.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
73
Target-ActionNote: A key window in OS X responds to key presses for an application and is the receiver of
messages from menus and dialogs. An application’s main window is the principal focus of user
actions and often has key status as well.
2. It tries the window object and then the window object’s delegate.
3. If the main window is different from the key window, it then starts over with the first responder in the
main window and works its way up the main window’s responder chain to the window object and its
delegate.
4. Next, the application object tries to respond. If it can’t respond, it tries its delegate. The application object
and its delegate are the receivers of last resort.
Control objects do not (and should not) retain their targets. However, clients of controlssending action messages
(applications, usually) are responsible for ensuring that their targets are available to receive action messages.
To do this, they may have to retain their targets in memory-managed environments. This precaution applies
equally to delegates and data sources.
The Action
An action is the message a control sends to the target or, from the perspective of the target, the method the
target implements to respond to the action message. A control or—as is frequently the case in AppKit—a
control’s cell stores an action as an instance variable of type SEL. SEL is an Objective-C data type used to
specify the signature of a message. An action message must have a simple, distinct signature. The method it
invokes returns nothing and usually has a sole parameter of type id. This parameter, by convention, is named
sender. Here is an example from the NSResponder class, which defines a number of action methods:
- (void)capitalizeWord:(id)sender;
Action methods declared by some Cocoa classes can also have the equivalent signature:
- (IBAction) deleteRecord:(id)sender;
In this case, IBAction does not designate a data type for a return value; no value is returned. IBAction is a
type qualifier that Interface Builder notices during application development to synchronize actions added
programmatically with its internal list of action methods defined for a project.
Target-Action
The Action
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
74iOS Note: In UIKit, action selectors can also take two other forms. See “Target-Action in UIKit” (page
78) for details.
The senderparameter usually identifies the control sending the action message (although it can be another
object substituted by the actual sender). The idea behind this is similar to a return address on a postcard. The
target can query the sender for more information if it needsto. If the actualsending objectsubstitutes another
object as sender, you should treat that object in the same way. For example, say you have a text field and when
the user enters text, the action method nameEntered: is invoked in the target:
- (void)nameEntered:(id) sender {
NSString *name = [sender stringValue];
if (![name isEqualToString:@""]) {
NSMutableArray *names = [self nameList];
[names addObject:name];
[sender setStringValue:@""];
}
}
Here the responding method extracts the contents of the text field, adds the string to an array cached as an
instance variable, and clears the field. Other possible queries to the sender would be asking an NSMatrix
object for its selected row ([sender selectedRow]), asking an NSButton object for its state ([sender
state]), and asking any cell associated with a control for its tag ([[sender cell] tag]), a tag being a
numeric identifier.
Target-Action in the AppKit Framework
The AppKit framework uses specific architectures and conventions in implementing target-action.
Controls, Cells, and Menu Items
Most controls in AppKit are objects that inherit from the NSControl class. Although a control has the initial
responsibility for sending an action message to its target, it rarely carries the information needed to send the
message. For this, it usually relies on its cell or cells.
A control almost always has one or more cells—objects that inherit from NSCell—associated with it. Why is
there this association? A control is a relatively “heavy” object because it inherits all the combined instance
variables of its ancestors, which include the NSView and NSResponder classes. Because controls are expensive,
Target-Action
Target-Action in the AppKit Framework
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
75cells are used to subdivide the screen real estate of a control into various functional areas. Cells are lightweight
objects that can be thought of as overlaying all or part of the control. But it's not only a division of area, it's a
division of labor. Cells do some of the drawing that controls would otherwise have to do, and cells hold some
of the data that controls would otherwise have to carry. Two items of this data are the instance variables for
target and action. Figure 12-1 (page 76) depicts the control-cell architecture.
Being abstract classes, NSControl and NSCell both incompletely handle the setting of the target and action
instance variables. By default, NSControl simply sets the information in its associated cell, if one exists.
(NSControl itself supports only a one-to-one mapping between itself and a cell; subclasses of NSControl
such as NSMatrix support multiple cells.) In its default implementation, NSCell simply raises an exception.
You must go one step further down the inheritance chain to find the class that really implements the setting
of target and action: NSActionCell.
Objects derived from NSActionCell provide target and action values to their controls so the controls can
compose and send an action message to the proper receiver. An NSActionCell object handles mouse (cursor)
tracking by highlighting its area and assisting its control in sending action messages to the specified target.
In most cases, the responsibility for an NSControl object’s appearance and behavior is completely given over
to a corresponding NSActionCell object. (NSMatrix, and itssubclass NSForm, are subclasses of NSControl
that don’t follow this rule.)
Figure 12-1 How the target-action mechanism works in the control-cell architecture
washerObject
dryerObject
washerCell
dryerCell
Target
Action
Target
Action
Control
(NSMatrix)
Cells
(NSButtonCell)
(void)dryIt: (id)sender
(void)washIt: (id)sender
Target-Action
Target-Action in the AppKit Framework
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
76When users choose an item from a menu, an action is sent to a target. Yet menus (NSMenu objects) and their
items (NSMenuItem objects) are completely separate, in an architectural sense, from controls and cells. The
NSMenuItem class implements the target-action mechanism for its own instances; an NSMenuItem object has
both target and action instance variables (and related accessor methods) and sends the action message to the
target when a user chooses it.
Note: See Control and Cell Programming Topics for Cocoa and Application Menu and Pop-up List
Programming Topics for more information about the control-cell architecture.
Setting the Target and Action
You can set the targets and actions of cells and controls programmatically or by using Interface Builder. For
most developers and mostsituations, Interface Builder isthe preferred approach. When you use it to set controls
and targets, Interface Builder provides visual confirmation, allows you to lock the connections, and archives
the connections to a nib file. The procedure is simple:
1. Declare an action method in the header file of your custom class that has the IBAction qualifier.
2. In Interface Builder, connect the control sending the message to the action method of the target.
If the action is handled by a superclass of your custom class or by an off-the-shelf AppKit or UIKit class, you
can make the connection without declaring any action method. Of course, if you declare an action method
yourself, you must be sure to implement it.
To set the action and the target programmatically, use the following methods to send messages to a control
or cell object:
- (void)setTarget:(id)anObject;
- (void)setAction:(SEL)aSelector;
The following example shows how you might use these methods:
[aCell setTarget:myController];
[aControl setAction:@selector(deleteRecord:)];
[aMenuItem setAction:@selector(showGuides:)];
Target-Action
Target-Action in the AppKit Framework
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
77Programmatically setting the target and action does have its advantages and in certain situations it is the only
possible approach. For example, you might want the target or action to vary according to some runtime
condition, such as whether a network connection exists or whether an inspector window has been loaded.
Another example is when you are dynamically populating the items of a pop-up menu, and you want each
pop-up item to have its own action.
Actions Defined by AppKit
The AppKit framework not only includes many NSActionCell-based controls for sending action messages,
it defines action methods in many of its classes. Some of these actions are connected to default targets when
you create a Cocoa application project. For example, the Quit command in the application menu is connected
to the terminate: method in the global application object (NSApp).
The NSResponder class also defines many default action messages (also known as standard commands) for
common operations on text. This allowsthe Cocoa textsystem to send these action messages up an application’s
responder chain—a hierarchical sequence of event-handling objects—where it can be handled by the first
NSView, NSWindow, or NSApplication object that implements the corresponding method.
Target-Action in UIKit
The UIKit framework also declares and implements a suite of control classes; the control classesin thisframework
inherit from the UIControl class, which defines most of the target-action mechanism for iOS. However there
are some fundamental differences in how the AppKit and UIKit frameworks implement target-action. One of
these differences is that UIKit does not have any true cell classes. Controls in UIKit do not rely upon their cells
for target and action information.
A larger difference in how the two frameworks implement target-action lies in the nature of the event model.
In the AppKit framework, the user typically uses a mouse and keyboard to register events for handling by the
system. These events—such as clicking on a button—are limited and discrete. Consequently, a control object
in AppKit usually recognizes a single physical event as the trigger for the action it sends to its target. (In the
case of buttons, this is a mouse-up event.) In iOS, the user’s fingers are what originate events instead of mouse
clicks, mouse drags, or physical keystrokes. There can be more than one finger touching an object on the screen
at one time, and these touches can even be going in different directions.
To account for this multitouch event model, UIKit declares a set of control-event constants in UIControl.h
that specify various physical gestures that users can make on controls, such as lifting a finger from a control,
dragging a finger into a control, and touching down within a text field. You can configure a control object so
that it responds to one or more of these touch events by sending an action message to a target. Many of the
Target-Action
Target-Action in UIKit
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
78control classes in UIKit are implemented to generate certain control events; for example, instances of the
UISlider class generate a UIControlEventValueChanged control event, which you can use to send an
action message to a target object.
You set up a control so that it sends an action message to a target object by associating both target and action
with one or more control events. To do this, send addTarget:action:forControlEvents: to the control
for each target-action pair you want to specify. When the user touches the control in a designated fashion, the
control forwards the action message to the global UIApplication object in a
sendAction:to:from:forEvent: message. As in AppKit, the global application object is the centralized
dispatch point for action messages. If the control specifies a nil target for an action message, the application
queries objects in the responder chain until it finds one that is willing to handle the action message—that is,
one implementing a method corresponding to the action selector.
In contrast to the AppKit framework, where an action method may have only one or perhapstwo valid signatures,
the UIKit framework allows three different forms of action selector:
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
To learn more about the target-action mechanism in UIKit, read UIControl Class Reference .
Target-Action
Target-Action in UIKit
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
79There are a number of data types in the Core Foundation framework and the Foundation framework that can
be used interchangeably. This capability, called toll-free bridging , means that you can use the same data type
as the parameter to a Core Foundation function call or as the receiver of an Objective-C message. For example,
NSLocale (see NSLocale Class Reference ) is interchangeable with its Core Foundation counterpart, CFLocale
(see CFLocale Reference ). Therefore, in a method where you see an NSLocale * parameter, you can pass a
CFLocaleRef, and in a function where you see a CFLocaleRef parameter, you can pass an NSLocale
instance. You cast one type to the other to suppress compiler warnings, as illustrated in the following example.
NSLocale *gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
CFLocaleRef gbCFLocale = (CFLocaleRef) gbNSLocale;
CFStringRef cfIdentifier = CFLocaleGetIdentifier (gbCFLocale);
NSLog(@"cfIdentifier: %@", (NSString *)cfIdentifier);
// logs: "cfIdentifier: en_GB"
CFRelease((CFLocaleRef) gbNSLocale);
CFLocaleRef myCFLocale = CFLocaleCopyCurrent();
NSLocale * myNSLocale = (NSLocale *) myCFLocale;
[myNSLocale autorelease];
NSString *nsIdentifier = [myNSLocale localeIdentifier];
CFShow((CFStringRef) [@"nsIdentifier: " stringByAppendingString:nsIdentifier]);
// logs identifier for current locale
Note from the example that the memory management functions and methods are also interchangeable—you
can use CFRelease with a Cocoa object and release and autorelease with a Core Foundation object.
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
80
Toll-Free BridgingNote: When using garbage collection, there are important differencesto how memory management
works for Cocoa objects and Core Foundation objects. See “Using Core Foundation with Garbage
Collection” for details.
Toll-free bridging has been available since OS X v10.0. Table 13-1 provides a list of the data types that are
interchangeable between Core Foundation and Foundation. For each pair, the table also lists the version of
OS X in which toll-free bridging between them became available.
Table 13-1 Data types that can be used interchangeably between Core Foundation and Foundation
Core Foundation type Foundation class Availability
CFArrayRef NSArray OS X v10.0
CFAttributedStringRef NSAttributedString OS X v10.4
CFCalendarRef NSCalendar OS X v10.4
CFCharacterSetRef NSCharacterSet OS X v10.0
CFDataRef NSData OS X v10.0
CFDateRef NSDate OS X v10.0
CFDictionaryRef NSDictionary OS X v10.0
CFErrorRef NSError OS X v10.5
CFLocaleRef NSLocale OS X v10.4
CFMutableArrayRef NSMutableArray OS X v10.0
CFMutableAttributedStringRef NSMutableAttributedString OS X v10.4
CFMutableCharacterSetRef NSMutableCharacterSet OS X v10.0
CFMutableDataRef NSMutableData OS X v10.0
CFMutableDictionaryRef NSMutableDictionary OS X v10.0
CFMutableSetRef NSMutableSet OS X v10.0
CFMutableStringRef NSMutableString OS X v10.0
CFNumberRef NSNumber OS X v10.0
CFReadStreamRef NSInputStream OS X v10.0
Toll-Free Bridging
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
81Core Foundation type Foundation class Availability
CFRunLoopTimerRef NSTimer OS X v10.0
CFSetRef NSSet OS X v10.0
CFStringRef NSString OS X v10.0
CFTimeZoneRef NSTimeZone OS X v10.0
CFURLRef NSURL OS X v10.0
CFWriteStreamRef NSOutputStream OS X v10.0
Note: Not all data types are toll-free bridged, even though their names might suggest that they
are. For example, NSRunLoop is not toll-free bridged to CFRunLoop, NSBundle is not toll-free bridged
to CFBundle, and NSDateFormatter is not toll-free bridged to CFDateFormatter.
Toll-Free Bridging
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
82This table describes the changes to Concepts in Objective-C Programming .
Date Notes
Descriptions of design patterns, architectures, and other concepts
important in Cocoa and Cocoa Touch development.
2012-01-09
2012-01-09 | © 2012 Apple Inc. All Rights Reserved.
83
Document Revision HistoryApple Inc.
© 2012 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, Cocoa, Cocoa Touch,
Objective-C, OS X, and Xcode are trademarks of
Apple Inc., registered in the U.S. and other
countries.
iOS is a trademark or registered trademark of
Cisco in the U.S. and other countries and is used
under license.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
Local and Push
Notification
Programming GuideContents
About Local Notifications and Push Notifications 5
At a Glance 6
The Problem That Local and Push Notifications Solve 6
Local and Push Notifications Are Different in Origination 6
You Schedule a Local Notification, Register a Push Notification, and Handle Both 6
The Apple Push Notification Service Is the Gateway for Push Notifications 7
You Must Obtain Security Credentials for Push Notifications 7
The Provider Communicates with APNs over a Binary Interface 7
Prerequisites 8
See Also 8
Local and Push Notifications in Depth 9
Push and Local Notifications Appear the Same to Users 9
More About Local Notifications 12
More About Push Notifications 13
Scheduling, Registering, and Handling Notifications 15
Preparing Custom Alert Sounds 15
Scheduling Local Notifications 16
Registering for Remote Notifications 19
Handling Local and Remote Notifications 21
Passing the Provider the Current Language Preference (Remote Notifications) 26
Apple Push Notification Service 28
A Push Notification and Its Path 28
Feedback Service 29
Quality of Service 30
Security Architecture 30
Service-to-Device Connection Trust 31
Provider-to-Service Connection Trust 31
Token Generation and Dispersal 32
Token Trust (Notification) 34
Trust Components 34
The Notification Payload 35
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
2Localized Formatted Strings 37
Examples of JSON Payloads 39
Provisioning and Development 42
Sandbox and Production Environments 42
Provisioning Procedures 43
Creating the SSL Certificate and Keys 43
Creating and Installing the Provisioning Profile 44
Installing the SSL Certificate and Key on the Server 45
Provider Communication with Apple Push Notification Service 47
General Provider Requirements 47
The Binary Interface and Notification Formats 48
The Feedback Service 53
Document Revision History 55
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
3
ContentsFigures, Tables, and Listings
Local and Push Notifications in Depth 9
Figure 1-1 A notification alert 10
Figure 1-2 An application icon with a badge number (iOS) 11
Figure 1-3 A notification alert message with the action button suppressed 11
Scheduling, Registering, and Handling Notifications 15
Listing 2-1 Creating, configuring, and scheduling a local notification 17
Listing 2-2 Presenting a local notification immediately while running in the background 18
Listing 2-3 Registering for remote notifications 21
Listing 2-4 Handling a local notification when an application is launched 23
Listing 2-5 Downloading data from a provider 24
Listing 2-6 Handling a local notification when an application is already running 25
Listing 2-7 Getting the current supported language and sending it to the provider 26
Apple Push Notification Service 28
Figure 3-1 A push notification from a provider to a client application 29
Figure 3-2 Push notifications from multiple providers to multiple devices 29
Figure 3-3 Sharing the device token 33
Table 3-1 Keys and values of the aps dictionary 36
Table 3-2 Child properties of the alert property 36
Provider Communication with Apple Push Notification Service 47
Figure 5-1 Simple notification format 49
Figure 5-2 Enhanced notification format 50
Figure 5-3 Format of error-response packet 51
Figure 5-4 Binary format of a feedback tuple 54
Table 5-1 Codes in error-response packet 51
Listing 5-1 Sending a notification in the simple format via the binary interface 49
Listing 5-2 Sending a notification in the enhanced format via the binary interface 52
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
4Local notifications and push notifications are ways for an application that isn’t running in the foreground to
let its users know it has information for them. The information could be a message, an impending calendar
event, or new data on a remote server. When presented by the operating system, local and push notifications
look and sound the same. They can display an alert message or they can badge the application icon. They can
also play a sound when the alert or badge number is shown.
Push notifications were introduced in iOS 3.0 and in OS X version 10.7. Local notifications were introduced in
iOS 4.0; they are not available in OS X.
When users are notified that the application has a message, event, or other data for them, they can launch the
application and see the details. They can also choose to ignore the notification, in which case the application
is not activated.
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
5
About Local Notifications and Push NotificationsNote: Push notifications and local notifications are not related to broadcast notifications
(NSNotificationCenter) or key-value observing notifications.
At a Glance
Local notifications and push notifications have several important aspects you should be aware of.
The Problem That Local and Push Notifications Solve
Only one application can be active in the foreground at any time. Many applications operate in a time-based
or interconnected environment where events of interest to users can occur when the application is not in the
foreground. Local and push notifications allow these applicationsto notify their users when these events occur.
Relevant Chapter: “Local and Push Notifications in Depth” (page 9)
Local and Push Notifications Are Different in Origination
Local and push notifications serve different design needs. A local notification is local to an application on an
iPhone, iPad, or iPod touch. Push notifications—also known as remote notifications—arrive from outside a
device. They originate on a remote server—the application’s provider—and are pushed to applications on
devices (via the Apple Push Notification service) when there are messages to see or data to download.
Relevant Chapter: “Local and Push Notifications in Depth” (page 9)
You Schedule a Local Notification, Register a Push Notification, and Handle Both
To have iOS deliver a local notification at a later time, an application creates a UILocalNotification object,
assignsit a delivery date and time,specifies presentation details, and schedulesit. To receive push notifications,
an application must register to receive the notifications and then pass to its provider a device token it gets
from the operating system.
When the operating system delivers a local notification (iOS only) or push notification (iOS or OS X) and the
target application is not running in the foreground, it presents the notification (alert, icon badge number,
sound). If there is a notification alert and the user taps or clicks the action button (or moves the action slider),
the application launches and calls a method to pass in the local-notification object or remote-notification
payload. If the application is running in the foreground when the notification is delivered, the application
delegate receives a local or push notification.
About Local Notifications and Push Notifications
At a Glance
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
6Relevant Chapter: “Scheduling, Registering, and Handling Notifications” (page 15)
The Apple Push Notification Service Is the Gateway for Push Notifications
Apple Push Notification service (APNs) propagates push notificationsto devices having applicationsregistered
to receive those notifications. Each device establishes an accredited and encrypted IP connection with the
service and receives notifications over this persistent connection. Providers connect with APNs through a
persistent and secure channel while monitoring incoming data intended for their client applications. When
new data for an application arrives, the provider prepares and sends a notification through the channel to
APNs, which pushes the notification to the target device.
Related Chapter: “Apple Push Notification Service” (page 28)
You Must Obtain Security Credentials for Push Notifications
To develop and deploy the provider side of an application for push notifications, you must get SSL certificates
from the appropriate Dev Center. Each certificate is limited to a single application, identified by its bundle ID;
it is also limited to one of two environments, sandbox (for development and testing) and production. These
environments have their own assigned IP address and require their own certificates. You must also obtain
provisioning profiles for each of these environments.
Related Chapter: “Provisioning and Development” (page 42)
The Provider Communicates with APNs over a Binary Interface
The binary interface is asynchronous and uses a streaming TCP socket design for sending push notifications
as binary content to APNs. There is a separate interface for the sandbox and production environments, each
with its own address and port. For each interface, you need to use TLS (or SSL) and the SSL certificate you
obtained to establish a secured communications channel. The provider composes each outgoing notification
and sends it over this channel to APNs.
APNs has a feedback service that maintains a per-application list of devicesfor which there were failed-delivery
attempts (that is, APNs was unable to deliver a push notification to an application on a device). Periodically,
the provider should connect with the feedback service to see what devices have persistent failures so that it
can refrain from sending push notifications to them.
About Local Notifications and Push Notifications
At a Glance
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
7Related Chapters: “Apple Push Notification Service” (page 28), “Provider Communication with
Apple Push Notification Service” (page 47)
Prerequisites
For local notifications and the client-side implementation of push notifications, familiarity with application
development for iOS is assumed. For the provider side of the implementation, knowledge of TLS/SSL and
streaming sockets is helpful.
See Also
You might find these additional sources of information useful for understanding and implementing local and
push notifications :
● The reference documentation for UILocalNotification, UIApplication, and
UIApplicationDelegate describe the local- and push-notification API for client applications in iOS.
● The reference documentation for NSApplication and NSApplicationDelegate Protocol describe
the push-notification API for client applications in OS X.
● Security Overview describes the security technologies and techniques used for the iOS and Macs.
● RFC 5246 is the standard for the TLS protocol.
Secure communication between data providers and Apple Push Notification Service requires knowledge
of Transport Layer Security (TLS) or its predecessor, Secure Sockets Layer (SSL). Refer to one of the many
online or printed descriptions of these cryptographic protocols for further information.
About Local Notifications and Push Notifications
Prerequisites
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
8The essential purpose of both local and push notifications is to enable an application to inform its users that
it hassomething for them—for example, a message or an upcoming appointment—when the application isn’t
running in the foreground. The essential difference between local notifications and push notificationsissimple:
● Local notifications are scheduled by an application and delivered by iOS on the same device.
Local notifications are available in iOS only.
● Push notifications, also known as remote notifications, are sent by an application’s remote server (its
provider) to Apple Push Notification service, which pushes the notification to devices on which the
application is installed.
Push notifications are available in both iOS and, beginning with OS X v10.7 (Lion), OS X.
The following sections describe what local and push notifications have in common and then examine their
differences.
Note: For usage guidelines for push and local notifications in iOS, see “Enabling Push Notifications”
in iOS Human Interface Guidelines.
Push and Local Notifications Appear the Same to Users
From a user’s perspective, a push notification and a local notification appear to be the same thing. But that’s
because the purpose is the same: to notify users of an application—which might not currently be running in
the foreground—that there is something of interest for them.
Let’s say you’re using your iPhone—making phone calls, surfing the Internet, listening to music. You have a
chess application installed on your iPhone, and you decide to start a game with a friend who is playing remotely.
You make the first move (which is duly noted by the game’s provider), and then quit the client application to
read some email. In the meantime, your friend counters your move. The provider for the chess application
learns about this move and, seeing that the chess application on your device is no longer connected, sends a
push notification to Apple Push Notification service (APNs).
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
9
Local and Push Notifications in DepthAlmost immediately, your device—or more precisely, the operating system on your device—receives the
notification over the Wi-Fi or cellular connection from APNs. Because your chess application is not currently
running, iOS displays an alert similar to Figure 1-1. The message consists of the application name, a short
message, and (in this case) two buttons: Close and View. The button on the right is called the action button
and its default title is“View”. An application can customize the title of the action button and can internationalize
the button title and the message so that they are in the user’s preferred language.
Figure 1-1 A notification alert
If you tap the View button, the chess application launches, connects with its provider, downloads the new
data, and adjuststhe chessboard user interface to show your friend’s move. (Pressing Close dismissesthe alert.)
OS X Note: Currently, the only type of push notification in OS X for non-running applications is icon
badging. In other words, an application’s icon in the Dock is badged only if the application isn’t
running. If users have not already placed the icon in the Dock, the system inserts the icon into the
Dock so that it can badge it (and removes it after the application next terminates). Running
applications may examine the notification payload for other types of notifications(alerts and sounds)
and handle them appropriately.
Let’s consider a type of application with another requirement. This application manages a to-do list, and each
item in the list has a date and time when the item must be completed. The user can request the application
to notify it at a specific interval before this due date expires. To effect this, the application schedules a local
notification for that date and time. Instead of specifying an alert message, this time the application chooses
to specify a badge number (1). At the appointed time, iOS displays a badge number in the upper-right corner
of the icon of the application, such as illustrated in Figure 1-2.
Local and Push Notifications in Depth
Push and Local Notifications Appear the Same to Users
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
10For both local and push notifications, the badge number is specific to an application and can indicate any
number of things,such asthe number of impending calendar events or the number of data itemsto download
or the number of unread (but already downloaded) email messages. The user sees the badge and taps the
application icon—or, in OS X, clicks the icon in the dock—to launch the application, which then displays the
to-do item or whatever else is of interest to the user.
Figure 1-2 An application icon with a badge number (iOS)
In iOS, an application can specify a sound file along with an alert message or badge number. The sound file
should contain a short, distinctive sound. At the same moment iOS displays the alert or badges the icon, it
plays the sound to alert the user to the incoming notification.
Notification alert message can have one button instead of two. In the latter case, the action button issuppressed,
as illustrated in Figure 1-3. The user can only dismiss these kinds of alerts.
Figure 1-3 A notification alert message with the action button suppressed
Local and Push Notifications in Depth
Push and Local Notifications Appear the Same to Users
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
11The operating system delivers a local or push notification to an application whether the application is running
or not. If the application is running when the notification arrives, no alert is displayed or icon badged or sound
played, even if (in iOS) the device screen islocked. Instead, the application delegate isinformed of the notification
and can handle it directly. (“Scheduling, Registering, and Handling Notifications” (page 15) discusses the
various delivery scenarios in detail.)
Users of iPhone, iPad, and iPod touch devices can control whether the device or specific applications installed
on the device should receive push notifications. They can also selectively enable or disable push notification
types (that is, icon badging, alert messages, and sounds) for specific applications. They set these restrictions
in the Notifications preference of the Settings application. The UIKit framework provides a programming
interface to detect this user preference for a given application.
More About Local Notifications
Local notifications(available only in iOS) are ideally suited for applications with time-based behaviors, including
simple calendar or to-do list applications. Applicationsthat run in the background for the limited period allowed
by iOS might also find local notifications useful. For example, applicationsthat depend on serversfor messages
or data can poll their servers for incoming items while running in the background; if a message is ready to
view or an update is ready to download, they can then present a local notification immediately to inform their
users.
A local notification is an instance of UILocalNotification with three general kinds of properties:
● Scheduled time. You must specify the date and time the operating system delivers the notification; this
is known as the fire date . You may qualify the fire date with a specific time zone so that the system can
make adjustments to the fire date when the user travels. You can also request the operating system to
reschedule the notification on some regular interval (weekly, monthly, and so on).
● Notification type. This category includes the alert message, the title of the action button, the application
icon badge number, and a sound to play.
● Custom data. Local notifications can include a dictionary of custom data.
“Scheduling Local Notifications” (page 16) describesthese propertiesin programmatic detail.Once an application
has created a local-notification object, it can either schedule it with the operating system or present it
immediately.
Each application on a device is limited to the soonest-firing 64 scheduled local notifications. The operating
system discards notificationsthat exceed thislimit. It considers a recurring notification to be a single notification.
Local and Push Notifications in Depth
More About Local Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
12More About Push Notifications
An iOS application or a Mac app is often only a part of a larger application based on the client/server model.
The client side of the application is installed on the device or computer; the server side of the application has
the main function of providing data to its many client applications. (Hence it is termed a provider.) A client
application occasionally connects with its provider and downloads the data that is waiting for it. Email and
social-networking applications are examples of this client/server model.
But what if the application is not connected to its provider or even running on the device or computer when
the provider has new data for it to download? How does it learn about this waiting data? Push notifications
are the solution to this dilemma. A push notification is a short message that a provider has delivered to the
operating system of a device or computer; the operating system, in turn, informsthe user of a client application
that there is data to be downloaded, a message to be viewed, and so on. If the user enables this feature (on
iOS) and the application is properly registered, the notification is delivered to the operating system and possibly
to the application. Apple Push Notification service is the primary technology for the push-notification feature.
Push notificationsserve much the same purpose as a background application on a desktop system, but without
the additional overhead. For an application that is not currently running—or, in the case of iOS, not running
in the foreground—the notification occurs indirectly. The operating system receives a push notification on
behalf of the application and alerts the user. Once alerted, users may choose to launch the application, which
then downloads the data from its provider. If an application is running when a notification comes in, the
application can choose to handle the notification directly.
iOS Note: Beginning with iOS 4.0, applications can run in the background, but only for a limited
period. Only one application may be executing in the foreground at a time.
As its name suggests, Apple Push Notification service (APNs) uses a push design to deliver notifications to
devices and computers. A push design differs from its opposite, a pull design, in that the immediate recipient
of the notification—in this case, the operating system—passively listensfor updatesrather than actively polling
for them. A push design makes possible a wide and timely dissemination of information with few of the
scalability problems inherent with pull designs. APNs uses a persistent IP connection for implementing push
notifications.
Most of a push notification consists of a payload: a property list containing APNs-defined propertiesspecifying
how the user is to be notified. For performance reasons, the payload is deliberately small. Although you may
define custom properties for the payload, you should never use the remote-notification mechanism for data
transport because delivery of push notificationsis not guaranteed. For more on the payload,see “The Notification
Payload” (page 35).
Local and Push Notifications in Depth
More About Push Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
13APNs retains the last notification it receives from a provider for an application on a device; so, if a device or
computer comes online and has not received the notification, APNs pushes the stored notification to it. A
device running iOS receives push notifications over both Wi-Fi and cellular connections; a computer running
OS X receives push notifications over both WiFi and Ethernet connections.
Important: In iOS, Wi-Fi is used for push notifications only if there is no cellular connection or if the device
is an iPod touch. For some devices to receive notifications via Wi-Fi, the device’s display must be on (that
is, it cannot be sleeping) or it must be plugged in. The iPad, on the other hand, remains associated with
the Wi-Fi access point while asleep, thus permitting the delivery of push notifications. The Wi-Fi radio wakes
the host processor for any incoming traffic.
Adding the remote-notification feature to your application requires that you obtain the proper certificates
from the Dev Center for either iOS or OS X and then write the requisite code for the client and provider sides
of the application. “Provisioning and Development” (page 42) explains the provisioning and setup steps, and
“Provider Communication with Apple Push Notification Service” (page 47) and “Scheduling, Registering, and
Handling Notifications” (page 15) describe the details of implementation.
Apple Push Notification service continually monitors providersfor irregular behavior, looking forsudden spikes
of activity, rapid connect-disconnect cycles, and similar activity. Apple seeksto notify providers when it detects
this behavior, and if the behavior continues, it may put the provider’s certificate on a revocation list and refuse
further connections. Any continued irregular or problematic behavior may result in the termination of a
provider's access to APNs.
Local and Push Notifications in Depth
More About Push Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
14This chapter describes the tasks that a iPhone, iPad, or iPod touch application should (or might) do to schedule
local notifications, register remote notifications, and handle both local and remote notifications. Because the
client-side API for push notifications refers to push notifications as remote notifications, that terminology is
used in this chapter.
Preparing Custom Alert Sounds
For remote notifications in iOS, you can specify a custom sound that iOS plays when it presents a local or
remote notification for an application. The sound files must be in the main bundle of the client application.
Because custom alert sounds are played by the iOS system-sound facility, they must be in one of the following
audio data formats:
● Linear PCM
● MA4 (IMA/ADPCM)
● µLaw
● aLaw
You can package the audio data in an aiff, wav, or caf file. Then, in Xcode, add the sound file to your project
as a nonlocalized resource of the application bundle.
You may use the afconvert tool to convert sounds. For example, to convert the 16-bit linear PCM system
sound Submarine.aiff to IMA4 audio in a CAF file, use the following command in the Terminal application:
afconvert /System/Library/Sounds/Submarine.aiff ~/Desktop/sub.caf -d ima4 -f caff
-v
You can inspect a sound to determine its data format by opening it in QuickTime Player and choosing Show
Movie Inspector from the Movie menu.
Custom sounds must be under 30 seconds when played. If a custom sound is over that limit, the defaultsystem
sound is played instead.
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
15
Scheduling, Registering, and Handling NotificationsScheduling Local Notifications
Creating and scheduling local notifications in iOS requires that you perform a few simple steps:
1. Allocate and initialize a UILocalNotification object.
2. Set the date and time that the operating system should deliver the notification. This is the fireDate
property.
If you set the timeZone property to the NSTimeZone object for the current locale, the system automatically
adjusts the fire date when the device travels across (and is reset for) different time zones. (Time zones
affect the values of date components—that is, day, month, hour, year, and minute—that the system
calculates for a given calendar and date value.) You can also schedule the notification for delivery on a
recurring basis (daily, weekly, monthly, and so on).
3. Configure the substance of the notification: alert, icon badge number, and sound.
● The alert has a property for the message (the alertBody property) and for the title of the action
button or slider (alertAction); both of these string values can be internationalized for the user’s
current language preference.
● You set the badge number to display on the application icon through the
applicationIconBadgeNumber property.
● You can assign the filename of a nonlocalized custom sound in the application’s main bundle to the
soundName property; to get the default system sound, assign
UILocalNotificationDefaultSoundName. Sounds should always accompany an alert message
or icon badging; they should not be played otherwise.
4. Optionally, you can attach custom data to the notification through the userInfo property.
Keys and values in the userInfo dictionary must be property-list objects.
5. Schedule the local notification for delivery.
You schedule a local notification by calling the UIApplicationmethod scheduleLocalNotification:.
The application uses the fire date specified in the UILocalNotification object for the moment of
delivery. Alternatively, you can present the notification immediately by calling the
presentLocalNotificationNow: method.
The method in Listing 2-1 creates and schedules a notification to inform the user of a hypothetical to-do list
application about the impending due date of a to-do item. There are a couple things to note about it. For the
alertBody and alertAction properties, it fetches from the main bundle (via the NSLocalizedString
macro) strings localized to the user’s preferred language. It also adds the name of the relevant to-do item to
a dictionary assigned to the userInfo property.
Scheduling, Registering, and Handling Notifications
Scheduling Local Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
16Listing 2-1 Creating, configuring, and scheduling a local notification
- (void)scheduleNotificationWithItem:(ToDoItem *)item interval:(int)minutesBefore
{
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setDay:item.day];
[dateComps setMonth:item.month];
[dateComps setYear:item.year];
[dateComps setHour:item.hour];
[dateComps setMinute:item.minute];
NSDate *itemDate = [calendar dateFromComponents:dateComps];
[dateComps release];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
localNotif.fireDate = [itemDate addTimeInterval:-(minutesBefore*60)];
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in
%i minutes.", nil),
item.eventName, minutesBefore];
localNotif.alertAction = NSLocalizedString(@"View Details", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName
forKey:ToDoItemKey];
localNotif.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
}
Scheduling, Registering, and Handling Notifications
Scheduling Local Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
17You can cancel a specific scheduled notification by calling cancelLocalNotification: on the application
object, and you can cancel all scheduled notifications by calling cancelAllLocalNotifications. Both of
these methods also programmatically dismiss a currently displayed notification alert.
Applications might also find local notifications useful when they run in the background and some message,
data, or other item arrivesthat might be of interest to the user. In this case, they should present the notification
immediately using the UIApplication method presentLocalNotificationNow: (iOS gives an application
a limited time to run in the background). Listing 2-2 illustrates how you might do this.
Listing 2-2 Presenting a local notification immediately while running in the background
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"Application entered background state.");
// bgTask is instance variable
NSAssert(self->bgTask == UIInvalidBackgroundTask, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}];
dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
NSString *friend = [self checkForIncomingChat];
if (friend) {
UILocalNotification *localNotif = [[UILocalNotification alloc]
init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat:
NSLocalizedString(@"%@ has a message for you.", nil),
friend];
localNotif.alertAction = NSLocalizedString(@"Read Message",
nil);
localNotif.soundName = @"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
[application presentLocalNotificationNow:localNotif];
Scheduling, Registering, and Handling Notifications
Scheduling Local Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
18[localNotif release];
friend = nil;
break;
}
}
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}
Registering for Remote Notifications
An application must register with Apple Push Notification service for the operating systems on a device and
on a computer to receive remote notifications sent by the application’s provider. Registration has three stages:
1. The application calls the registerForRemoteNotificationTypes: method.
2. The delegate implements the
application:didRegisterForRemoteNotificationsWithDeviceToken: method to receive the
device token.
3. It passes the device token to its provider as a non-object, binary value.
Note: Unless otherwise noted, all methods cited in thissection are declared with identicalsignatures
by both UIApplication and NSApplication, and, for delegates, by both
NSApplicationDelegate Protocol and UIApplicationDelegate.
What happens between the application, the device, Apple Push Notification Service, and the provider during
this sequence is illustrated by Figure 3-3 in “Token Generation and Dispersal” (page 32).
An application should register every time it launches and give its provider the current token. It calls
theregisterForRemoteNotificationTypes: method to kick off the registration process. The parameter
of this method takes a UIRemoteNotificationType (or, for OS X, a NSRemoteNotificationType) bit
mask that specifies the initial types of notifications that the application wishes to receive—for example,
icon-badging and sounds, but not alert messages. In iOS, users can thereafter modify the enabled notification
types in the Notifications preference of the Settings application. In both iOS and OS X, you can retrieve the
Scheduling, Registering, and Handling Notifications
Registering for Remote Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
19currently enabled notification types by calling the enabledRemoteNotificationTypes method. The
operating system does not badge icons, display alert messages, or play alertsoundsif any of these notifications
types are not enabled, even if they are specified in the notification payload.
OS XNote: Because the only notification type supported for non-running applicationsisicon-badging,
simply pass NSRemoteNotificationTypeBadge as the parameter of
registerForRemoteNotificationTypes:.
If registration issuccessful, APNsreturns a device token to the device and iOS passesthe token to the application
delegate in the application:didRegisterForRemoteNotificationsWithDeviceToken: method. The
application should connect with its provider and pass it this token, encoded in binary format. If there is a
problem in obtaining the token, the operating system informs the delegate by calling the
application:didFailToRegisterForRemoteNotificationsWithError:method. The NSError object
passed into this method clearly describes the cause of the error. The error might be, for instance, an erroneous
aps-environment value in the provisioning profile. You should view the error as a transient state and not
attempt to parse it. (See “Creating and Installing the Provisioning Profile” (page 44) for details.)
iOS Note: If a cellular or Wi-Fi connection is not available, neither the
application:didRegisterForRemoteNotificationsWithDeviceToken: method or the
application:didFailToRegisterForRemoteNotificationsWithError: method is called.
For Wi-Fi connections, this sometimes occurs when the device cannot connect with APNs over port
5223. If this happens, the user can move to another Wi-Fi network that isn’t blocking this port or,
on an iPhone or iPad, wait until the cellular data service becomes available. In either case, the
connection should then succeed and one of the delegation methods is called.
By requesting the device token and passing it to the provider every time your application launches, you help
to ensure that the provider has the current token for the device. If a user restores a backup to a device or
computer other than the one that the backup was created for (for example, the user migrates data to a new
device or computer), he or she must launch the application at least once for it to receive notifications again.
If the user restores backup data to a new device or computer, or reinstalls the operating system, the device
token changes. Moreover, never cache a device token and give that to your provider; always get the token
from the system whenever you need it. If your application has previously registered, calling
registerForRemoteNotificationTypes: resultsin the operating system passing the device token to the
delegate immediately without incurring additional overhead.
Listing 2-3 gives a simple example of how you might register for remote notifications in an iOS application.
The code would be nearly identical for a Mac app. (SendProviderDeviceToken is a hypothetical method
defined by the client in which it connects with its provider and passes it the device token.)
Scheduling, Registering, and Handling Notifications
Registering for Remote Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
20Listing 2-3 Registering for remote notifications
- (void)applicationDidFinishLaunching:(UIApplication *)app {
// other setup tasks here....
[[UIApplication sharedApplication]
registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound)];
}
// Delegation methods
- (void)application:(UIApplication *)app
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
const void *devTokenBytes = [devToken bytes];
self.registered = YES;
[self sendProviderDeviceToken:devTokenBytes]; // custom method
}
- (void)application:(UIApplication *)app
didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(@"Error in registration. Error: %@", err);
}
Handling Local and Remote Notifications
Let’s review the possible scenarios when the operating delivers a local notification or a remote notification for
an application.
● The notification is delivered when the application isn’t running in the foreground.
In this case, the system presents the notification, displaying an alert, badging an icon, perhaps playing a
sound.
● As a result of the presented notification, the user taps the action button of the alert or taps (or clicks) the
application icon.
If the action button is tapped (on a device running iOS), the system launches the application and the
application calls its delegate’s application:didFinishLaunchingWithOptions: method (if
implemented); it passesin the notification payload (for remote notifications) or the local-notification object
(for local notifications).
Scheduling, Registering, and Handling Notifications
Handling Local and Remote Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
21If the application icon is tapped on a device running iOS, the application calls the same method, but
furnishes no information about the notification . If the application icon is clicked on a computer running
OS X, the application calls the delegate’s applicationDidFinishLaunching: method in which the
delegate can obtain the remote-notification payload.
iOS Note: The application delegate could implement applicationDidFinishLaunching:
rather than application:didFinishLaunchingWithOptions:, but that is strongly
discouraged. The latter method allows the application to receive information related to the
reason for its launching, which can include things other than notifications.
● The notification is delivered when the application is running in the foreground.
The application calls its delegate’s application:didReceiveRemoteNotification: method (for
remote notifications) or application:didReceiveLocalNotification:method (forlocal notifications)
and passes in the notification payload or the local-notification object.
Note: The delegate methods cited in this section that have “RemoteNotification” in their name are
declared with identical signatures by by both NSApplicationDelegate Protocol and
UIApplicationDelegate.
An application can use the passed-in remote-notification payload or, in iOS, the UILocalNotification
object to help set the context for processing the item related to the notification. Ideally, the delegate does the
following on each platform to handle the delivery of remote and local notifications in all situations:
● For OS X, it should adopt the NSApplicationDelegate Protocol protocol and implement both the
applicationDidFinishLaunching: method and the
application:didReceiveRemoteNotification: method.
● For iOS, it should should adopt the UIApplicationDelegate protocol and implement both the
application:didFinishLaunchingWithOptions: method and the
application:didReceiveRemoteNotification: or
application:didReceiveLocalNotification: method.
Scheduling, Registering, and Handling Notifications
Handling Local and Remote Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
22iOS Note: In iOS, you can determine whether an application is launched as a result of the user
tapping the action button or whether the notification was delivered to the already-running application
by examining the application state. In the delegate’s implementation of the
application:didReceiveRemoteNotification: or
application:didReceiveLocalNotification: method, get the value of the
applicationState property and evaluate it. If the value is UIApplicationStateInactive, the
user tapped the action button; if the value is UIApplicationStateActive, the application was
frontmost when it received the notification.
The delegate for an iOS application in Listing 2-4 implements the
application:didFinishLaunchingWithOptions: method to handle a local notification. It gets the
associated UILocalNotification object from the launch-options dictionary using the
UIApplicationLaunchOptionsLocalNotificationKey key. From the UILocalNotification object’s
userInfo dictionary, it accesses the to-do item that is the reason for the notification and uses it to set the
application’s initial context. As shown in this example, you should appropriately reset the badge number on
the application icon—or remove it if there are no outstanding items—as part of handling the notification.
Listing 2-4 Handling a local notification when an application is launched
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions {
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
NSString *itemName = [localNotif.userInfo objectForKey:ToDoItemKey];
[viewController displayItem:itemName]; // custom method
application.applicationIconBadgeNumber =
localNotif.applicationIconBadgeNumber-1;
}
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
The implementation for a remote notification would be similar, except that you would use a specially declared
constant in each platform as a key to access the notification payload:
Scheduling, Registering, and Handling Notifications
Handling Local and Remote Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
23●
In iOS, the delegate, in its implementation of the application:didFinishLaunchingWithOptions:
method, usesthe UIApplicationLaunchOptionsRemoteNotificationKey key to accessthe payload
from the launch-options dictionary.
●
In OS X, the delegate, in its implementation of the applicationDidFinishLaunching: method, uses
the the NSApplicationLaunchRemoteNotificationKey key to access the payload dictionary from
the userInfo dictionary of the NSNotification object that is passed into the method.
The payload itself is an NSDictionary object that contains the elements of the notification—alert message,
badge number, sound, and so on. It can also contain custom data the application can use to provide context
when setting up the initial user interface. See “The Notification Payload” (page 35) for details about the
remote-notification payload.
Important: You should never define custom properties in the notification payload for the purpose of
transporting customer data or any other sensitive data. Delivery of remote notifications is not guaranteed.
One example of an appropriate usage for a custom payload property is a string identifying an email account
from which messages are downloaded to an email client; the application can incorporate this string in its
download user-interface. Another example of custom payload property is a timestamp for when the provider
first sent the notification; the client application can use this value to gauge how old the notification is.
When handling remote notifications in application:didFinishLaunchingWithOptions: or
applicationDidFinishLaunching:, the application delegate might perform a major additional task. Just
after the application launches, the delegate should connect with its provider and fetch the waiting data. Listing
2-5 gives a schematic illustration of this procedure.
Listing 2-5 Downloading data from a provider
- (void)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary
*)opts {
// check launchOptions for notification payload and custom data, set UI context
[self startDownloadingDataFromProvider]; // custom method
app.applicationIconBadgeNumber = 0;
// other setup tasks here....
}
Scheduling, Registering, and Handling Notifications
Handling Local and Remote Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
24Note: A client application should always communicate with its provider asynchronously or on a
secondary thread.
The code in Listing 2-6 shows an implementation of the application:didReceiveLocalNotification:
method which, as you’ll recall, is called when application is running in the foreground. Here the application
delegate doesthe same work asit doesin Listing 2-4. It can accessthe UILocalNotification object directly
this time because this object is an argument of the method.
Listing 2-6 Handling a local notification when an application is already running
- (void)application:(UIApplication *)app
didReceiveLocalNotification:(UILocalNotification *)notif {
NSString *itemName = [notif.userInfo objectForKey:ToDoItemKey]
[viewController displayItem:itemName]; // custom method
application.applicationIconBadgeNumber =
notification.applicationIconBadgeNumber-1;
}
If you want your application to catch remote notifications that the system delivers while it is running in the
foreground, the application delegate should implement the
application:didReceiveRemoteNotification: method. The delegate should begin the procedure for
downloading the waiting data, message, or other item and, after this concludes, it should remove the badge
from the application icon. (If your application frequently checks with its provider for new data, implementing
this method might not be necessary.) The dictionary passed in the second parameter of this method is the
notification payload; you should not use any custom properties it contains to alter your application’s current
context.
Even though the only supported notification type for nonrunning applications in OS X is icon-badging, the
delegate can implement application:didReceiveRemoteNotification: to examine the notification
payload for other types of notifications and handle them appropriately (that is, display an alert or play a sound).
Scheduling, Registering, and Handling Notifications
Handling Local and Remote Notifications
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
25iOS Note: If the user unlocks the device shortly after a remote-notification alert is displayed, the
operating system automatically triggers the action associated with the alert. (This behavior is
consistent with SMS and calendar alerts.) This makes it even more important that actions related to
remote notifications do not have destructive consequences. A user should always make decisions
that result in the destruction of data in the context of the application that stores the data.
Passing the Provider the Current Language Preference (Remote
Notifications)
If an application doesn’t use the loc-key and loc-args properties of the aps dictionary for client-side
fetching of localized alert messages, the provider needs to localize the text of alert messages it puts in the
notification payload. To do this, however, the provider needs to know the language that the device user has
selected as the preferred language. (The user sets this preference in the General > International > Language
view of the Settings application.) The client application should send its provider an identifier of the preferred
language; this could be a canonicalized IETF BCP 47 language identifier such as “en” or “fr”.
Note: For more information about the loc-key and loc-args properties and client-side message
localizations, see “The Notification Payload” (page 35).
Listing 2-7 illustrates a technique for obtaining the currently selected language and communicating it to the
provider. In iOS, the array returned by the preferredLanguages of NSLocale contains one object: an
NSString object encapsulating the language code identifying the preferred language. The UTF8String
coverts the string object to a C string encoded as UTF8.
Listing 2-7 Getting the current supported language and sending it to the provider
NSString *preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0];
const char *langStr = [preferredLang UTF8String];
[self sendProviderCurrentLanguage:langStr]; // custom method
}
The application might send its provider the preferred language every time the user changes something in the
current locale. To do this, you can listen for the notification named
NSCurrentLocaleDidChangeNotification and, in your notification-handling method, get the code
identifying the preferred language and send that to your provider.
Scheduling, Registering, and Handling Notifications
Passing the Provider the Current Language Preference (Remote Notifications)
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
26If the preferred language is not one the application supports, the provider should localize the message text in
a widely spoken fallback language such as English or Spanish.
Scheduling, Registering, and Handling Notifications
Passing the Provider the Current Language Preference (Remote Notifications)
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
27Apple Push Notification service (APNsforshort) isthe centerpiece of the push notificationsfeature. It is a robust
and highly efficientservice for propagating information to devicessuch asiPhone, iPad, and iPod touch devices.
Each device establishes an accredited and encrypted IP connection with the service and receives notifications
over this persistent connection. If a notification for an application arrives when that application is not running,
the device alerts the user that the application has data waiting for it.
Software developers (“providers”) originate the notifications in their server software. The provider connects
with APNs through a persistent and secure channel while monitoring incoming data intended for their client
applications. When new data for an application arrives, the provider prepares and sends a notification through
the channel to APNs, which pushes the notification to the target device.
In addition to being a simple but efficient and high-capacity transport service, APNs includes a default
quality-of-service component that provides store-and-forward capabilities. See “Quality of Service” (page 30)
for more information.
“Provider Communication with Apple Push Notification Service” (page 47) and “Scheduling, Registering, and
Handling Notifications” (page 15) discuss the specific implementation requirements for providers and iOS
applications, respectively.
A Push Notification and Its Path
Apple Push Notification service transports and routes a notification from a given provider to a given device.
A notification is a short message consisting of two major pieces of data: the device token and the payload. The
device token is analogous to a phone number; it contains information that enables APNs to locate the device
on which the client application is installed. APNs also uses it to authenticate the routing of a notification. The
payload is a JSON-defined property list thatspecifies how the user of an application on a device isto be alerted.
Note: For more information about the device token,see “Security Architecture” (page 30); for further
information about the notification payload, see “The Notification Payload” (page 35) .
The flow of remote-notification data is one-way. The provider composes a notification package that includes
the device token for a client application and the payload. The provider sends the notification to APNs which
in turn pushes the notification to the device.
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
28
Apple Push Notification ServiceWhen it authenticates itself to APNs, a provider furnishes the service with its topic, which identifies the
application for which it’s providing data. The topic is currently the bundle identifier of the target application
on an iOS device.
Figure 3-1 A push notification from a provider to a client application
Provider APNS notification notification
Client
App
iPhone
notification
Figure 3-1 is a greatly simplified depiction of the virtual network APNs makes possible among providers and
devices. The device-facing and provider-facing sides of APNs both have multiple points of connection; on the
provider-facing side, these are called gateways. There are typically multiple providers, each making one or
more persistent and secure connections with APNs through these gateways. And these providers are sending
notifications through APNs to many devices on which their client applications are installed. Figure 3-2 is a
slightly more realistic depiction.
Figure 3-2 Push notifications from multiple providers to multiple devices
APNS
Provider
B
Provider
A
Feedback Service
Sometimes APNs might attempt to deliver notifications for an application on a device, but the device may
repeatedly refuse delivery because there is no target application. This often happens when the user has
uninstalled the application. In these cases, APNs informs the provider through a feedback service that the
provider connects with. The feedback service maintains a list of devices per application for which there were
recent, repeated failed attempts to deliver notifications. The provider should obtain this list of devices and
stop sending notifications to them. For more on this service, see “The Feedback Service” (page 53).
Apple Push Notification Service
Feedback Service
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
29Quality of Service
Apple Push Notification Service includes a default Quality of Service (QoS) component that performs a
store-and-forward function. If APNs attempts to deliver a notification but the device is offline, the QoS stores
the notification. It retains only one notification per application on a device: the last notification received from
a provider for that application. When the offline device later reconnects, the QoS forwardsthe stored notification
to the device. The QoS retains a notification for a limited period before deleting it.
Security Architecture
To enable communication between a provider and a device, Apple Push Notification Service must expose
certain entry points to them. But then to ensure security, it must also regulate access to these entry points.
For this purpose, APNs requires two different levels of trust for providers, devices, and their communications.
These are known as connection trust and token trust.
Connection trust establishes certainty that, on one side, the APNs connection is with an authorized provider
with whom Apple has agreed to deliver notifications. At the device side of the connection, APNs must validate
that the connection is with a legitimate device.
After APNs has established trust at the entry points, it must then ensure that it conveys notificationsto legitimate
end points only. To do this, it must validate the routing of messages traveling through the transport; only the
device that is the intended target of a notification should receive it.
In APNs, assurance of accurate message routing—or token trust—is made possible through the device token.
A device token is an opaque identifier of a device that APNs gives to the device when it first connects with it.
The device shares the device token with its provider. Thereafter, this token accompanies each notification from
the provider. It is the basis for establishing trust that the routing of a particular notification is legitimate. (In a
metaphorical sense, it has the same function as a phone number, identifying the destination of a
communication.)
Note: A device token is not the same thing asthe device UDID returned by the uniqueIdentifier
property of UIDevice.
The following sections discuss the requisite components for connection trust and token trust as well as the
four procedures for establishing trust.
Apple Push Notification Service
Quality of Service
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
30Service-to-Device Connection Trust
APNs establishes the identity of a connecting device through TLS peer-to-peer authentication. (Note that iOS
takes care of this stage of connection trust; you do not need to implement anything yourself.) In the course
of this procedure, a device initiates a TLS connection with APNs, which returns its server certificate. The device
validates this certificate and then sends its device certificate to APNs, which validates that certificate.
TLS initiation
Device certificate
Server certificate
TLS established
Validate device certificate
Device APNS
Validate server certificate
Provider-to-Service Connection Trust
Connection trust between a provider and APNs is also established through TLS peer-to-peer authentication.
The procedure is similar to that described in “Service-to-Device Connection Trust” (page 31). The provider
initiates a TLS connection, getsthe server certificate from APNs, and validatesthat certificate. Then the provider
Apple Push Notification Service
Security Architecture
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
31sends its provider certificate to APNs, which validates it on its end. Once this procedure is complete, a secure
TLS connection has been established; APNsis now satisfied that the connection has been made by a legitimate
provider.
TLS initiation
Provider certificate
Server certificate
TLS established
Validate provider certificate
Provider APNS
Validate server certificate
Note that provider connection is valid for delivery to only one specific application, identified by the topic
(bundle ID) specified in the certificate. APNs also maintains a certificate revocation list; if a provider’s certificate
is on this list, APNs may revoke provider trust (that is, refuse the connection).
Token Generation and Dispersal
An iOS-based application must register to receive push notifications; it typically does this right after it is
installed on a device. (This procedure is described in “Scheduling, Registering, and Handling Notifications” (page
15).) iOS receives the registration request from an application, connects with APNs, and forwards the request.
Apple Push Notification Service
Security Architecture
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
32APNs generates a device token using information contained in the unique device certificate. The device token
contains an identifier of the device. It then encrypts the device token with a token key and returns it to the
device.
Token
Connect (Token, ...)
Token
Generate token package
Encrypt token with token key
Generate device ID from
device certificate
Provider Device APNS
The device returns the device token to the requesting application as an NSData object. The application then
must then deliver the device token to its provider in either binary or hexadecimal format. Figure 3-3 also
illustratesthe token generation and dispersalsequence, but in addition showsthe role of the client application
in furnishing its provider with the device token.
Figure 3-3 Sharing the device token
deviceToken
APNS
Provider
Client
App
2
1
3
4
SSL connect
deviceToken
deviceToken
The form of this phase of token trust ensures that only APNs generates the token which it will later honor, and
it can assure itself that a token handed to it by a device is the same token that it previously provisioned for
that particular device—and only for that device.
Apple Push Notification Service
Security Architecture
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
33Token Trust (Notification)
After iOS obtains a device token from APNs, as described in “Token Generation and Dispersal” (page 32), it
must provide APNs with the token every time it connects with it. APNs decrypts the device token and validates
that the token was generated for the connecting device. To validate, APNs ensures that the device identifier
contained in the token matches the device identifier in the device certificate.
Every notification that a provider sends to APNs for delivery to a device must be accompanied by the device
token it obtained from an application on that device. APNs decrypts the token using the token key, thereby
ensuring that the notification is valid. It then uses the device ID contained in the device token to determine
the destination device for the notification.
Token, Payload
Response (OK)
Payload
Connect (Token, ...)
Decrypt token and validate
with device certificate
Provider APNS Device
Decrypt token with
token key
Trust Components
To support the security model for APNs, providers and devices must possess certain certificates, certificate
authority (CA) certificates, or tokens.
● Provider: Each provider requires a unique provider certificate and private cryptographic key for validating
their connection with APNs. This certificate, provisioned by Apple, must identify the particular topic
published by the provider; the topic is the bundle ID of the client application. For each notification, the
provider must furnish APNs with a device token identifying the target device. The provider may optionally
wish to validate the service it is connecting to using the public server certificate provided by the APNs
server.
Apple Push Notification Service
Security Architecture
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
34● Device: iOS uses the public server certificate passed to it by APNs to authenticate the service that it has
connected to. It has a unique private key and certificate that it uses to authenticate itself to the service
and establish the TLS connection. It obtains the device certificate and key during device activation and
stores them in the keychain. iOS also holds its particular device token, which it receives during the service
connection process. Each registered client application isresponsible for delivering thistoken to its content
provider.
APNs servers also have the necessary certificates, CA certificates, and cryptographic keys (private and public)
for validating connections and the identities of providers and devices.
The Notification Payload
Each push notification carries with it a payload. The payload specifies how users are to be alerted to the data
waiting to be downloaded to the client application. The maximum size allowed for a notification payload is
256 bytes; Apple Push Notification Service refuses any notification that exceeds this limit. Remember that
delivery of notifications is “best effort” and is not guaranteed.
For each notification, providers must compose a JSON dictionary object that strictly adheres to RFC 4627. This
dictionary must contain another dictionary identified by the key aps. The aps dictionary contains one or more
properties that specify the following actions:
● An alert message to display to the user
● A number to badge the application icon with
● A sound to play
Note: Although you can combine an alert message, icon badging, and a sound in a single notification,
you should consider the human-interface implications with push notifications. For example, a user
might find frequent alert messages with accompanying sound more annoying than useful, especially
when the data to be downloaded is not critical.
If the target application isn’t running when the notification arrives, the alert message, sound, or badge value
is played orshown. If the application isrunning, iOS deliversit to the application delegate as an NSDictionary
object. The dictionary contains the corresponding Cocoa property-list objects (plus NSNull).
Providers can specify custom payload values outside the Apple-reserved aps namespace. Custom values must
use the JSON structured and primitive types: dictionary (object), array,string, number, and Boolean. You should
not include customer information as custom payload data. Instead, use it for such purposes as setting context
(for the user interface) or internal metrics. For example, a custom payload value might be a conversation
Apple Push Notification Service
The Notification Payload
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
35identifier for use by an instant-message client application or a timestamp identifying when the provider sent
the notification. Any action associated with an alert message should not be destructive—for example, deleting
data on the device.
Important: Because delivery is not guaranteed, you should not depend on the remote-notificationsfacility
for delivering critical data to an application via the payload. And never include sensitive data in the payload.
You should use it only to notify the user that new data is available.
Table 3-1 lists the keys and expected values of the aps payload.
Table 3-1 Keys and values of the aps dictionary
Key Value type Comment
If this property is included, iOS displays a standard alert. You may specify a
string asthe value of alert or a dictionary asits value. If you specify a string,
it becomes the message text of an alert with two buttons: Close and View.
If the user taps View, the application is launched.
Alternatively, you can specify a dictionary as the value of alert. See Table
3-2 (page 36) for descriptions of the keys of this dictionary.
string or
dictionary
alert
The number to display as the badge of the application icon. If this property
is absent, the badge is not changed. To remove the badge, set the value of
this property to 0.
badge number
The name of a sound file in the application bundle. The sound in this file is
played as an alert. If the sound file doesn’t exist or default is specified as
the value, the default alert sound is played. The audio must be in one of the
audio data formats that are compatible with system sounds; see “Preparing
Custom Alert Sounds” (page 15) for details.
sound string
Table 3-2 Child properties of the alert property
Value Comment
type
Key
body string The text of the alert message.
Apple Push Notification Service
The Notification Payload
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
36Value Comment
type
Key
If a string is specified, displays an alert with two buttons, whose
behavior is described in Table 3-1. However, iOS uses the string
as a key to get a localized string in the current localization to use
for the right button’s title instead of “View”. If the value is null,
the system displays an alert with a single OK button that simply
dismisses the alert when tapped. See “Localized Formatted
Strings” (page 37) for more information.
string or
null
action-loc-key
A key to an alert-message string in a Localizable.strings file
for the current localization (which is set by the user’s language
preference). The key string can be formatted with %@ and %n$@
specifiers to take the variables specified in loc-args. See
“Localized Formatted Strings” (page 37) for more information.
loc-key string
Variable string values to appear in place of the format specifiers
in loc-key. See “Localized Formatted Strings” (page 37) for more
information.
array of
strings
loc-args
The filename of an image file in the application bundle; it may
include the extension or omit it. The image is used as the launch
image when userstap the action button or move the action slider.
If this property is notspecified, the system either usesthe previous
snapshot,uses the image identified by the UILaunchImageFile
key in the application’s Info.plist file, or falls back to
Default.png.
This property was added in iOS 4.0.
launch-image string
Note: If you want the iPhone, iPad, or iPod touch device to display the message text as-is in an alert
that has both the Close and View buttons, then specify a string as the direct value of alert. Don’t
specify a dictionary as the value of alert if the dictionary only has the body property.
Localized Formatted Strings
You can display localized alert messages in two ways. The server originating the notification can localize the
text; to do this, it must discover the current language preference selected for the device (see “Passing the
Provider the Current Language Preference (Remote Notifications)” (page 26)). Or the client application can
store in its bundle the alert-message strings translated for each localization it supports. The provider specifies
the loc-key and loc-args properties in the aps dictionary of the notification payload. When the device
receives the notification (assuming the application isn’t running), it uses these aps-dictionary properties to
find and format the string localized for the current language, which it then displays to the user.
Apple Push Notification Service
The Notification Payload
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
37Here’s how that second option works in a little more detail.
An iOS application can internationalize resources such as images, sounds, and text for each language that it
supports, Internationalization collects the resources and puts them in a subdirectory of the bundle with a
two-part name: a language code and an extension of .lproj (for example, fr.lproj). Localized strings that
are programmatically displayed are put in a file called Localizable.strings. Each entry in this file has a
key and a localized string value; the string can have format specifiers for the substitution of variable values.
When an application asksfor a particular resource—say a localized string—it getsthe resource that islocalized
for the language currently selected by the user. For example, if the preferred language is French, the
corresponding string value for an alert message would be fetched from Localizable.strings in the
fr.lproj directory in the application bundle. (iOS makes this request through the NSLocalizedString
macro.)
Note: This general pattern is also followed when the value of the action-loc-key property is a
string. This string is a key into the Localizable.strings in the localization directory for the
currently selected language. iOS uses this key to get the title of the button on the right side of an
alert message (the “action” button).
To make this clearer, let’s consider an example. The provider specifies the following dictionary as the value of
the alert property:
"alert" : { "loc-key" : "GAME_PLAY_REQUEST_FORMAT", "loc-args" : [ "Jenna", "Frank"]
},
When the device receives the notification, it uses "GAME_PLAY_REQUEST_FORMAT" as a key to look up the
associated string value in the Localizable.strings file in the .lproj directory for the current language.
Assuming the current localization has an Localizable.strings entry such as this:
"GAME_PLAY_REQUEST_FORMAT" = "%@ and %@ have invited you to play Monopoly";
the device displays an alert with the message “Jenna and Frank have invited you to play Monopoly”.
In addition to the format specifier %@, you can %n$@ format specifiers for positional substitution of string
variables. The n is the index (starting with 1) of the array value in loc-args to substitute. (There’s also the %%
specifier for expressing a percentage sign (%).) So if the entry in Localizable.strings is this:
"GAME_PLAY_REQUEST_FORMAT" = "%2$@ and %1$@ have invited you to play Monopoly";
Apple Push Notification Service
The Notification Payload
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
38the device displays an alert with the message "Frank and Jenna have invited you to play Monopoly".
For a full example of a notification payload that usesthe loc-key and loc-arg properties,see the last example
of “Examples of JSON Payloads.” To learn more about internationalization in iOS, see ““Advanced App Tricks”” in
iOS App Programming Guide ; for general information about internationalization, see Internationalization
Programming Topics. String formatting is discussed in “Formatting String Objects” in String Programming Guide .
Note: You should use the loc-key and loc-args properties—and the alert dictionary in
general—only if you absolutely need to. The values of these properties, especially if they are long
strings, might use up more bandwidth than is good for performance. Many if not most applications
may not need these properties because their message strings are originated by users and thus are
implicitly "localized."
Examples of JSON Payloads
The following examples of the payload portion of notifications illustrate the practical use of the properties
listed in Table 3-1. Properties with “acme” in the key name are examples of custom payload data. The examples
include whitespace and newline characters for readability; for better performance, providers should omit
whitespace and newline characters.
Example 1: The following payload has an aps dictionary with a simple, recommended form for alert messages
with the default alert buttons (Close and View). It uses a string as the value of alert rather than a dictionary.
This payload also has a custom array property.
{
"aps" : { "alert" : "Message received from Bob" },
"acme2" : [ "bang", "whiz" ]
}
Example 2. The payload in the example uses an aps dictionary to request that the device display an alert
message with an Close button on the left and a localized title for the "action" button on the right side of the
alert. In this case, “PLAY” is used as a key into the Localizable.strings file for the currently selected
language to get the localized equivalent of “Play”. The aps dictionary also requests that the application icon
be badged with 5.
{ "aps" : { "alert" : { "body" : "Bob wants to play poker",
"action-loc-key" : "PLAY" }, "badge" : 5, }, "acme1" : "bar",
"acme2" : [ "bang", "whiz" ] }
Apple Push Notification Service
The Notification Payload
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
39Example 3. The payload in this example specifies that device should display an alert message with both Close
and View buttons. It also request that the application icon be badged with 9 and that a bundled alert sound
be played when the notification is delivered.
{
"aps" : {
"alert" : "You got your emails.",
"badge" : 9,
"sound" : "bingbong.aiff"
},
"acme1" : "bar",
"acme2" : 42
}
Example 4. The interesting thing about the payload in this example is that it uses the loc-key and loc-args
child properties of the alert dictionary to fetch a formatted localized string from the application’s bundle
and substitute the variable string values(loc-args) in the appropriate places. It also specifies a custom sound
and include a custom property.
{
"aps" : {
"alert" : { "loc-key" : "GAME_PLAY_REQUEST_FORMAT", "loc-args" : [ "Jenna",
"Frank"] },
"sound" : "chime"
},
"acme" : "foo"
}
Example 5. The following example shows an empty aps dictionary; because the badge property is missing,
any current badge number shown on the application icon is removed. The acme2 custom property is an array
of two integers.
{
"aps" : {
},
"acme2" : [ 5, 8 ]
Apple Push Notification Service
The Notification Payload
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
40}
Remember, for better performance, you should strip all whitespace and newline characters from the payload
before including it in the notification.
Apple Push Notification Service
The Notification Payload
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
41Sandbox and Production Environments
To develop and deploy the provider side of a client/server application, you must get SSL certificates from the
appropriate Dev Center. Each certificate is limited to a single application, identified by its bundle ID. Each
certificate is also limited to one of two development environments, each with its own assigned IP address:
● Sandbox: The sandbox environment is used for initial development and testing of the provider application.
It provides the same set of services as the production environment, although with a smaller number of
server units. The sandbox environment also acts a virtual device, enabling simulated end-to-end testing.
You accessthe sandbox environment at gateway.sandbox.push.apple.com, outbound TCP port 2195.
● Production: Use the production environment when building the production version of the provider
application. Applications using the production environment must meet Apple’s reliability requirements.
You access the production environment at gateway.push.apple.com, outbound TCP port 2195.
You must getseparate certificatesfor the sandbox (development) environment and the production environment.
The certificates are associated with an identifier of the application that is the recipient of push notifications;
this identifier includes the application’s bundle ID. When you create a provisioning profile for one of the
environments, the requisite entitlements are automatically added to the profile, including the entitlement
specific to push notifications, . The two provisioning profiles are called Development
and Distribution. The Distribution provisioning profile is a requirement for submitting your application to the
App Store.
OS X Note: The entitlement for the OS X provisioning profile is
com.apple.developer.aps-environment, which scopes it to the platform.
You can determine in Xcode which environment you are in by the selection of a code-signing identity. If you
see an “iPhone Developer: Firstname Lastname ” certificate/provisioning profile pair, you are in the sandbox
environment. If you see an “iPhone Distribution: Companyname ” certificate/provisioning profile pair, you are
in the production environment. It is a good idea to create a Distribution release configuration in Xcode to help
you further differentiate the environments.
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
42
Provisioning and DevelopmentAlthough an SSL certificate is not put into a provisioning profile, the is added to the
profile because of the association of the certificate and a particular application ID. As a result this entitlement
is built into the application, which enables it to receive push notifications.
Provisioning Procedures
In the iOS Developer Program, each member on a development team has one of three roles: team agent, team
admin, and team member. The roles differ in relation to iPhone development certificates and provisioning
profiles. The team agent isthe only person on the team who can create Development (Sandbox) SSL certificates
and Distribution (Production) SSL certificates. The team admin and the team agent can both create both
Development and Distribution provisioning profiles. Team members may only download and install certificates
and provisioning profiles. The procedures in the following sections make reference to these roles.
Note: The iOS Provisioning Portal makes available to all iOS Developer Program members a user
guide and a series of videos that explain all aspects of certificate creation and provisioning. The
following sections focus on APNs-specific aspects of the process and summarize other aspects. To
access the portal, iOS Developer Program members should go to the iOS Dev Center (http://developer.apple.com/devcenter/ios), log in, and click then go to the iOS Provisioning Portal page (there’s
a link in the upper right).
Creating the SSL Certificate and Keys
In the provisioning portal of the iOS Dev Center, the team agent selects the application IDs for APNs. He also
completes the following steps to create the SSL certificate:
1. Click App IDs in the sidebar on the left side of the window.
The next page displays your valid application IDs. An application ID consists of an application’s bundle ID
prefixed with a ten-character code generated by Apple. The team admin must enter the bundle ID. For a
certificate, it must incorporate a specific bundle ID; you cannot use a “wildcard” application ID.
2. Locate the application ID for the sandbox SSL certificate (and that is associated with the Development
provisioning profile) and click Configure.
You must see “Available” under the Apple Push Notification Service column to configure a certificate for
this application ID.
3. In the Configure App ID page, check the Enable Push Notification Services box and click the Configure
button.
Clicking this button launches an APNs Assistant, which guides you through the next series of steps.
Provisioning and Development
Provisioning Procedures
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
434. The first step requires that you launch the Keychain Access application and generate a Certificate Signing
Request (CSR).
Follow the instructions presented in the assistant. When you are finished generating a CSR, click Continue
in Keychain Access to return to the APNs Assistant.
When you create a CSR, Keychain Access generates a private and a public cryptographic key pair. The
private key is put into your Login keychain by default. The public key is included in the CSR sent to the
provisioning authority. When the provisioning authority sendsthe certificate back to you, one of the items
in that certificate is the public key.
5. In the Submit Certificate Signing Request pane, click Choose File. Navigate to the CSR file you created in
the previous step and select it.
6. Click the Generate button.
While displaying the Generate Your Certificate pane, the Assistant configures and generates your Client
SSL Certificate. If it succeeds, it displays the message “Your APNs Certificate has been generated.” Click
Continue to proceed to the next step.
7. In the next pane, click the Download Now button to download the certificate file to your download location.
Navigate to that location and double-click the certificate file (which has an extension of cer) to install it
in your keychain. When you are finished, click Done in the APNs Assistant.
Double-clicking the file launches Keychain Access. Make sure you install the certificate in your login keychain
on the computer you are using for provider development. In Keychain Access, ensure that your certificate
user ID matches your application’s bundle ID. The APNs SSL certificate should be installed on your
notification server.
When you finish these steps you are returned to the Configure App ID page of the iOS Dev Center portal. The
certificate should be badged with a green circle and the label “Enabled”.
To create a certificate for the production environment, repeat the same procedure but choose the application
ID for the production certificate.
Creating and Installing the Provisioning Profile
The Team Admin or Team Agent must next create the provisioning profile (Development or Distribution) used
in the server side of remote-notification development. The provisioning profile is a collection of assets that
associates developers of an application and their devices with an authorized development team and enables
those devicesto be used for testing. The profile contains certificates, device identifiers, the application’s bundle
ID, and all entitlements, including . All team members must install the provisioning
profile on the devices on which they will run and test application code.
Provisioning and Development
Provisioning Procedures
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
44Note: Refer to the program user guide for the details of creating a provisioning profile.
To download and install the provisioning profile, team members should complete the following steps:
1. Go to the provisioning portal in the iOS Dev Center.
2. Create a new provisioning profile that contains the App ID you registered for APNs.
3. Modify any existing profile before you download the new one.
You have to modify the profile in some minor way (for example, toggle an option) for the portal to generate
a new provisioning profile. If the profile isn't so “dirtied,” you're given the original profile without the push
entitlements.
4. From the download location, drag the profile file (which has an extension of mobileprovision) onto
the Xcode or iTunes application icons.
Alternatively, you can move the profile file to ~/Library/MobileDevice/Provisioning Profiles.
Create the directory if it does not exist.
5. Verify that the entitlements in the provisioning-profile file are correct. To do this, open the
.mobileprovision file in a text editor. The contents of the file are structured in XML. In the Entitlements
dictionary locate the aps-environment key. For a development provisioning profile, the string value of
this key should be development; for a distribution provisioning profile, the string value should be
production.
6. In the Xcode Organizer window, go the Provisioning Profiles section and install the profile on your device.
When you build the project, the binary is now signed by the certificate using the private key.
Installing the SSL Certificate and Key on the Server
You should install the SSL distribution certificate and private cryptographic key you obtained earlier on the
server computer on which the provider code runs and from which it connects with the sandbox or production
versions of APNs. To do so, complete the following steps:
1. Open Keychain Access utility and click the My Certificates category in the left pane.
2. Find the certificate you want to install and disclose its contents.
You'll see both a certificate and a private key.
3. Select both the certificate and key, choose File > Export Items, and export them as a Personal Information
Exchange (.p12) file.
4. Servers implemented in languages such as Ruby and Perl often are better able to deal with certificates in
the Personal Information Exchange format. To convert the certificate to thisformat, complete the following
steps:
Provisioning and Development
Provisioning Procedures
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
45a. In KeyChain Access,select the certificate and choose File > Export Items. Select the Personal Information
Exchange (.p12) option, select a save location, and click Save.
b. Launch the Terminal application and enter the following command after the prompt:
openssl pkcs12 -in CertificateName.p12 -out CertificateName.pem -nodes
5. Copy the .pem certificate to the new computer and install it in the appropriate place.
Provisioning and Development
Provisioning Procedures
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
46This chapter describesthe interfacesthat providers use for communication with Apple Push Notification service
(APNs) and discusses some of the functions that providers are expected to fulfill.
General Provider Requirements
As a provider you communicate with Apple Push Notification service over a binary interface. This interface is
a high-speed, high-capacity interface for providers; it uses a streaming TCP socket design in conjunction with
binary content. The binary interface is asynchronous.
The binary interface of the production environment is available through gateway.push.apple.com, port
2195; the binary interface of the sandbox (development) environment is available through
gateway.sandbox.push.apple.com, port 2195. You may establish multiple, parallel connections to the
same gateway or to multiple gateway instances.
For each interface you should use TLS (or SSL) to establish a secured communications channel. The SSL certificate
required for these connections is provisioned through the iOS Provisioning Portal. (See “Provisioning and
Development” (page 42) for details.) To establish a trusted provider identity, you should present this certificate
to APNs at connection time using peer-to-peer authentication.
Note: To establish a TLS session with APNs, an Entrust Secure CA root certificate must be installed
on the provider’s server. If the server is running OS X, this root certificate is already in the keychain.
On other systems, the certificate might not be available. You can download this certificate from the
Entrust SSL Certificates website.
You should also retain connections with APNs across multiple notifications. APNs may consider connections
that are rapidly and repeatedly established and torn down as a denial-of-service attack. Upon error, APNs closes
the connection on which the error occurred.
As a provider, you are responsible for the following aspects of push notifications:
● You must compose the notification payload (see “The Notification Payload” (page 35)).
● You are responsible for supplying the badge number to be displayed on the application icon.
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
47
Provider Communication with Apple Push
Notification Service● You should regularly connect with the feedback web server and fetch the current list of those devices that
have repeatedly reported failed-delivery attempts. Then you should cease sending notifications to the
devices associated with those applications. See “The Feedback Service” (page 53) for more information.
If you intend to support notification messagesin multiple languages, but do not use the loc-key and loc-args
properties of the aps payload dictionary for client-side fetching of localized alert strings, you need to localize
the text of alert messages on the server side. To do this, you need to find out the current language preference
from the client application. “Scheduling, Registering, and Handling Notifications” (page 15) suggests an
approach for obtaining this information. See “The Notification Payload” (page 35) for information about the
loc-key and loc-args properties.
The Binary Interface and Notification Formats
The binary interface employs a plain TCP socket for binary content that is streaming in nature. For optimum
performance, you should batch multiple notificationsin a single transmission over the interface, either explicitly
or using a TCP/IP Nagle algorithm.
The interface supports two formats for notification packets, a simple format and an enhanced format that
addresses some of the issues with the simple format:
● Notification expiry. APNs has a store-and-forward feature that keeps the most recent notification sent to
an application on a device. If the device is offline at time of delivery, APNs delivers the notification when
the device next comes online. With the simple format, the notification is delivered regardless of the
pertinence of the notification. In other words, the notification can become “stale” over time. The enhanced
format includes an expiry value that indicates the period of validity for a notification. APNs discards a
notification in store-and-forward when this period expires.
● Error response. With the simple format, if you send a notification packet that is malformed in some
way—for example, the payload exceeds the stipulated limit—APNs responds by severing the connection.
It gives no indication why it rejected the notification. The enhanced format lets a provider tag a notification
with an arbitrary identifier. If there is an error, APNs returns a packet that associates an error code with
the identifier. This response enables the provider to locate and correct the malformed notification.
The enhanced format is recommended for most providers.
Provider Communication with Apple Push Notification Service
The Binary Interface and Notification Formats
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
48Let’s examine the simple notification format first because much of this format is shared with the enhanced
format. Figure 5-1 illustrates this format.
Figure 5-1 Simple notification format
0 0 32 deviceToken (binary) 0 34 {"aps":{"alert":"You have mail!"}}
Bytes: 1 2 32 2
Command
Token length Payload length
(big endian) (big endian)
34
The first byte in the simple format is a command value of 0 (zero). The lengths of the device token and the
payload must be in network order (that is, big endian). In addition, you should encode the device token in
binary format. The payload must not exceed 256 bytes and must not be null-terminated.
Listing 5-1 gives an example of a function that sends a push notification to APNs over the binary interface
using the simple notification format. The example assumes prior SSL connection to gateway.push.apple.com
(or gateway.sandbox.push.apple.com) and peer-exchange authentication.
Listing 5-1 Sending a notification in the simple format via the binary interface
static bool sendPayload(SSL *sslPtr, char *deviceTokenBinary, char *payloadBuff,
size_t payloadLength)
{
bool rtn = false;
if (sslPtr && deviceTokenBinary && payloadBuff && payloadLength)
{
uint8_t command = 0; /* command number */
char binaryMessageBuff[sizeof(uint8_t) + sizeof(uint16_t) +
DEVICE_BINARY_SIZE + sizeof(uint16_t) + MAXPAYLOAD_SIZE];
/* message format is, |COMMAND|TOKENLEN|TOKEN|PAYLOADLEN|PAYLOAD| */
char *binaryMessagePt = binaryMessageBuff;
uint16_t networkOrderTokenLength = htons(DEVICE_BINARY_SIZE);
uint16_t networkOrderPayloadLength = htons(payloadLength);
/* command */
*binaryMessagePt++ = command;
/* token length network order */
Provider Communication with Apple Push Notification Service
The Binary Interface and Notification Formats
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
49memcpy(binaryMessagePt, &networkOrderTokenLength, sizeof(uint16_t));
binaryMessagePt += sizeof(uint16_t);
/* device token */
memcpy(binaryMessagePt, deviceTokenBinary, DEVICE_BINARY_SIZE);
binaryMessagePt += DEVICE_BINARY_SIZE;
/* payload length network order */
memcpy(binaryMessagePt, &networkOrderPayloadLength, sizeof(uint16_t));
binaryMessagePt += sizeof(uint16_t);
/* payload */
memcpy(binaryMessagePt, payloadBuff, payloadLength);
binaryMessagePt += payloadLength;
if (SSL_write(sslPtr, binaryMessageBuff, (binaryMessagePt -
binaryMessageBuff)) > 0)
rtn = true;
}
return rtn;
}
Figure 5-2 depicts the enhanced format for notification packets. With this format, if APNs encounters an
unintelligible command, it returns an error response before disconnecting.
Figure 5-2 Enhanced notification format
1 Identifier
Bytes: 1 4
Expiry
4
Command
32 deviceToken (binary) 0 34 {"aps":{"alert":"You have mail!"}}
2 32 2
Token length Payload length
(big endian) (big endian)
34
0
The first byte in the enhanced notification format is a command value of 1. The two new fields in this format
are for an identifier and an expiry value. (Everything else is the same as the simple notification format.)
●
Identifier—An arbitrary value that identifies this notification. This same identifier is returned in a
error-response packet if APNs cannot interpret a notification.
Provider Communication with Apple Push Notification Service
The Binary Interface and Notification Formats
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
50● Expiry—A fixed UNIX epoch date expressed in seconds (UTC) that identifies when the notification is no
longer valid and can be discarded. The expiry value should be in network order (big endian). If the expiry
value is positive, APNs tries to deliver the notification at least once. You can specify zero or a value less
than zero to request that APNs not store the notification at all.
If you send a notification and APNs finds the notification malformed or otherwise unintelligible, it returns an
error-response packet prior to disconnecting. (If there is no error, APNs doesn’t return anything.) Figure 5-3
depicts the format of the error-response packet.
Figure 5-3 Format of error-response packet
8
Bytes: 1
Command Status
n Identifier
1 4
The packet has a command value of 8 followed by a one-byte status code and the same notification identifier
specified by the provider when it composed the notification. Table 5-1 lists the possible status codes and their
meanings.
Table 5-1 Codes in error-response packet
Status code Description
0 No errors encountered
1 Processing error
2 Missing device token
3 Missing topic
4 Missing payload
5 Invalid token size
6 Invalid topic size
7 Invalid payload size
8 Invalid token
255 None (unknown)
Provider Communication with Apple Push Notification Service
The Binary Interface and Notification Formats
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
51Listing 5-2 modifies the code in Listing 5-1 (page 49) to compose a push notification in the enhanced format
before sending it to APNs. As with the earlier example, it assumes prior SSL connection to
gateway.push.apple.com (or gateway.sandbox.push.apple.com) and peer-exchange authentication.
Listing 5-2 Sending a notification in the enhanced format via the binary interface
static bool sendPayload(SSL *sslPtr, char *deviceTokenBinary, char *payloadBuff,
size_t payloadLength)
{
bool rtn = false;
if (sslPtr && deviceTokenBinary && payloadBuff && payloadLength)
{
uint8_t command = 1; /* command number */
char binaryMessageBuff[sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t)
+ sizeof(uint16_t) +
DEVICE_BINARY_SIZE + sizeof(uint16_t) + MAXPAYLOAD_SIZE];
/* message format is, |COMMAND|ID|EXPIRY|TOKENLEN|TOKEN|PAYLOADLEN|PAYLOAD|
*/
char *binaryMessagePt = binaryMessageBuff;
uint32_t whicheverOrderIWantToGetBackInAErrorResponse_ID = 1234;
uint32_t networkOrderExpiryEpochUTC = htonl(time(NULL)+86400); // expire
message if not delivered in 1 day
uint16_t networkOrderTokenLength = htons(DEVICE_BINARY_SIZE);
uint16_t networkOrderPayloadLength = htons(payloadLength);
/* command */
*binaryMessagePt++ = command;
/* provider preference ordered ID */
memcpy(binaryMessagePt, &whicheverOrderIWantToGetBackInAErrorResponse_ID,
sizeof(uint32_t));
binaryMessagePt += sizeof(uint32_t);
/* expiry date network order */
memcpy(binaryMessagePt, &networkOrderExpiryEpochUTC, sizeof(uint32_t));
binaryMessagePt += sizeof(uint32_t);
Provider Communication with Apple Push Notification Service
The Binary Interface and Notification Formats
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
52/* token length network order */
memcpy(binaryMessagePt, &networkOrderTokenLength, sizeof(uint16_t));
binaryMessagePt += sizeof(uint16_t);
/* device token */
memcpy(binaryMessagePt, deviceTokenBinary, DEVICE_BINARY_SIZE);
binaryMessagePt += DEVICE_BINARY_SIZE;
/* payload length network order */
memcpy(binaryMessagePt, &networkOrderPayloadLength, sizeof(uint16_t));
binaryMessagePt += sizeof(uint16_t);
/* payload */
memcpy(binaryMessagePt, payloadBuff, payloadLength);
binaryMessagePt += payloadLength;
if (SSL_write(sslPtr, binaryMessageBuff, (binaryMessagePt - binaryMessageBuff))
> 0)
rtn = true;
}
return rtn;
}
Take note that the device token in the production environment and the device token in the development
(sandbox) environment are not the same value.
The Feedback Service
If a provider attempts to deliver a push notification to an application, but the application no longer exists on
the device, the device reports that fact to Apple Push Notification Service. This situation often happens when
the user has uninstalled the application. If a device reports failed-delivery attempts for an application, APNs
needs some way to inform the provider so that it can refrain from sending notifications to that device. Doing
this reduces unnecessary message overhead and improves overall system performance.
For this purpose Apple Push Notification Service includes a feedback service that APNs continually updates
with a per-application list of devices for which there were failed-delivery attempts. The devices are identified
by device tokens encoded in binary format. Providers should periodically query the feedback service to get
Provider Communication with Apple Push Notification Service
The Feedback Service
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
53the list of device tokens for their applications, each of which is identified by its topic. Then, after verifying that
the application hasn’t recently been re-registered on the identified devices, a provider should stop sending
notifications to these devices.
Access to the feedback service takes place through a binary interface similar to that used for sending push
notifications. You access the production feedback service via feedback.push.apple.com, port 2196; you
access the sandbox feedback service via feedback.sandbox.push.apple.com, port 2196. As with the
binary interface for push notifications, you must use TLS (or SSL) to establish a secured communications channel.
The SSL certificate required for these connections is the same one that is provisioned for sending notifications.
To establish a trusted provider identity, you should present this certificate to APNs at connection time using
peer-to-peer authentication.
Once you are connected, transmission begins immediately; you do not need to send any command to APNs.
Begin reading the stream written by the feedback service until there is no more data to read. The received
data is in tuples having the following format:
Figure 5-4 Binary format of a feedback tuple
n n n n 0 32 deviceToken (binary)
Bytes: 4 2 32
Token length
time_t
(big endian) (big endian)
A timestamp (as a four-byte time_t value) indicating when the APNs determined
that the application no longer exists on the device. This value, which is in network
order, represents the seconds since 1970, anchored to UTC.
You should use the timestamp to determine if the application on the device
re-registered with your service since the moment the device token was recorded on
the feedback service. If it hasn’t, you should cease sending push notifications to the
device.
Timestamp
Token length The length of the device token as a two-byte integer value in network order.
Device token The device token in binary format.
Note: APNs monitors providers for their diligence in checking the feedback service and refraining
from sending push notifications to nonexistent applications on devices.
Provider Communication with Apple Push Notification Service
The Feedback Service
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
54This table describes the changes to Local and Push Notification Programming Guide .
Date Notes
Added information about implementing push notifications on an OS X
desktop client. Unified the guide for iOS and OS X.
2011-08-09
Describes how to determine if an application is launched because the
user tapped the notification alert's action button.
2010-08-03
2010-07-08 Changed occurrences of "iPhone OS" to "iOS."
Updated and reorganized to describe local notifications, a feature
introduced in iOS 4.0. Also describes a new format for push notifications
sent to APNs.
2010-05-27
2010-01-28 Made many small corrections.
Made minor corrections and linked to short inline articles on Cocoa
concepts.
2009-08-14
Added notes about Wi-Fi and frequency of registration, and gateway
address for sandbox. Updated with various clarifications and
enhancements.
2009-05-22
First version of a document that explains how providers can send push
notifications to client applications using Apple Push Notification Service.
2009-03-15
2011-08-09 | © 2011 Apple Inc. All Rights Reserved.
55
Document Revision HistoryApple Inc.
© 2011 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, Cocoa, iPad, iPhone, iPod,
iPod touch, iTunes, Keychain, Mac, OS X,
QuickTime, Sand, and Xcode are trademarks of
Apple Inc., registered in the U.S. and other
countries.
App Store is a service mark of Apple Inc.
Times is a registered trademark of Heidelberger
Druckmaschinen AG, available from Linotype
Library GmbH.
UNIX is a registered trademark of The Open
Group.
iOS is a trademark or registered trademark of
Cisco in the U.S. and other countries and is used
under license.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
Mac App Programming
GuideContents
About OS X App Design 7
At a Glance 7
Cocoa Helps You Create Great Apps for OS X 7
Common Behaviors Make Apps Complete 8
Get It Right: Meet System and App Store Requirements 8
Finish Your App with Performance Tuning 8
How to Use This Document 9
See Also 9
The Mac Application Environment 10
An Environment Designed for Ease of Use 10
A Sophisticated Graphics Environment 11
Low-Level Details of the Runtime Environment 12
Based on UNIX 12
Concurrency and Threading 12
The File System 13
Security 18
The Core App Design 21
Fundamental Design Patterns 21
The App Style Determines the Core Architecture 23
The Core Objects for All Cocoa Apps 26
Additional Core Objects for Multiwindow Apps 29
Integrating iCloud Support Into Your App 30
Shoebox-Style Apps Should Not Use NSDocument 31
Document-Based Apps Are Based on an NSDocument Subclass 31
Documents in OS X 31
The Document Architecture Provides Many Capabilities for Free 32
The App Life Cycle 33
The main Function is the App Entry Point 33
The App’s Main Event Loop Drives Interactions 34
Automatic and Sudden Termination of Apps Improve the User Experience 36
Support the Key Runtime Behaviors in Your Apps 36
Automatic Termination 37
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
2Sudden Termination 38
User Interface Preservation 39
Apps Are Built Using Many Different Pieces 43
The User Interface 44
Event Handling 45
Graphics, Drawing, and Printing 46
Text Handling 47
Implementing the Application Menu Bar 47
Xcode Templates Provide the Menu Bar 48
Connect Menu Items to Your Code or Your First Responder 48
Implementing the Full-Screen Experience 49
Full-Screen API in NSApplication 49
Full-Screen API in NSWindow 50
Full-Screen API in NSWindowDelegate Protocol 50
Supporting Common App Behaviors 53
You Can Prevent the Automatic Relaunch of Your App 53
Making Your App Accessible Enables Many Users 53
Provide User Preferences for Customization 56
Integrate Your App With Spotlight Search 57
Use Services to Increase Your App’s Usefulness 58
Optimize for High Resolution 58
Think About Points, Not Pixels 58
Provide High-Resolution Versions of Graphics 59
Use High-Resolution-Savvy Image-Loading Methods 60
Use APIs That Support High Resolution 60
Prepare for Fast User Switching 61
Take Advantage of the Dock 62
Build-Time Configuration Details 63
Configuring Your Xcode Project 63
The Information Property List File 64
The OS X Application Bundle 66
Internationalizing Your App 69
Tuning for Performance and Responsiveness 71
Speed Up Your App’s Launch Time 71
Delay Initialization Code 71
Simplify Your Main Nib File 72
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
3
ContentsMinimize Global Variables 72
Minimize File Access at Launch Time 73
Don’t Block the Main Thread 73
Decrease Your App’s Code Size 73
Compiler-Level Optimizations 74
Use Core Data for Large Data Sets 75
Eliminate Memory Leaks 75
Dead Strip Your Code 75
Strip Symbol Information 76
Document Revision History 77
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
4
ContentsFigures, Tables, and Listings
The Mac Application Environment 10
Table 1-1 Key directories for Mac apps 14
Table 1-2 Attributes for the OS X file system 17
Listing 1-1 Getting the path to the Application Support directory 16
The Core App Design 21
Figure 2-1 The Calculator single-window utility app 24
Figure 2-2 The iPhoto single-window app 25
Figure 2-3 TextEdit document window 26
Figure 2-4 Key objects in a single-window app 27
Figure 2-5 Key objects in a multiwindow document app 29
Figure 2-6 Document file, object, and data model 32
Figure 2-7 The main event loop 35
Figure 2-8 Responder objects targeted by Cocoa for preservation 40
Figure 2-9 Windows and menus in an app 44
Figure 2-10 Processing events in the main run loop 45
Table 2-1 Fundamental design patterns used by Mac apps 21
Table 2-2 The core objects used by all Cocoa apps 27
Table 2-3 Additional objects used by multiwindow document apps 30
Listing 2-1 The main function of a Mac app 34
Listing 2-2 Returning the main window for a single-window app 43
Implementing the Full-Screen Experience 49
Table 3-1 Window delegate methods supporting full-screen mode 51
Supporting Common App Behaviors 53
Figure 4-1 Universal Access system preference dialog 55
Figure 4-2 Spotlight extracting metadata 57
Figure 4-3 Content appears the same size at standard resolution and high resolution 59
Build-Time Configuration Details 63
Figure 5-1 The information property list editor 65
Figure 5-2 The Language preference view 70
Table 5-1 A typical application bundle 66
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
5Tuning for Performance and Responsiveness 71
Table 6-1 Compiler optimization options 74
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
6
Figures, Tables, and ListingsThis document is the starting point for learning how to create Mac apps. It contains fundamental information
about the OS X environment and how your apps interact with that environment. It also contains important
information about the architecture of Mac apps and tips for designing key parts of your app.
At a Glance
Cocoa is the application environment that unlocks the full power of OS X. Cocoa provides APIs, libraries, and
runtimes that help you create fast, exciting apps that automatically inherit the beautiful look and feel of OS X,
as well as standard behaviors users expect.
Cocoa Helps You Create Great Apps for OS X
You write apps for OS X using Cocoa, which provides a significant amount of infrastructure for your program.
Fundamental design patterns are used throughout Cocoa to enable your app to interface seamlessly with
subsystem frameworks, and core application objects provide key behaviorsto supportsimplicity and extensibility
in app architecture. Key parts of the Cocoa environment are designed particularly to support ease of use, one
of the most important aspects of successful Mac apps. Many apps should adopt iCloud to provide a more
coherent user experience by eliminating the need to synchronize data explicitly between devices.
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
7
About OS X App DesignRelevant Chapters: “The Mac Application Environment” (page 10), “The Core App Design” (page
21), and “Integrating iCloud Support Into Your App” (page 30)
Common Behaviors Make Apps Complete
During the design phase of creating your app, you need to think about how to implement certain features
that users expect in well-formed Mac apps. Integrating these features into your app architecture can have an
impact on the user experience: accessibility, preferences, Spotlight, services, resolution independence, fast
user switching, and the Dock. Enabling your app to assume full-screen mode, taking over the entire screen,
provides users with a more immersive, cinematic experience and enables them to concentrate fully on their
content without distractions.
Relevant Chapters: “Supporting Common App Behaviors” (page 53) and “Implementing the
Full-Screen Experience” (page 49)
Get It Right: Meet System and App Store Requirements
Configuring your app properly is an important part of the development process. Mac apps use a structured
directory called a bundle to manage their code and resource files. And although most of the files are custom
and exist to support your app, some are required by the system or the App Store and must be configured
properly. The application bundle also contains the resources you need to provide to internationalize your app
to support multiple languages.
Relevant Chapter: “Build-Time Configuration Details” (page 63)
Finish Your App with Performance Tuning
As you develop your app and your project code stabilizes, you can begin performance tuning. Of course, you
want your app to launch and respond to the user’s commands as quickly as possible. A responsive app fits
easily into the user’s workflow and gives an impression of being well crafted. You can improve the performance
of your app by speeding up launch time and decreasing your app’s code footprint.
About OS X App Design
At a Glance
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
8Relevant Chapter: “Tuning for Performance and Responsiveness” (page 71)
How to Use This Document
This guide introduces you to the most important technologies that go into writing an app. In this guide you
will see the whole landscape of what's needed to write one. That is, this guide shows you all the "pieces" you
need and how they fit together. There are important aspects of app design that this guide does not cover,
such as user interface design. However, this guide includes many links to other documents that provide details
about the technologies it introduces, as well as links to tutorials that provide a hands-on approach.
In addition, this guide emphasizes certain technologies introduced in OS X v10.7, which provide essential
capabilities that set your app apart from older ones and give it remarkable ease of use, bringing some of the
best features from iOS to OS X.
See Also
The following documents provide additional information about designing Mac apps, as well as more details
about topics covered in this document:
● To work through a tutorial showing you how to create a Cocoa app, see Start Developing Mac Apps Today .
● For information about user interface design enabling you to create effective apps using OS X, see OS X
Human Interface Guidelines.
● To understand how to create an explicit app ID, create provisioning profiles, and enable the correct
entitlementsfor your application,so you can sell your application through the Mac App Store or use iCloud
storage, see Tools Workflow Guide for Mac .
● For information about the design patterns used in Cocoa, see Cocoa Fundamentals Guide .
● For a general survey of OS X technologies, see Mac Technology Overview.
● To understand how to implement a document-based app, see Document-Based App Programming Guide
for Mac .
About OS X App Design
How to Use This Document
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
9OS X incorporates the latest technologies for creating powerful and fun-to-use apps. But the technologies by
themselves are not enough to make every app great. What sets an app apart from its peers is how it helps the
user achieve some tangible goal. After all, users are not going to care what technologies an app uses, as long
as it helps them do what they need to do. An app that gets in the user’s way is going to be forgotten, but one
that makes work (or play) easier and more fun is going to be remembered.
You use Cocoa to write apps for OS X. Cocoa gives you access to all of the features of OS X and allows you to
integrate your app cleanly with the rest of the system. This chapter covers the key parts of OS X that help you
create great apps. In particular, this chapter describessome of the important ease-of-use technologiesintroduced
in OS X v10.7. For a more thorough list of technologies available in OS X, see Mac Technology Overview.
An Environment Designed for Ease of Use
OS X strives to provide an environment that is transparent to users and as easy to use as possible. By making
hard tasks simple and getting out of the way, the system makes it easier for the user to be creative and spend
less time worrying about the steps needed to make the computer work. Of course, simplifying tasks means
your app has to do more of the work, but OS X provides help in that respect too.
As you design your app, you should think about the tasks that users normally perform and find ways to make
them easier. OS X supports powerful ease-of-use features and design principles. For example:
● Users should not have to save their work manually. The document model in Cocoa provides support for
saving the user’sfile-based documents without user interaction;see “The Document Architecture Provides
Many Capabilities for Free” (page 32).
● Apps should restore the user’s work environment at login time. Cocoa provides support for archiving the
current state of the app’s interface (including the state of unsaved documents) and restoring that state at
launch time; see “User Interface Preservation” (page 39).
● Appsshould support automatic termination so that the user never hasto quit them. Automatic termination
means that when the user closes an app’s windows, the app appears to quit but actually just moves to
the background quietly. The advantage is that subsequent launches are nearly instant as the app simply
moves back to the foreground; see “Automatic and Sudden Termination of Apps Improve the User
Experience” (page 36)
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
10
The Mac Application Environment● You should consider providing your users with an immersive, full-screen experience by implementing a
full-screen version of your user interface. The full-screen experience eliminates outside distractions and
allows the user to focus on their content; see “Implementing the Full-Screen Experience” (page 49).
● Support trackpad gestures for appropriate actions in your app. Gestures provide simple shortcuts for
common tasks and can be used to supplement existing controls and menu commands. OS X provides
automatic support for reporting gestures to your app through the normal event-handling mechanism;
see Cocoa Event Handling Guide .
● Consider minimizing or eliminating the user’s interactions with the raw file system. Rather than expose
the entire file system to the user through the open and save panels, some apps, in the manner of iPhoto
and iTunes, can provide a better user experience by presenting the user’s content in a simplified browser
designed specifically for the app’s content. OS X uses a well-defined file system structure that allows you
to place and find files easily and includes many technologies for accessing those files; see “The File
System” (page 13).
● For apps that support custom document types, provide a Quick Look plug-in so that users can view your
documents from outside of your app; see Quick Look Programming Guide .
● Apps should support the fundamental features for the OS X user experience that make apps elegant and
intuitive,such as direct manipulation and drag-and-drop. Usersshould remain in control, receive consistent
feedback, and be able to explore because the app is forgiving with reversible actions; see OS X Human
Interface Guidelines.
All of the preceding features are supported by Cocoa and can be incorporated with relatively little effort.
A Sophisticated Graphics Environment
High-quality graphics and animation make your app look great and can convey a lot of information to the user.
Animations in particular are a great way to provide feedback about changes to your user interface. So as you
design your app, keep the following ideas in mind:
● Use animations to provide feedback and convey changes. Cocoa provides mechanisms for creating
sophisticated animations quickly in both the AppKit and Core Animation frameworks. For information
about creating view-based animations, see Cocoa Drawing Guide . For information about using Core
Animation to create your animations, see Core Animation Programming Guide .
●
Include high-resolution versions of your art and graphics. OS X automatically loads high-resolution image
resources when an app runs on a screen whose scaling factor is greater than 1.0. Including such image
resources makes your app’s graphics look even sharper and crisper on those higher-resolution screens.
Forinformation aboutthe graphicstechnologies available inOS X,see “Media Layer” in Mac TechnologyOverview.
The Mac Application Environment
A Sophisticated Graphics Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
11Low-Level Details of the Runtime Environment
When you are ready to begin writing actual code, there are a lot of technologies available to make your life
easier. OS X supports all of the basic features such as memory management, file management, networking,
and concurrency that you need to write your code. In some cases, though, OS X also provides more sophisticated
services (or specific coding conventions) that, when followed, can make writing your code even easier.
Based on UNIX
OS X is powered by a 64-bit Mach kernel, which manages processor resources, memory, and other low-level
behaviors. On top of the kernel sits a modified version of the Berkeley Software Distribution (BSD) operating
system, which provides interfaces that apps can use to interact with the lower-level system. This combination
of Mach and BSD provides the following system-level support for your apps:
● Preemptive multitasking—All processes share the CPU efficiently. The kernel schedules processes in a
way that ensures they all receive the time they need to run. Even background apps continue to receive
CPU time to execute ongoing tasks.
● Protected memory—Each process runs in its own protected memory space, which prevents processes
from accidentally interfering with each other. (Apps can share part of their memory space to implement
fast interprocess communication but take responsibility for synchronizing and locking that memory
appropriately.)
● Virtual memory—64-bit apps have a virtual address space of approximately 18 exabytes (18 billion billion
bytes). (If you create a 32-bit app, the amount of virtual memory is only 4 GB.) When an app’s memory
usage exceedsthe amount of free physical memory, the system transparently writes pagesto disk to make
more room. Written out pages remain on disk until they are needed in memory again or the app exits.
● Networking and Bonjour—OS X provides support for the standard networking protocols and services in
use today. BSD sockets provide the low-level communication mechanism for apps, but higher-level
interfaces also exist. Bonjour simplifies the user networking experience by providing a dynamic way to
advertise and connect to network services over TCP/IP.
For detailed information about the underlying environment of OS X, see “Kernel and Device Drivers Layer” in Mac
Technology Overview.
Concurrency and Threading
Each process starts off with a single thread of execution and can create more threads as needed. Although you
can create threads directly using POSIX and other higher-level interfaces, for most types of work it is better to
create them indirectly using block objects with Grand Central Dispatch (GCD) or operation objects, a Cocoa
concurrency technology implemented by the NSOperation class.
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
12GCD and operation objects are an alternative to raw threads that simplify or eliminate many of the problems
normally associated with threaded programming,such assynchronization and locking. Specifically, they define
an asynchronous programming model in which you specify only the work to be performed and the order in
which you want it performed. The system then handles the tedious work required to schedule the necessary
threads and execute your tasks as efficiently as possible on the current hardware. You should not use GCD or
operations for work requiring time-sensitive data processing (such as audio or video playback), but you can
use them for most other types of tasks.
For more information on using GCD and operation objects to implement concurrency in your apps, see
Concurrency Programming Guide .
The File System
The file system in OS X is structured to provide a better experience for users. Rather than exposing the entire
file system to the user, the Finder hides any files and directories that an average user should not need to use,
such as the contents of low-level UNIX directories. This is done to provide a simpler interface for the end user
(and only in places like the Finder and the open and save panels). Apps can still access any files and directories
for which they have valid permissions, regardless of whether they are hidden by the Finder.
When creating apps, you should understand and follow the conventions associated with the OS X file system.
Knowing where to put files and how to get information out of the file system ensures a better user experience.
A Few Important App Directories
The OS X file system is organized in a way that groups related files and data together in specific places. Every
file in the file system has its place and apps need to know where to put the files they create. This is especially
important if you are distributing your app through the App Store, which expects you to put your app’s data
files in specific directories.
Table 1-1 lists the directories with which apps commonly interact. Some of these directories are inside the
home directory, which is either the user’s home directory or, if the app adopts App Sandbox, the app’s container
directory as described in “App Sandbox and XPC” (page 18). Because the actual paths can differ based on
these conditions, use the URLsForDirectory:inDomains: method of the NSFileManager classto retrieve
the actual directory path. You can then add any custom directory and filename information to the returned
URL object to complete the path.
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
13Table 1-1 Key directories for Mac apps
Directory Description
Thisisthe installation directory for your app bundle. The path for the global Applications
directory is /Applications but each user directory may have a local applications
directory containing user-specific apps. Regardless, you should not need to use this
path directly. To access resources inside your application bundle, use an NSBundle
object instead.
For more information about the structure of your application bundle and how you
locate resources, see “The OS X Application Bundle” (page 66).
Applications
directory
The configuration of your app determines the location of the home directory seen by
your app:
● For apps running in a sandbox in OS X v10.7 and later, the home directory is the
app’s container directory. For more information about the container directory,
see “The Keychain” (page 20).
● For apps running outside of a sandbox (including those running in versions of OS
X before 10.7), the home directory isthe user-specific subdirectory of /Users that
contains the user’s files.
To retrieve the path to the home directory, use the NSHomeDirectory function.
Home
directory
The Library directory is the top-level directory for storing private app-related data and
preferences. There are several Library directories scattered throughout the system but
you should always use the one located inside the current home directory.
Do not store files directly at the top-level of the Library directory. Instead, store them
in one of the specific subdirectories described in this table.
In OS X v10.7 and later, the Finder hides the Library directory in the user’s home folder
by default. Therefore, you should never store files in this directory that you want the
user to access.
To get the path to this directory use the NSLibraryDirectory search path key with
the NSUserDomainMask domain.
Library
directory
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
14Directory Description
The Application Support directory is where your app stores any type of file thatsupports
the app but is not required for the app to run, such as document templates or
configuration files. The files should be app-specific but should never store user data.
This directory is located inside the Library directory.
Never store files at the top level of this directory: Always put them in a subdirectory
named for your app or company.
If the resources apply to all users on the system, such as document templates, place
them in /Library/Application Support. To get the path to this directory use
the NSApplicationSupportDirectory search path key with the
NSLocalDomainMask domain. If the resources are user-specific, such as workspace
configuration files, place them in the current user’s ~/Library/Application
Support directory. To get the path to this directory use the
NSApplicationSupportDirectory search path key with the NSUserDomainMask
domain.
Application
Support
directory
The Caches directory is where you store cache files and other temporary data that your
app can re-create as needed. This directory is located inside the Library directory.
Never store files at the top level of this directory: Always put them in a subdirectory
named for your app or company. Your app is responsible for cleaning out cache data
files when they are no longer needed. The system does not delete files from this
directory.
To get the path to this directory use the NSCachesDirectory search path key with
the NSUserDomainMask domain.
Caches
directory
The Movies directory contains the user’s video files.
To get the path to this directory use the NSMoviesDirectory search path key with
the NSUserDomainMask domain.
Movies
directory
The Music directory contains the user’s music and audio files.
To get the path to this directory use the NSMusicDirectory search path key with
the NSUserDomainMask domain.
Music
directory
The Pictures directory contains the user’s images and photos.
To get the path to this directory use the NSPicturesDirectory search path key
with the NSUserDomainMask domain.
Pictures
directory
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
15Directory Description
The Temporary directory is where you store files that do not need to persist between
launches of your app. You normally use this directory for scratch files or other types
ofshort-lived data filesthat are not related to your app’s persistent data. This directory
is typically hidden from the user.
Your app should remove files from this directory as soon as it is done with them. The
system may also purge lingering files from this directory at system startup.
To get the path to this directory use the NSTemporaryDirectory function.
Temporary
directory
Listing 1-1 shows an example of how to retrieve the base path to the Application Support directory and
then append a custom app directory to it.
Listing 1-1 Getting the path to the Application Support directory
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* appSupportDir = nil;
NSArray *urls = [fileManager URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask];
if ([paths count] > 0) {
appSupportDir = [[urls objectAtIndex:0]
URLByAppendingPathComponent:@"com.example.MyApp"];
}
For more information about how to access files in well known system directories, see File System Programming
Guide .
Coordinating File Access with Other Processes
In OS X, other processes may have access to the same files that your app does. Therefore, when working with
files, you should use the file coordination interfacesintroduced in OS X v10.7 to be notified when other processes
(including the Finder) attempt to read or modify files your app is currently using. For example, coordinating
file access is critical when your app adopts iCloud storage.
The file coordination APIs allow you to assert ownership over files and directories that your app cares about.
Any time another process attempts to touch one of those items, your app is given a chance to respond. For
example, when an app attemptsto read the contents of a document your app is editing, you can write unsaved
changes to disk before the other process is allowed to do its reading.
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
16Using iCloud document storage, for example, you must incorporate file coordination because multiple apps
can access your document files in iCloud. The simplest way to incorporate file coordination into your app is to
use the NSDocument class, which handles all of the file-related management for you. See Document-Based
App Programming Guide for Mac .
On the other hand, if you're writing a library-style (or “shoebox”) app, you must use the file coordination
interfaces directly, as described in File System Programming Guide .
Interacting with the File System
Disks in Macintosh computers are formatted using the HFS+ file system by default. However, Macintosh
computers can interact with disks that use other formats so you should never code specifically to any one file
system. Table 1-2 lists some of the basic file system attributes you may need to consider in your app and how
you should handle them.
Table 1-2 Attributes for the OS X file system
Attribute Description
The HFS+ file system is case-insensitive but also case-preserving. Therefore, when
specifying filenames and directoriesin your code, it is best to assume case-sensitivity.
Case sensitivity
Construct paths using the methods of the NSURL and NSString classes. The NSURL
classis preferred for path construction because of its ability to specify not only paths
in the local file system but paths to network resources.
Path
construction
Many file-related attributes can be retrieved using the getResourceValue:
forKey:error: method of the NSURL class. You can also use an NSFileManager
object to retrieve many file-related attributes.
File attributes
File permissions are managed using access control lists(ACLs) and BSD permissions.
The system uses ACLs whenever possible to specify precise permissionsfor files and
directories, but it falls back to using BSD permissions when no ACLs are specified.
By default, any files your app creates are owned by the current user and given
appropriate permissions. Thus, your app should always be able to read and write
files it creates explicitly. In addition, the app’s sandbox may allow it to access other
filesin specific situations. For more information about the sandbox,see “App Sandbox
and XPC” (page 18).
File permissions
Appsthat cannot use the File Coordination interfaces(see “Coordinating File Access
with Other Processes” (page 16)) to track changes to files and directories can use
the FSEvents API instead. This API provides a lower-level interface for tracking file
system interactions and is available in OS X v10.5 and later.
For information on how to use the FSEvents API,see File SystemEvents Programming
Guide .
Tracking file
changes
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
17Security
The security technologies in OS X help you safeguard sensitive data created or managed by your app, and help
minimize damage caused by successful attacks from hostile code. These technologies impact how your app
interacts with system resources and the file system.
App Sandbox and XPC
You secure your app against attack from malware by following the practices recommended in Secure Coding
Guide . But an attacker needs only to find a single hole in your defenses, or in any of the frameworks and libraries
that you link against, to gain control of your app along with all of its privileges.
Starting in OS X v10.7, App Sandbox provides a last line of defense against stolen, corrupted, or deleted user
data if malicious code exploits your app. App Sandbox also minimizes the damage from coding errors. Its
strategy is twofold:
1. App Sandbox enables you to describe how your app interacts with the system. The system then grants
your app the access it needs to get its job done, and no more. For your app to provide the highest level
of damage containment, the best practice is to adopt the tightest sandbox possible.
2. App Sandbox allows the user to transparently grant your app additional access by way of Open and Save
dialogs, drag and drop, and other familiar user interactions.
You describe your app’s interaction with the system by way of setting entitlements in Xcode. An entitlement
is a key-value pair, defined in a property list file, that confers a specific capability or security permission to a
target. For example, there are entitlement keys to indicate that your app needs access to the camera, the
network, and user data such as the Address Book. For details on all the entitlements available in OS X, see
Entitlement Key Reference .
When you adopt App Sandbox, the system provides a special directory for use by your app—and only by your
app—called a container. Your app has unfettered read/write access to the container. All OS X path-finding
APIs, above the POSIX layer, are relative to the container instead of to the user’s home directory. Othersandboxed
apps have no access to your app’s container, as described further in “Code Signing” (page 19).
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
18iOS Note: Because it is not for user documents, an OS X container differs from an iOS container
which, in iOS, is the one and only location for user documents. As the sole local location for user
documents, an iOS container is usually known simply as an app’s Documents directory.
In addition, an iOS container contains the app itself. This is not so in OS X.
iCloud Note: Apple’s iCloud technology, as described in “iCloud Storage”, uses the name “container”
as well. There is no functional connection between an iCloud container and an App Sandbox container.
Your sandboxed app can access paths outside of its container in the following three ways:
● At the specific direction of the user
● By you configuring your app with entitlements for specific file-system locations, such as the Movies folder
● When a path is in any of certain directories that are world readable
The OS X security technology that interacts with the user to expand yoursandbox is called Powerbox. Powerbox
has no API. Your app uses Powerbox transparently when, for example, you use the NSOpenPanel and
NSSavePanel classes, or when the user employs drag and drop with your app.
Some app operations are more likely to be targets of malicious exploitation. Examples are the parsing of data
received over a network, and the decoding of video frames. By using XPC, you can improve the effectiveness
of the damage containment offered by App Sandbox by separating such potentially dangerous activities into
their own address spaces.
XPC is an OS X interprocess communication technology that complements App Sandbox by enabling privilege
separation. Privilege separation, in turn, is a development strategy in which you divide an app into pieces
according to the system resource access that each piece needs. The component pieces that you create are
called XPC services. For details on adopting XPC, see Daemons and Services Programming Guide .
For a complete explanation of App Sandbox and how to use it, read App Sandbox Design Guide .
Code Signing
OS X employs the security technology known as code signing to allow you to certify that your app was indeed
created by you. After an app is code signed, the system can detect any change to the app—whether the change
is introduced accidentally or by malicious code. Various security technologies, including App Sandbox and
parental controls, depend on code signing.
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
19In most cases, you can rely on Xcode’s automatic code signing, which requires only that you specify a code
signing identity in the build settings for your project. The steps to take are described in “Code Signing Your App”
in Tools Workflow Guide for Mac . If you need to incorporate code signing into an automated build system, or
if you link your app against third-party frameworks, refer to the procedures described in Code Signing Guide .
When you adopt App Sandbox, you must code sign your app. This is because entitlements (including the
special entitlement that enables App Sandbox) are built into an app’s code signature.
OS X enforces a tie between an app’s container and the app’s code signature. This important security feature
ensures that no other sandboxed app can access your container. The mechanism works as follows: After the
system creates a container for an app, each time an app with the same bundle ID launches, the system checks
that the app’s code signature matches a code signature expected by the container. If the system detects a
mismatch, it prevents the app from launching.
For a complete explanation of code signing in the context of App Sandbox, read “App Sandbox in Depth” in App
Sandbox Design Guide .
The Keychain
A keychain is a secure, encrypted container for storing a user’s passwords and other secrets. It is designed to
help a user manage their multiple logins, each with its own ID and password. You should always use keychain
to store sensitive credentials for your app.
For more on the keychain, see “Keychain Services Concepts” in Keychain Services Programming Guide .
The Mac Application Environment
Low-Level Details of the Runtime Environment
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
20To unleash the power of OS X, you develop apps using the Cocoa application environment. Cocoa presents
the app’s user interface and integrates it tightly with the other components of the operating system. Cocoa
provides an integrated suite of object-oriented software components packaged in two core class libraries, the
AppKit and Foundation frameworks, and a number of underlying frameworks providing supporting technologies.
Cocoa classes are reusable and extensible—you can use them as is or extend them for your particular
requirements.
Cocoa makes it easy to create apps that adopt all of the conventions and expose all of the power of OS X. In
fact, you can create a new Cocoa application project in Xcode and, without adding any code, have a functional
app. Such an app is able to display its window (or create new documents) and implements many standard
system behaviors. And although the Xcode templates provide some code to make this all happen, the amount
of code they provide is minimal. Most of the behavior is provided by Cocoa itself.
To make a great app, you should build on the foundations Cocoa lays down for you, working with the
conventions and infrastructure provided for you. To do so effectively, it'simportant to understand how a Cocoa
app fits together.
Fundamental Design Patterns
Cocoa incorporates many design patterns in its implementation. Table 2-1 lists the key design patterns with
which you should be familiar.
Table 2-1 Fundamental design patterns used by Mac apps
Design pattern Why it is important
Use of the Model-View-Controller (MVC) design pattern ensures that the
objects you create now can be reused or updated easily in future versions of
your app.
Cocoa provides most of the classes used to build your app’s controller and
view layers. It is your job to customize the classes you need and provide the
necessary data model objects to go with them.
MVC is central to a good design for a Cocoa application because many Cocoa
technologies and architectures are based on MVC and require that your custom
objects assume one of the MVC roles.
Model-View-Controller
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
21
The Core App DesignDesign pattern Why it is important
The delegation design pattern allows you to change the runtime behavior of
an object without subclassing. Delegate objects conform to a specific protocol
that defines the interaction points between the delegate and the object it
modifies. Atspecific points, the master object callsthe methods of its delegate
to provide it with information or ask what to do. The delegate can then take
whatever actions are appropriate.
Delegation
The responder chain definesthe relationships between event-handling objects
in your app. As events arrive, the app dispatches them to the first responder
object for handling. If that object does not want the event, it passes it to the
next responder, which can either handle the event or send it to its next
responder, and so on up the chain.
Windows and views are the most common types of responder objects and are
always the first responders for mouse events. Other types of objects, such as
your app’s controller objects, may also be responders.
Responder chain
Controls use the target-action design pattern to notify your app of user
interactions. When the user interacts with a control in a predefined way (such
as by touching a button), the controlsends a message (the action) to an object
you specify (the target). Upon receiving the action message, the target object
can then respond in an appropriate manner.
Target-action
Block objects are a convenient way to encapsulate code and local stack
variablesin a form that can be executed later. Blocks are used in lieu of callback
functions by many frameworks and are also used in conjunction with Grand
Central Dispatch to perform tasks asynchronously.
For more information about using blocks, see Blocks Programming Topics.
Block objects
Notifications are used throughout Cocoa to deliver news of changes to your
app. Many objects send notifications at key moments in the object’s life cycle.
Intercepting these notifications gives you a chance to respond and add custom
behavior.
Notifications
KVO tracks changes to a specific property of an object. When that property
changes, the change generates automatic notifications for any objects that
registered an interest in that property. Those observers then have a chance to
respond to the change.
Key-value observing
(KVO)
The Core App Design
Fundamental Design Patterns
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
22Design pattern Why it is important
Cocoa bindings provide a convenient bridge between the model, view, and
controller portions of your app. You bind a view to some underlying data object
(which can be static or dynamic) through one of your controllers. Changes to
the view are then automatically reflected in the data object, and vice versa.
The use of bindings is not required for apps but does minimize the amount of
code you have to write. You can set up bindings programmatically or using
Interface Builder.
Bindings
For a more detailed discussion of Cocoa and the design patterns you use to implement Mac apps, see Cocoa
Fundamentals Guide .
The App Style Determines the Core Architecture
The style of your app defines which core objects you must use in its implementation. Cocoa supports the
creation of both single-window and multiwindow apps. For multiwindow designs, it also provides a document
architecture to help manage the files associated with each app window. Thus, apps can have the following
forms:
● Single-window utility app
● Single-window library-style app
● Multiwindow document-based app
You should choose a basic app style early in your design process because that choice affects everything you
do later. The single-window styles are preferred in many cases, especially for developers bringing apps from
iOS. The single-window style typically yields a more streamlined user experience, and it also makes it easier
for your app to support a full-screen mode. However, if your app works extensively with complex documents,
the multiwindow style may be preferable because it provides more document-related infrastructure to help
you implement your app.
The Core App Design
The App Style Determines the Core Architecture
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
23The Calculator app provided with OS X, shown in Figure 2-1, is an example of a single-window utility app.
Utility apps typically handle ephemeral data or manage system processes. Calculator does not create or deal
with any documents or persistent user data but simply processes numerical data entered by the user into the
text field in its single window, displaying the results of its calculations in the same field. When the user quits
the app, the data it processed is simply discarded.
Figure 2-1 The Calculator single-window utility app
Single-window, library-style (or “shoebox”) apps do handle persistent user data. One of the most prominent
examples of a library-style app is iPhoto, shown in Figure 2-2. The user data handled by iPhoto are photos (and
associated metadata), which the app edits, displays, and stores. All user interaction with iPhoto happens in a
single window. Although iPhoto stores its data in files, it doesn’t present the files to the user. The app presents
a simplified interface so that users don’t need to manage files in order to use the app. Instead, they work
directly with their photos. Moreover, iPhoto hides its files from regular manipulation in the Finder by placing
The Core App Design
The App Style Determines the Core Architecture
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
24them within a single package. In addition, the app savesthe user’s editing changesto disk at appropriate times.
So, users are relieved of the need to manually save, open, or close documents. This simplicity for users is one
of the key advantages of the library-style app design.
Figure 2-2 The iPhoto single-window app
A good example of a multiwindow document-based app is TextEdit, which creates, displays, and edits documents
containing plain or styled text and images. TextEdit does not organize or manage its documents—users do
that with the Finder. Each TextEdit document opens in its own window, multiple documents can be open at
The Core App Design
The App Style Determines the Core Architecture
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
25one time, and the user interacts with the frontmost document using controls in the window’s toolbar and the
app’s menu bar. Figure 2-3 shows a document created by TextEdit. For more information about the
document-based app design, see “Document-Based Apps Are Based on an NSDocument Subclass” (page 31).
Figure 2-3 TextEdit document window
Both single-window and multiwindow apps can present an effective full-screen mode, which provides an
immersive experience that enables users to focus on their tasks without distractions. For information about
full-screen mode, see “Implementing the Full-Screen Experience” (page 49).
The Core Objects for All Cocoa Apps
Regardless of whether you are using a single-window or multiwindow app style, all apps use the same core
set of objects. Cocoa provides the default behavior for most of these objects. You are expected to provide a
certain amount of customization of these objects to implement your app’s custom behavior.
The Core App Design
The App Style Determines the Core Architecture
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
26Figure 2-4 shows the relationships among the core objects for the single-window app styles. The objects in
this figure are separated according to whether they are part of the model, view, or controller portions of the app.
As you can see from the figure, the Cocoa–provided objects provide much of the controller and view layer for
your app.
Figure 2-4 Key objects in a single-window app
Table 2-2 (page 27) describes the roles played by the objects in the diagram.
Table 2-2 The core objects used by all Cocoa apps
Object Description
(Required) Runs the event loop and manage interactions between your app and
the system. You typically use the NSApplication class as is, putting any custom
app-object-related code in your application delegate object.
NSApplication
object
(Expected) A custom object that you provide which works closely with the
NSApplication object to run the app and manage the transitions between
different application states.
Your application delegate object must conform to the NSApplicationDelegate
Protocol.
Application
delegate object
The Core App Design
The App Style Determines the Core Architecture
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
27Object Description
Store content specific to your app. A banking app might store a database
containing financial transactions, whereas a painting app might store an image
object or the sequence of drawing commands that led to the creation of that
image.
Data model
objects
Responsible for loading and managing a single window each and coordinating
with the system to handle standard window behaviors.
You subclass NSWindowController tomanage both the window and its contents.
Each window controller is responsible for everything that happens in its window.
If the contents of your window are simple, the window controller may do all of
the management itself. If your window is more complex, the window controller
might use one or more view controllers to manage portions of the window.
Window
controllers
Represent your onscreen windows, configured in different styles depending on
your app’s needs. For example, most windows have title bars and borders but you
can also configure windows without those visual adornments. A window object
is almost always managed by a window controller.
An app can also have secondary windows, also known as dialogs and panels.
These windows are subordinate to the current document window or, in the case
of single-window apps, to the main window. They support the document or main
window, for example, allowing selection of fonts and color, allowing the selection
of tools from a palette, or displaying a warning‚ A secondary window is often
modal.
Window objects
Coordinate the loading of a single view hierarchy into your app. Use view
controllersto divide up the work for managing more sophisticated window layouts.
Your view controllers work together (with the window controller) to present the
window contents.
If you have developed iOS apps, be aware that AppKit view controllers play a less
prominent role than UIKit view controllers. In OS X, AppKit view controllers are
assistantsto the window controller, which is ultimately responsible for everything
that goes in the window. The main job of an AppKit view controller is to load its
view hierarchy. Everything else is custom code that you write.
View controllers
Define a rectangular region in a window, draw the contents of that region, and
handle events in that region. Views can be layered on top of each other to create
view hierarchies, whereby one view obscures a portion of the underlying view.
View objects
Representstandard system controls. These view subclasses provide standard visual
items such as buttons, text fields, and tables that you can use to build your user
interface. Although a few controls are used as is to present visual adornments,
most work with your code to manage user interactions with your app’s content.
Control objects
The Core App Design
The App Style Determines the Core Architecture
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
28Additional Core Objects for Multiwindow Apps
As opposed to a single-window app, a multiwindow app uses several windows to present its primary content.
The Cocoa support for multiwindow appsis built around a document-based model implemented by a subsystem
called the document architecture. In this model, each document object manages its content, coordinates the
reading and writing of that content from disk, and presents the content in a window for editing. All document
objects work with the Cocoa infrastructure to coordinate event delivery and such, but each document object
is otherwise independent of its fellow document objects.
Figure 2-5 shows the relationships among the core objects of a multiwindow document-based app. Many of
the same objects in this figure are identical to those used by a single-window app. The main difference is the
insertion of the NSDocumentController and NSDocument objects between the application objects and the
objects for managing the user interface.
Figure 2-5 Key objects in a multiwindow document app
Table 2-3 (page 30) describes the role of the inserted NSDocumentController and NSDocument objects.
(For information about the roles of the other objects in the diagram, see Table 2-2 (page 27).)
The Core App Design
The App Style Determines the Core Architecture
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
29Table 2-3 Additional objects used by multiwindow document apps
Object Description
The NSDocumentController class defines a high-level controller for creating
and managing all document objects. In addition to managing documents, the
document controller also manages many document-related menu items, such as
the Open Recent menu and the open and save panels.
Document
Controller object
The NSDocument class is the base class for implementing documents in a
multiwindow app. This class acts as the controller for the data objects associated
with the document. You define your own custom subclasses to manage the
interactions with your app’s data objects and to work with one or more
NSWindowController objectsto display the document contents on the screen.
Document object
Integrating iCloud Support Into Your App
No matter how you store your app’s data, iCloud is a convenient way to make that data available to all of a
user’s devices. To integrate iCloud into your app, you change where you store user files. Instead of storing
them in the user’s Home folder or in your App Sandbox container, you store them in special file system locations
known as ubiquity containers. A ubiquity containerserves asthe local representation of corresponding iCloud
storage. It is outside of your App Sandbox container, and so requires specific entitlements for your app to
interact with it.
In addition to a change in file system locations, your app design needs to acknowledge that your data model
is accessible to multiple processes. The following considerations apply:
● Document-based apps get iCloud support through the NSDocument class, which handles most of the
interactions required to manage the on-disk file packages that represent documents.
●
If you implement a custom data model and manage files yourself, you must explicitly use file coordination
to ensure that the changes you make are done safely and in concert with the changes made on the user’s
other devices. For details,see “The Role of File Coordinators and Presenters” in File System Programming Guide .
● For storing small amounts of data in iCloud, you use key-value storage. Use key-value storage for such
things as stocks or weather information, locations, bookmarks, a recent-documents list, settings and
preferences, and simple game state. Every iCloud app should take advantage of key-value storage. To
interact with key-value storage, you use the shared NSUbiquitousKeyValueStore object.
To learn how to adopt iCloud in your app, read iCloud Design Guide .
The Core App Design
The App Style Determines the Core Architecture
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
30Shoebox-Style Apps Should Not Use NSDocument
When implementing a single-window, shoebox-style (sometimes referred to as a “library” style) app, it is
sometimes better not to use NSDocument objectsto manage your content. The NSDocument class was designed
specifically for use in multiwindow document apps. Instead, use custom controller objects to manage your
data. Those custom controllers would then work with a view controller or your app’s main window controller
to coordinate the presentation of the data.
Although you normally use an NSDocumentController object only in multiwindow apps, you can subclass
it and use it in a single-window app to coordinate the Open Recent and similar behaviors. When subclassing,
though, you must override any methods related to the creation of NSDocument objects.
Document-Based Apps Are Based on an NSDocument Subclass
Documents are containers for user data that can be stored in files and iCloud. In a document-based design,
the app enables users to create and manage documents containing their data. One app typically handles
multiple documents, each in its own window, and often displays more than one document at a time. For
example, a word processor provides commands to create new documents, it presents an editing environment
in which the user enters text and embeds graphics into the document, it saves the document data to disk (and,
optionally, iCloud), and it provides other document-related commands, such as printing and version
management. In Cocoa, the document-based app design is enabled by the document architecture, which is
part of of the AppKit framework.
Documents in OS X
There are several waysto think of a document. Conceptually, a document is a container for a body of information
that can be named and stored in a disk file and in iCloud. In this sense, the document is not the same as the
file but is an object in memory that owns and manages the document data. To users, the document is their
information—such as text and graphics formatted on a page. Programmatically, a document is an instance of
a custom NSDocument subclass that knows how to represent internally persistent data that it can display in
windows. This document object knows how to read document data from a file and create an object graph in
The Core App Design
Document-Based Apps Are Based on an NSDocument Subclass
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
31memory for the document data model. It also knows how to handle the user’s editing commands to modify
the data model and write the document data back out to disk. So, the document object mediates between
different representations of document data, as shown in Figure 2-6.
Figure 2-6 Document file, object, and data model
Using iCloud, documents can be shared automatically among a user’s computers and iOS devices. Changes to
the document data are synchronized without user intervention. For information about iCloud, see “Integrating
iCloud Support Into Your App” (page 30).
The Document Architecture Provides Many Capabilities for Free
The document-based style of app is a design choice that you should consider when you design your app. If it
makes sense for your users to create multiple discrete sets of data, each of which they can edit in a graphical
environment and store in files or iCloud, then you certainly should plan to develop a document-based app.
The Cocoa document architecture provides a framework for document-based apps to do the following things:
● Create new documents. The first time the user chooses to save a new document, it presents a dialog
enabling the user to name and save the document in a disk file in a user-chosen location.
● Open existing documents stored in files. A document-based app specifies the types of document it can
read and write, as well as read-only and write-only types. It can represent the data of different types
internally and display the data appropriately. It can also close documents.
● Automatically save documents. Document-based apps can adopt autosaving in place, and its documents
are automatically saved at appropriate times so that the data the user sees on screen is effectively the
same as that saved on disk. Saving is done safely, so that an interrupted save operation does not leave
data inconsistent.
The Core App Design
Document-Based Apps Are Based on an NSDocument Subclass
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
32● Asynchronously read and write document data. Reading and writing are done asynchronously on a
background thread, so that lengthy operations do not make the app’s user interface unresponsive. In
addition, reads and writes are coordinated using NSFilePresenter protocol and NSFileCoordinator
class to reduce version conflicts. Coordinated reads and writes reduce version conflicts both among
different appssharing document data in localstorage and among different instances of an app on different
devices sharing document data via iCloud.
● Manage multiple versions of documents. Autosave creates versions at regular intervals, and users can
manually save a version whenever they wish. Users can browse versions and revert the document’s contents
to a chosen version using a Time Machine–like interface. The version browser is also used to resolve version
conflicts from simultaneous iCloud updates.
● Print documents. The print dialog and page setup dialog enable the user to choose various page layouts.
● Monitor and set the document’s edited status and validate menu items. To avoid automatic saving of
inadvertent changes, old files are locked from editing until explicitly unlocked by the user.
● Track changes. The document manages its edited status and implements multilevel undo and redo.
● Handle app and window delegation. Notifications are sent and delegate methods called at significant
lifecycle events, such as when the app terminates.
See Document-Based App Programming Guide for Mac for more detailed information about how to implement
a document-based app.
The App Life Cycle
The app life cycle is the progress of an app from its launch through its termination. Apps can be launched by
the user or the system. The user launches apps by double-clicking the app icon, using Launchpad, or opening
a file whose type is currently associated with the app. In OS X v10.7 and later, the system launches apps at user
login time when it needs to restore the user’s desktop to its previous state.
When an app is launched, the system creates a process and all of the normal system-related data structures
for it. Inside the process, it creates a main thread and uses it to begin executing your app’s code. At that point,
your app’s code takes over and your app is running.
The main Function is the App Entry Point
Like any C-based app, the main entry point for a Mac app at launch time is the main function. In a Mac app,
the main function is used only minimally. Its main job is to give control to the AppKit framework. Any new
project you create in Xcode comes with a default main function like the one shown in Listing 2-1. You should
normally not need to change the implementation of this function.
The Core App Design
The App Life Cycle
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
33Listing 2-1 The main function of a Mac app
#import
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **) argv);
}
The NSApplicationMain function initializes your app and prepares it to run. As part of the initialization
process, this function does several things:
● Creates an instance of the NSApplication class. You can access this object from anywhere in your app
using the sharedApplication class method.
● Loads the nib file specified by the NSMainNibFile key in the Info.plist file and instantiates all of the
objects in that file. This is your app’s main nib file and should contain your application delegate and any
other critical objects that must be loaded early in the launch cycle. Any objects that are not needed at
launch time should be placed in separate nib files and loaded later.
● Calls the run method of your application object to finish the launch cycle and begin processing events.
By the time the run method is called, the main objects of your app are loaded into memory but the app is still
not fully launched. The run method notifies the application delegate that the app is about to launch, shows
the application menu bar, opens any files that were passed to the app, does some framework housekeeping,
and starts the event processing loop. All of this work occurs on the app’s main thread with one exception. Files
may be opened in secondary threads if the canConcurrentlyReadDocumentsOfType: class method of the
corresponding NSDocument object returns YES.
If your app preserves its user interface between launch cycles, Cocoa loads any preserved data at launch time
and uses it to re-create the windows that were open the last time your app was running. For more information
about how to preserve your app’s user interface, see “User Interface Preservation” (page 39).
The App’s Main Event Loop Drives Interactions
Asthe user interacts with your app, the app’s main event loop processesincoming events and dispatchesthem
to the appropriate objects for handling. When the NSApplication object is first created, it establishes a
connection with the system window server, which receives events from the underlying hardware and transfers
The Core App Design
The App Life Cycle
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
34them to the app. The app also sets up a FIFO event queue to store the events sent to it by the window server.
The main event loop isthen responsible for dequeueing and processing events waiting in the queue, asshown
in Figure 2-7.
Figure 2-7 The main event loop
The run method of the NSApplication object is the workhorse of the main event loop. In a closed loop, this
method executes the following steps until the app terminates:
1. Services window-update notifications, which results in the redrawing of any windows that are marked as
“dirty.”
2. Dequeues an event from its internal event queue using the
nextEventMatchingMask:untilDate:inMode:dequeue: method and converts the event data into
an NSEvent object.
3. Dispatchesthe event to the appropriate target object using the sendEvent: method of NSApplication.
When the app dispatches an event, the sendEvent: method uses the type of the event to determine the
appropriate target. There are two major types of input events: key events and mouse events. Key events are
sent to the key window—the window that is currently accepting key presses. Mouse events are dispatched
to the window in which the event occurred.
For mouse events, the window looks for the view in which the event occurred and dispatches the event to
that object first. Views are responder objects and are capable of responding to any type of event. If the view is
a control, it typically uses the event to generate an action message for its associated target.
The overall process for handling events is described in detail in Cocoa Event Handling Guide .
The Core App Design
The App Life Cycle
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
35Automatic and Sudden Termination of Apps Improve the User Experience
In OS X v10.7 and later, the use of the Quit command to terminate an app is diminished in favor of more
user-centric techniques. Specifically, Cocoa supports two techniques that make the termination of an app
transparent and fast:
● Automatic termination eliminates the need for users to quit an app. Instead, the system manages app
termination transparently behind the scenes, terminating apps that are not in use to reclaim needed
resources such as memory.
● Sudden termination allows the system to kill an app’s process immediately without waiting for it to
perform any final actions. The system uses this technique to improve the speed of operations such as
logging out of, restarting, or shutting down the computer.
Automatic termination and sudden termination are independent techniques, although both are designed to
improve the user experience of app termination. Although Apple recommendsthat appssupport both, an app
can support one technique and not the other. Apps that support both techniques can be terminated by the
system without the app being involved at all. On the other hand, if an app supports sudden termination but
not automatic termination, then it must be sent a Quit event, which it needs to process without displaying
any user interface dialogs.
Automatic termination transfers the job of managing processes from the user to the system, which is better
equipped to handle the job. Users do not need to manage processes manually anyway. All they really need is
to run apps and have those apps available when they need them. Automatic termination makes that possible
while ensuring that system performance is not adversely affected.
Apps must opt in to both automatic termination and sudden termination and implement appropriate support
for them. In both cases, the app must ensure that any user data is saved well before termination can happen.
And because the user does not quit an autoterminable app, such an app should also save the state of its user
interface using the built-in Cocoa support. Saving and restoring the interface state provides the user with a
sense of continuity between app launches.
For information on how to support for automatic termination in your app, see “Automatic Termination” (page
37). For information on how to support sudden termination, see “Sudden Termination” (page 38).
Support the Key Runtime Behaviors in Your Apps
No matter what style of app you are creating, there are specific behaviors that all apps should support. These
behaviors are intended to help users focus on the content they are creating rather than focus on app
management and other busy work that is not part of creating their content.
The Core App Design
Support the Key Runtime Behaviors in Your Apps
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
36Automatic Termination
Automatic termination is a feature that you must explicitly code for in your app. Declaring support for automatic
termination is easy, but apps also need to work with the system to save the current state of their user interface
so that it can be restored later as needed. The system can kill the underlying process for an auto-terminable
app at any time, so saving this information maintains continuity for the app. Usually, the system kills an app’s
underlying process some time after the user has closed all of the app’s windows. However, the system may
also kill an app with open windows if the app is not currently on screen, perhaps because the user hid it or
switched spaces.
To support automatic termination, you should do the following:
● Declare your app’s support for automatic termination, either programmatically or using an Info.plist
key.
● Support saving and restoring your window configurations.
● Save the user’s data at appropriate times.
● Single-window, library-style apps should implement strategies for saving data at appropriate
checkpoints.
● Multiwindow, document-based apps can use the autosaving and saveless documents capabilities in
NSDocument.
● Whenever possible, support sudden termination for your app as well.
Enabling Automatic Termination in Your App
Declaring support for automatic termination letsthe system know that itshould manage the actual termination
of your app at appropriate times. An app has two ways to declare its support for automatic termination:
●
Include the NSSupportsAutomaticTermination key (with the value YES) in the app’s Info.plist
file. This sets the app’s default support status.
● Use the NSProcessInfo classto declare support for automatic termination dynamically. Use thistechnique
to change the default support of an app that includes the NSSupportsAutomaticTermination key in
its Info.plist file.
Automatic Data-Saving Strategies Relieve the User
You should always avoid forcing the user to save changesto their data manually. Instead, implement automatic
data saving. For a multiwindow app based on NSDocument, automatic saving is as simple as overriding the
autosavesInPlace classmethod to return YES. Formore information,seeDocument-Based App Programming
Guide for Mac .
The Core App Design
Support the Key Runtime Behaviors in Your Apps
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
37For a single-window, library-style app, identify appropriate pointsin your code where any user-related changes
should be saved and write those changes to disk automatically. This benefits the user by eliminating the need
to think about manually saving changes, and when done regularly, it ensures that the user does not lose much
data if there is a problem.
Some appropriate times when you can save user data automatically include the following:
● When the user closes the app window or quits the app (applicationWillTerminate:)
● When the app is deactivated (applicationWillResignActive:)
● When the user hides your app (applicationWillHide:)
● Whenever the user makes a valid change to data in your app
The last item means that you have the freedom to save the user’s data at any time it makes sense to do so. For
example, if the user is editing fields of a data record, you can save each field value as it is changed or you can
wait and save all fields when the user displays a new record. Making these types of incremental changes ensures
that the data is always up-to-date but also requires more fine-grained management of your data model. In
such an instance, Core Data can help you make the changes more easily. For information about Core Data, see
Core Data Starting Point.
Sudden Termination
Sudden termination lets the system know that your app’s process can be killed directly without any additional
involvement from your app. The benefit of supporting sudden termination is that it lets the system close apps
more quickly, which is important when the user is shutting down a computer or logging out.
An app has two ways to declare its support for sudden termination:
●
Include the NSSupportsSuddenTermination key (with the value YES) in the app’s Info.plist file.
● Use the NSProcessInfo class to declare support for sudden termination dynamically. You can also use
this class to change the default support of an app that includes the NSSupportsSuddenTermination
key in its Info.plist file.
One solution is to declare global support for the feature globally and then manually override the behavior at
appropriate times. Because sudden termination means the system can kill your app at any time after launch,
you should disable it while performing actions that might lead to data corruption if interrupted. When the
action is complete, reenable the feature again.
The Core App Design
Support the Key Runtime Behaviors in Your Apps
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
38You disable and enable sudden termination programmatically using the disableSuddenTermination and
enableSuddenTerminationmethods ofthe NSProcessInfo class. Thesemethodsincrement and decrement
a counter, respectively, maintained by the process. When the value of this counter is 0, the process is eligible
for sudden termination. When the value is greater than 0, sudden termination is disabled.
Enabling and disabling sudden termination dynamically also meansthat your app should save data progressively
and not rely solely on user actions to save important information. The best way to ensure that your app’s
information is saved at appropriate times is to support the interfaces in OS X v10.7 for saving your document
and window state. Those interfaces facilitate the automatic saving of relevant user and app data. For more
information about saving your user interface state, see “User Interface Preservation” (page 39). For more
information about saving your documents, see “Document-Based Apps Are Based on an NSDocument
Subclass” (page 31).
For additional information about enabling and disabling sudden termination,see NSProcessInfo Class Reference .
User Interface Preservation
The Resume feature, in OS X v10.7 and later, saves the state of your app’s windows and restores them during
subsequent launches of your app. Saving the state of your windows enables you to return your app to the
state it was in when the user last used it. Use the Resume feature especially if your app supports automatic
termination, which can cause your app to be terminated while it is running but hidden from the user. If your
app supports automatic termination but does not preserve its interface, the app launches into its default state.
Users who only switched away from your app might think that the app crashed while it was not being used.
Writing Out the State of Your Windows and Custom Objects
You must do the following to preserve the state of your user interface:
● For each window, you must set whether the window should be preserved using the setRestorable:
method.
● For each preserved window, you must specify an object whose job is to re-create that window at launch
time.
● Any objects involved in your user interface must write out the data they require to restore their state later.
● At launch time, you must use the provided data to restore your objects to their previous state.
The actual process of writing out your application state to disk and restoring it later is handled by Cocoa, but
you must tell Cocoa what to save. Your app’s windows are the starting point for all save operations. Cocoa
iterates over all of your app’s windows and saves data for the ones whose isRestorable method returns
YES. Most windows are preserved by default, but you can change the preservation state of a window using
the setRestorable: method.
The Core App Design
Support the Key Runtime Behaviors in Your Apps
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
39In addition to preserving your windows, Cocoa saves data for most of the responder objects associated with
the window. Specifically, it saves the views and window controller objects associated with the window. (For a
multiwindow document-based app, the window controller also saves data from its associated document
object.) Figure 2-8 shows the path that Cocoa takes when determining which objects to save. Window objects
are always the starting point, but other related objects are saved, too.
Figure 2-8 Responder objects targeted by Cocoa for preservation
All Cocoa window and view objects save basic information about their size and location, plus information
about other attributes that might affect the way they are currently displayed. For example, a tab view saves
the index of the selected tab, and a text view savesthe location and range of the current textselection. However,
these responder objects do not have any inherent knowledge about your app’s data structures. Therefore, it
is your responsibility to save your app’s data and any additional information needed to restore the window to
its current state. There are several places where you can write out your custom state information:
●
If you subclass NSWindow or NSView, implement the encodeRestorableStateWithCoder: method
in your subclass and use it to write out any relevant data.
Alternatively, your custom responder objects can override the restorableStateKeyPaths method and
use it to specify key paths for any attributes to be preserved. Cocoa uses the key paths to locate and save
the data for the corresponding attribute. Attributes must be compliant with key-value coding and Key-value
observing.
●
If your window has a delegate object, implement the window:willEncodeRestorableState: method
for the delegate and use it to store any relevant data.
●
In your window controller, use the encodeRestorableStateWithCoder: method to save any relevant
data or configuration information.
The Core App Design
Support the Key Runtime Behaviors in Your Apps
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
40Be judicious when deciding what data to preserve, and strive to write out the smallest amount of information
that is required to reconfigure your window and associated objects. You are expected to save the actual data
that the window displays and enough information to reattach the window to the same data objects later.
Important: Never use the user interface preservation mechanism as a way to save your app’s actual data.
The archive created for interface preservation can change frequently and may be ignored altogether if there
is a problem during the restoration process. Your app data should always be saved independently in data
files that are managed by your app.
For information on how to use coder objects to archive state information, see NSCoder Class Reference . For
additional information on what you need to do to save state in a multiwindow document-based app, see
Document-Based App Programming Guide for Mac .
Notifying Cocoa About Changes to Your Interface State
Whenever the preserved state of one of your responder objects changes, mark the object as dirty by calling the
invalidateRestorableState method of that object. Having done so, at some point in the future,
encodeRestorableStateWithCoder: message is sent to your responder object. Marking your responder
objects as dirty lets Cocoa know that it needs to write their preservation state to disk at an appropriate time.
Invalidating your objects is a lightweight operation in itself because the data is not written to disk right away.
Instead, changes are coalesced and written at key times, such as when the user switches to another app or
logs out.
You should mark a responder object as dirty only for changes that are truly interface related. For example, a
tab view marks itself as dirty when the user selects a different tab. However, you do not need to invalidate
your window or its views for many content-related changes, unless the content changes themselves caused
the window to be associated with a completely different set of data-providing objects.
If you used the restorableStateKeyPaths method to declare the attributes you want to preserve, Cocoa
preserves and restores the values of those attributes of your responder object. Therefore, any key paths you
provide should be key-value observing compliant and generate the appropriate notifications. For more information
on how to support key-value observing in your objects, see Key-Value Observing Programming Guide .
Restoring Your Windows and Custom Objects at Launch TIme
As part of your app’s normal launch cycle, Cocoa checks to see whether there is any preserved interface data.
If there is, Cocoa usesthat data to try to re-create your app’s windows. Every window must identify a restoration
class that knows about the window and can act on its behalf at launch time to create the window when asked
to do so by Cocoa.
The Core App Design
Support the Key Runtime Behaviors in Your Apps
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
41The restoration class is responsible for creating both the window and all of the critical objects required by that
window. For most app styles, the restoration class usually creates one or more controller objects as well. For
example, in a single-window app, the restoration class would likely create the window controller used to
manage the window and then retrieve the window from that object. Because it createsthese controller objects
too, you typically use high-level application classesfor your restoration classes. An app might use the application
delegate, a document controller, or even a window controller as a restoration class.
During the launch cycle, Cocoa restores each preserved window as follows:
1. Cocoa retrieves the window’s restoration class from the preserved data and calls its
restoreWindowWithIdentifier:state:completionHandler: class method.
2. The restoreWindowWithIdentifier:state:completionHandler: class method must call the
provided completion handler with the desired window object. To do this, it does one of the following:
●
It creates any relevant controller objects (including the window controller) that might normally be
created to display the window.
●
If the controller objects already exist (perhaps because they were already loaded from a nib file), the
method gets the window from those existing objects.
If the window could not be created, perhaps because the associated document was deleted by the user,
the restoreWindowWithIdentifier:state:completionHandler: should pass an error object to
the completion handler.
3. Cocoa uses the returned window to restore it and any preserved responder objects to their previous state.
● Standard Cocoa window and view objects are restored to their previousstate without additional help.
If you subclass NSWindow or NSView, implement the restoreStateWithCoder: method to restore
any custom state.
If you implemented the restorableStateKeyPaths method in your custom responder objects,
Cocoa automatically sets the value of the associated attributes to their preserved values. Thus, you
do not have to implement the restoreStateWithCoder: to restore these attributes.
● For the window delegate object, Cocoa calls the window:didDecodeRestorableState: method
to restore the state of that object.
● For your window controller, Cocoa calls the restoreStateWithCoder: method to restore its state.
When re-creating each window, Cocoa passes the window’s unique identifier string to the restoration class.
You are responsible for assigning user interface identifier strings to your windows prior to preserving the
window state. You can assign an identifier in your window’s nib file or by setting your window object's
identifier property (defined in NSUserInterfaceItemIdentification protocol). For example, you
might give your preferences window an identifier of preferences and then check for that identifier in your
The Core App Design
Support the Key Runtime Behaviors in Your Apps
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
42implementation. Your restoration class can use this identifier to determine which window and associated
objects it needs to re-create. The contents of an identifier string can be anything you want but should be
something to help you identify the window later.
For a single-window app whose main window controller and window are loaded from the main nib file, the
job of your restoration class is fairly straightforward. Here, you could use the application delegate’s class as
the restoration class and implement the restoreWindowWithIdentifier:state:completionHandler:
method similar to the implementation shown in Listing 2-2. Because the app has only one window, it returns
the main window directly. If you used the application delegate’s class asthe restoration classfor other windows,
your own implementation could use the identifier parameter to determine which window to create.
Listing 2-2 Returning the main window for a single-window app
+ (void)restoreWindowWithIdentifier:(NSString *)identifier
state:(NSCoder *)state
completionHandler:(void (^)(NSWindow *, NSError *))completionHandler
{
// Get the window from the window controller,
// which is stored as an outlet by the delegate.
// Both the app delegate and window controller are
// created when the main nib file is loaded.
MyAppDelegate* appDelegate = (MyAppDelegate*)[[NSApplication sharedApplication]
delegate];
NSWindow* mainWindow = [appDelegate.windowController window];
// Pass the window to the provided completion handler.
completionHandler(mainWindow, nil);
}
Apps Are Built Using Many Different Pieces
The objects of the core architecture are important but are not the only objects you need to consider in your
design. The core objects manage the high-level behavior of your app, but the objects in your app’s view layer
do most of the work to display your custom content and respond to events. Other objects also play important
roles in creating interesting and engaging apps.
The Core App Design
Apps Are Built Using Many Different Pieces
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
43The User Interface
An app’s user interface is made up of a menu bar, one or more windows, and one or more views. The menu
bar is a repository for commands that the user can perform in the app. Commands may apply to the app as a
whole, to the currently active window, or to the currently selected object. You are responsible for defining the
commands that your app supports and for providing the event-handling code to respond to them.
You use windows and views to present your app’s visual content on the screen and to manage the immediate
interactions with that content. A window is an instance of the NSWindow class. A panel is an instance of the
NSPanel class(which is a descendant of NSWindow) that you use to presentsecondary content. Single-window
apps have one main window and may have one or more secondary windows or panels. Multiwindow apps
have multiple windows for displaying their primary content and may have one or more secondary windows
or panels too. The style of a window determines its appearance on the screen. Figure 2-9 shows the menu bar,
along with some standard windows and panels.
Figure 2-9 Windows and menus in an app
A view, an instance of the NSView class, defines the content for a rectangular region of a window. Views are
the primary mechanism for presenting content and interacting with the user and have several responsibilities.
For example:
● Drawing and animation support. Views draw content in their rectangular area. Views that support Core
Animation layers can use those layers to animate their contents.
● Layout and subview management. Each view manages a list ofsubviews, allowing you to create arbitrary
view hierarchies. Each view defineslayout and resizing behaviorsto accommodate changesin the window
size.
The Core App Design
Apps Are Built Using Many Different Pieces
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
44● Event handling. Views receive events. Views forward events to other objects when appropriate.
For information about creating and configuring windows, see Window Programming Guide . For information
about using and creating view hierarchies, see View Programming Guide .
Event Handling
The system window server is responsible for tracking mouse, keyboard, and other events and delivering them
to your app. When the system launches an app, it creates both a process and a single thread for the app. This
initial thread becomes the app’s main thread. In it, the NSApplication object sets up the main run loop and
configures its event-handling code, as shown in Figure 2-10. As the window server delivers events, the app
queues those events and then processes them sequentially in the app’s main run loop. Processing an event
involves dispatching the event to the object best suited to handle it. For example, mouse events are usually
dispatched to the view in which the event occurred.
Figure 2-10 Processing events in the main run loop
Note: A run loop monitors sources of input on a specific thread of execution. The app’s event queue
represents one of these inputsources. While the event queue is empty, the main thread sleeps. When
an event arrives, the run loop wakes up the thread and dispatches control to the NSApplication
object to handle the event. After the event has been handled, control passes back to the run loop,
which can then process another event, process other input sources, or put the thread back to sleep
if there is nothing more to do. For more information about how run loops and input sources work,
see Threading Programming Guide .
Distributing and handling events is the job of responder objects, which are instances of the NSResponder
class. The NSApplication, NSWindow, NSDrawer, NSView, NSWindowController, and NSViewController
classes are all descendants of NSResponder. After pulling an event from the event queue, the app dispatches
The Core App Design
Apps Are Built Using Many Different Pieces
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
45that event to the window object where it occurred. The window object, in turn, forwards the event to its first
responder. In the case of mouse events, the first responder is typically the view object (NSView) in which the
touch took place. For example, a mouse event occurring in a button is delivered to the corresponding button
object.
If the first responder is unable to handle an event, it forwardsthe event to its nextresponder, which istypically
a parent view, view controller, or window. If that object is unable to handle the event, it forwards it to its next
responder, and so , until the event is handled. Thisseries of linked responder objectsis known asthe responder
chain. Messages continue traveling up the responder chain—toward higher-level responder objects, such as
a window controller or the application object—until the event is handled. If the event isn't handled, it is
discarded.
The responder object that handles an event often sets in motion a series of programmatic actions by the app.
For example, a control object (that is, a subclass of NSControl) handles an event by sending an action message
to another object, typically the controller that manages the current set of active views. While processing the
action message, the controller might change the user interface or adjust the position of views in ways that
require some of those views to redraw themselves. When this happens, the view and graphics infrastructure
takes over and processes the required redraw events in the most efficient manner possible.
For more information about responders, the responder chain, and handling events, see Cocoa Event Handling
Guide .
Graphics, Drawing, and Printing
There are two basic ways in which a Mac app can draw its content:
● Native drawing technologies (such as Core Graphics and AppKit)
● OpenGL
The native OS X drawing technologies typically use the infrastructure provided by Cocoa views and windows
to render and present custom content. When a view is first shown, the system asks it to draw its content.
System views draw their contents automatically, but custom views must implement a drawRect: method.
Inside this method, you use the native drawing technologies to draw shapes, text, images, gradients, or any
other visual content you want. When you want to update your view’s visual content, you mark all or part of
the view invalid by calling its setNeedsDisplay: or setNeedsDisplayInRect: method. The system then
calls your view’s drawRect: method (at an appropriate time) to accommodate the update. This cycle then
repeats and continues throughout the lifetime of your app.
If you are using OpenGL to draw your app’s content, you still create a window and view to manage your content,
but those objects simply provide the rendering surface for an OpenGL drawing context. Once you have that
drawing context, your app is responsible for initiating drawing updates at appropriate intervals.
The Core App Design
Apps Are Built Using Many Different Pieces
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
46For information about how to draw custom content in your views, see Cocoa Drawing Guide .
Text Handling
The Cocoa text system, the primary text-handling system in OS X, is responsible for the processing and display
of all visible text in Cocoa. It provides a complete set of high-quality typographical services through the
text-related AppKit classes, which enable apps to create, edit, display, and store text with all the characteristics
of fine typesetting.
The Cocoa text system provides all these basic and advanced text-handling features, and it also satisfies
additional requirements from the ever-more-interconnected computing world: support for the character sets
of all of the world’s living languages, powerful layout capabilities to handle various text directionality and
nonrectangular text containers, and sophisticated typesetting capabilities such as control of kerning, ligatures,
line breaking, and justification. Cocoa’s object-oriented textsystem is designed to provide all these capabilities
without requiring you to learn about or interact with more of the system than is necessary to meet the needs
of your app.
Underlying the Cocoa text system is Core Text, which provides low-level, basic text layout and font-handling
capabilities to higher-level engines such as Cocoa and WebKit. Core Text provides the implementation for
many Cocoa text technologies. App developers typically have no need to use Core Text directly. However, the
Core Text API is accessible to developers who must use it directly, such as those writing apps with their own
layout engine and those porting older ATSUI- or QuickDraw-based codebases to the modern world.
For more information about the Cocoa text system, see Cocoa Text Architecture Guide .
Implementing the Application Menu Bar
The classes NSMenu and NSMenuItem are the basis for all types of menus. An instance of NSMenu manages a
collection of menu items and draws them one beneath another. An instance of NSMenuItem represents a
menu item; it encapsulates all the information its NSMenu object needs to draw and manage it, but does no
drawing or event-handling itself. You typically use Interface Builder to create and modify any type of menu,
so often there is no need to write any code.
The application menu bar stretches across the top of the screen, replacing the menu bar of any other app
when the app is foremost. All of an app’s menus in the menu bar are owned by one NSMenu instance that’s
created by the app when it starts up.
The Core App Design
Implementing the Application Menu Bar
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
47Xcode Templates Provide the Menu Bar
Xcode’s Cocoa application templates provide that NSMenu instance in a nib file called MainMenu.xib. This
nib file contains an application menu (named with the app’s name), a File menu (with all of its associated
commands), an Edit menu (with text editing commands and Undo and Redo menu items), and Format, View,
Window, and Help menus (with their own menu items representing commands). These menu items, as well
as all of the menu items of the File menu, are connected to the appropriate first-responder action methods.
For example, the About menu item is connected to the orderFrontStandardAboutPanel: action method
in the File’s Owner that displays a standard About window.
The template has similar ready-made connections for the Edit, Format, View, Window, and Help menus. If your
app does not support any of the supplied actions (for example, printing), you should remove the associated
menu items (or menu) from the nib. Alternatively, you may want to repurpose and rename menu commands
and action methodsto suit your own app, taking advantage of the menu mechanism in the template to ensure
that everything is in the right place.
Connect Menu Items to Your Code or Your First Responder
For your app’s custom menu items that are not already connected to action methods in objects or placeholder
objects in the nib file, there are two common techniques for handling menu commands in a Mac app:
● Connect the corresponding menu item to a first responder method.
● Connect the menu item to a method of your custom application object or your application delegate object.
Of these two techniques, the first is more common given that many menu commands act on the current
document or its contents, which are part of the responder chain. The second technique is used primarily to
handle commands that are global to the app, such as displaying preferences or creating a new document. It
is possible for a custom application object or its delegate to dispatch events to documents, but doing so is
generally more cumbersome and prone to errors. In addition to implementing action methods to respond to
your menu commands, you must also implement the methods of the NSMenuValidation protocol to enable
the menu items for those commands.
Step-by-step instructions for connecting menu items to action methods in your code are given in “Designing
User Interfaces in Xcode”. For more information about menu validation and other menu topics, see Application
Menu and Pop-up List Programming Topics.
The Core App Design
Implementing the Application Menu Bar
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
48Enabling a window of your app to assume full-screen mode, taking over the entire screen, provides users with
a more immersive, cinematic experience. Full-screen appearance can be striking and can make your app stand
out. From a practical standpoint, full-screen mode presents a better view of users’ data, enabling them to
concentrate fully on their content without the distractions of other apps or the desktop.
In full-screen mode, by default, the menu bar and Dock are autohidden; that is, they are normally hidden but
reappear when the user moves the pointer to the top or bottom of the screen. A full-screen window does not
draw its titlebar and may have special handling for its toolbar.
The full-screen experience makes sense for many apps but not for all. For example, the Finder, Address Book,
and Calculator would not provide any benefit to users by assuming full-screen mode. The same is probably
true for most utility apps. Media-rich apps, on the other hand, can often benefit from full-screen presentation.
Beginning with OS X v10.7, Cocoa includes support for full-screen mode through APIs in NSApplication,
NSWindow, and NSWindowDelegate protocol. When the user chooses to enter full-screen mode, Cocoa
dynamically creates a space and puts the window into that space. This behavior enables the user to have one
window of an app running in full-screen mode in one space, while using other windows of that app, as well
as other apps, on the desktop in otherspaces. While in full-screen mode, the user can switch between windows
in the current app or switch apps.
Apps that have implemented full-screen user interfaces in previous versions of OS X should consider
standardizing on the full-screen APIs in OS X v10.7.
Full-Screen API in NSApplication
Full-screen support in NSApplication is provided by the presentation option
NSApplicationPresentationFullScreen. You can find the current presentation mode via the
NSApplication method currentSystemPresentationOptions, which is also key-value observable. You
can set the presentation options using the NSApplication method setPresentationOptions:. (Be sure
to observe the restrictions on presentation option combinations documented with
NSApplicationPresentationOptions, and set the presentation optionsin a try-catch block to ensure that
your program does not crash from an invalid combination of options.)
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
49
Implementing the Full-Screen ExperienceA window delegate may also specify that the window toolbar be removed from the window in full-screen
mode and be shown automatically with the menu bar by including
NSApplicationPresentationAutoHideToolbar in the presentation options returned from the
window:willUseFullScreenPresentationOptions: method of NSWindowDelegate.
Full-Screen API in NSWindow
The app must specify whether a given window can enter full-screen mode. Apps can set collection behavior
using the setCollectionBehavior: method by passing in various constants, and the current options may
be accessed via the collectionBehavior method. You can choose between two constants to override the
window collection behavior, as shown in the following table:
Constant Behavior
The frontmost window with this collection behavior becomes
the full-screen window. A window with this collection
behavior has a full-screen button in the upper right of its
titlebar.
NSWindowCollectionBehaviorFullScreenPrimary
Windows with this collection behavior can be shown in the
same space with the full-screen window.
NSWindowCollectionBehaviorFullScreenAuxiliary
When a window goesinto full-screen mode, the styleMask changesto NSFullScreenWindowMask to reflect
the state of the window.The setting of the styleMask goesthrough the setStyleMask: method. As a result,
a window can override this method if it has customization to do when entering or exiting full-screen.
A window can be taken into or out of full-screen mode using the toggleFullScreen: method. If an app
supports full-screen mode, it should add a menu item to the View menu with toggleFullScreen: as the
action, and nil as the target.
Full-Screen API in NSWindowDelegate Protocol
The following notifications are sent before and after the window enters and exits full-screen mode:
NSWindowWillEnterFullScreenNotification
NSWindowDidEnterFullScreenNotification
NSWindowWillExitFullScreenNotification
NSWindowDidExitFullScreenNotification
Implementing the Full-Screen Experience
Full-Screen API in NSWindow
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
50The window delegate has the following corresponding window delegate notification methods:
windowWillEnterFullScreen:
windowDidEnterFullScreen:
windowWillExitFullScreen:
windowDidExitFullScreen:
The NSWindowDelegate protocol methods supporting full-screen mode are listed in Table 3-1.
Table 3-1 Window delegate methods supporting full-screen mode
Method Description
Invoked to allow the delegate
to modify the full-screen
content size.
window: willUseFullScreenContentSize:
Returns the presentation
options the window will use
when transitioning to
full-screen mode.
window: willUseFullScreenPresentationOptions:
Invoked when the window is
about to enter full-screen mode.
The window delegate can
implement this method to
customize the animation by
returning a custom window or
array of windows containing
layers or other effects.
customWindowsToEnterFullScreenForWindow:
The system has started its
animation into full-screen
mode, including transitioning
into a new space. You can
implement this method to
perform custom animation with
the given duration to be in sync
with the system animation.
window:
startCustomAnimationToEnterFullScreenWithDuration:
Invoked if the window failed to
enter full-screen mode.
windowDidFailToEnterFullScreen:
Implementing the Full-Screen Experience
Full-Screen API in NSWindowDelegate Protocol
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
51Method Description
Invoked when the window is
about to exit full-screen mode.
The window delegate can
implement this method to
customize the animation when
the window is about to exit
full-screen by returning a
custom window or array of
windows containing layers or
other effects.
customWindowsToExitFullScreenForWindow:
The system has started its
animation out of full-screen,
including transitioning back to
the desktop space. You can
implement this method to
perform custom animation with
the given duration to be in sync
with the system animation.
window:
startCustomAnimationToExitFullScreenWithDuration:
Invoked if the window failed to
exit full-screen mode.
windowDidFailToExitFullScreen:
For more information about full-screen mode, see NSWindowDelegate Protocol Reference and the OS X Human
Interface Guidelines.
Implementing the Full-Screen Experience
Full-Screen API in NSWindowDelegate Protocol
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
52During the design phase of creating your app, you need to think about how to implement certain features
that users expect in well-formed Mac apps. Integrating these features into your app architecture can have an
impact on the data model or may require cooperation between significantly different portions in the app.
You Can Prevent the Automatic Relaunch of Your App
By default, as part of the Resume feature of OS X v10.7, apps that were open at logout are relaunched by the
system when the user logsin again. You can prevent the automatic relaunching of your app at login by sending
it a disableRelaunchOnLogin message. This NSApplication method increments a counter that controls
the app being relaunched; if the counter is 0 at the time the user logs out, then the app is relaunched when
the user logs back in. The counter is initially zero, providing the default relaunch behavior.
Your can reinstate automatic relaunching of your app by sending it an enableRelaunchOnLogin message.
This message decrements the relaunch counter, so an equal number of disableRelaunchOnLogin and
enableRelaunchOnLogin messages enables relaunching. Both methods are thread safe.
If your app should not be relaunched because it launches via some other mechanism, such as the launchd
system process, then the recommended practice is to send the app a disableRelaunchOnLogin message
once, and never pair it with an enableRelaunchOnLogin message.
If your app should not be relaunched because it triggers a restart (for example, if it is an installer), then the
recommended practice is to send it a disableRelaunchOnLogin message immediately before you attempt
to trigger a restart and send it an enableRelaunchOnLogin message immediately after. This procedure
handles the case where the user cancels restarting; if the user later restarts for another reason, then your app
should be relaunched.
Making Your App Accessible Enables Many Users
Millions of people have a disability or special need. These include visual and hearing impairments, physical
disabilities, and cognitive and learning challenges. Access to computers is vitally important for this population,
because computers can provide a level of independence that is difficult to attain any other way. As populations
around the world age, an increasing number of people will experience age-related disabilities, such as vision
or hearing loss. Current and future generations of the elderly will expect to be able to continue using their
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
53
Supporting Common App Behaviorscomputers and accessing their data, regardless of the state of their vision and hearing. Apps that support
customizable text displays, access by a screen reader, or the replacement of visual cues by audible ones can
serve this population well.
OS X is designed to accommodate assistive technologies and has many built-in features to help people with
disabilities. Users access most of this functionality through the Universal Access pane of System Preferences.
Some of these built-in technologies take advantage of the same accessibility architecture that allows external
assistive technologies to access your app. Designing your app with accessibility in mind not only allows you
to reach a larger group of users, it results in a better experience for all your users.
As a first step in designing your app, be sure to read OS X Human Interface Guidelines. That book provides
detailed specifications and best practices for designing and implementing an intuitive, consistent, and
aesthetically pleasing user interface that delivers the superlative experience Macintosh users have come to
expect. During the design process, you also should be aware of the accessibility perspective on many basic
design considerations. Consider the following basic accessibility-related design principles:
● Support full keyboard navigation. For many users, a mouse is difficult, if not impossible, to use.
Consequently, a user should be able to perform all your app’s functions using the keyboard alone.
● Don’t override built-in keyboard shortcuts. This applies both to the keyboard shortcuts OS X reserves
(listed in “Keyboard Shortcuts”) and to the accessibility-related keyboard shortcuts (listed in “Accessibility
Keyboard Shortcuts”). As a general rule, you should never override reserved keyboard shortcuts. In particular,
you should take care not to override accessibility-related keyboard shortcuts or your app will not be
accessible to users who enable full keyboard access.
A corollary to this principle is to avoid creating too many new keyboard shortcuts that are specific to your
app. Users should not have to memorize a new set of keyboard commands for each app they use.
● Provide alternatives for drag-and-drop operations. If your app relies on drag-and-drop operations in its
workflow, you should provide alternate ways to accomplish the same tasks. This may not be easy; in fact,
it may require the design of an alternate interface for apps that are heavily dependent on drag and drop.
● Make sure there’s always a way out of your app’s workflow. This is important for all users, of course,
but it’s essential for users of assistive technologies. A user relying on an assistive app to use your app may
have a somewhat narrower view of your app’s user interface. For this reason, it’s especially important to
make canceling operations and retracing steps easy.
In addition to the basic design principles, you should consider the requirements of specific disabilities and
resulting design solutions and adaptations you can implement. The main theme of these suggestions is to
provide as many alternate modes of content display as possible, so that users can find the way that suits their
needs best. Consider the following categories of disabilities:
Supporting Common App Behaviors
Making Your App Accessible Enables Many Users
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
54● Visual Disabilities. Visual disabilities include blindness, color blindness, and low vision. Make your app
accessible to assistive apps, such as screen readers. Ensure that color is not the only source of come
particular information in your user interface. Provide an audio option for all visual cues and feedback, as
well as succinct descriptions of images and animated content.
● Hearing Disabilities. When you design the audio output of your app, remember that some users may not
be able to hear your app’s sound effects well or at all. And, of course, there are situations in which any
user could not use audio output effectively, such as in a library. Providing redundant audio and visual
output can aid comprehension for users with other types of disabilities as well, such as cognitive and
learning disabilities.
● Motor and Cognitive Disabilities. People with motor disabilities may need to use alternatives to the
standard mouse and keyboard input devices. Other users may have difficulty with the fine motor control
required to double-click a mouse or to press key combinations on the keyboard. Users with cognitive or
learning disabilities may need extra time to complete tasks or respond to alerts.
OS X providessupport for many types of disabilities at the system level through solutions offered in the Universal
Access system preferences, illustrated in Figure 4-1. In addition, most standard Cocoa objects implement
accessibility through the NSAccessibility protocol, providing reasonable default behavior in most cases,
Cocoa apps built with standard objects are automatically accessible. In general, you need to explicitly implement
the NSAccessibility protocol methods only if you subclass one of them, adding new behavior.
Figure 4-1 Universal Access system preference dialog
Supporting Common App Behaviors
Making Your App Accessible Enables Many Users
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
55The NSAccessibility informal protocol defines methods that Cocoa classes must implement to make
themselves available to an external assistive app. An assistive app interacts with your app to allow persons
with disabilities to use your app. For example, a person with a visual impairment could use an app to convert
menu items and button labels into speech and then perform actions by verbal command.
An accessible object is described by a set of attributes that define characteristics such as the object type, its
value, its size and position on the screen, and its place in the accessibility hierarchy. For some objects, the set
of attributes can include parameterized attributes. Parameterized attributes behave similar to a function by
allowing you to pass a parameter when requesting an attribute value.
For more information, see Accessibility Overview for OS X .
Provide User Preferences for Customization
Preferences are settings that enable users to customize the appearance or behavior of your software. The OS
X user defaultssystem lets you access and manage user preferences. You can use the defaultssystem to provide
reasonable initial values for app settings, as well as save and retrieve the user's own preference selections
across sessions. The Cocoa NSUserDefaults class provides programmatic access to the user defaults system.
The NSUserDefaults class provides convenience methodsfor accessing common typessuch asfloats, doubles,
integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections
a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you
want to store any other type of object, you should typically archive it to create an instance of NSData.
The user defaults system groups defaults into domains. Two of the domains are persistently saved in the user
defaults database: the app domain stores app-specific defaults, and the global domain stores defaults applicable
to all apps. In addition, the user defaults system provides three volatile domains whose values last only while
the user defaults object exists: the argument domain for defaults set from command-line arguments, the
languages domain containing defaults for a locale if one is specified, and the registration domain for “factory
defaults” registered by the app.
Your app uses the NSUserDefaults class to register default preferences, and typically it also provides a user
interface (a preferences panel) that enables the user to change those preferences. Preferences are stored in
the ~/Library/Preferences/ folder in a standard XML-format property list file as a set of key-value pairs.
The app-specific preferences property list is identified by the bundle identifier of the app.
For more details, see Preferences and Settings Programming Guide .
Supporting Common App Behaviors
Provide User Preferences for Customization
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
56Integrate Your App With Spotlight Search
Spotlight is a fast desktop search technology that allows users to organize and search for files based on
metadata. Metadata is data about a file, rather than the actual content stored in the file. Metadata can include
familiar information such as an asset’s author and modification date but it can also be keywords or other
information that is custom to a particular asset. For example, an image file might have metadata describing
the image’s dimensions and color model.
Developers of appsthatsave documentsto disk should consider providing Spotlightsupport by implementing
a metadata importer. A Spotlight metadata importer is a small plug-in bundle that you create to extract
information from files created by your app. Spotlight importers are used by the Spotlight server to gather
information about new and existing files, as illustrated in Figure 4-2.
Figure 4-2 Spotlight extracting metadata
Apple provides importers for many standard file types that the system uses, including RTF, JPEG, Mail, PDF and
MP3. However, if you define a custom document format, you must create a metadata importer for your own
content. Xcode provides a template for writing Spotlight importer plug-ins. For information about writing a
Spotlight importer, see Spotlight Importer Programming Guide .
In addition, you may wish to provide metadata search capability in your app. Cocoa provides the
NSMetadataQuery class which enables you to construct queries using a subset of the NSPredicate classes
and execute the queries asynchronously. For information about providing Spotlight search capability in your
app, see File Metadata Search Programming Guide .
For more information about Spotlight, see Spotlight Overview.
Supporting Common App Behaviors
Integrate Your App With Spotlight Search
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
57Use Services to Increase Your App’s Usefulness
Services allow a user to access the functionality of one app from within another app. An app that provides a
service advertises the operations it can perform on a particular type of data—for example, encryption of text,
optical character recognition of a bitmapped image, or generating text such as a message of the day. When
the user is manipulating that particular type of data in some app, the user can choose the appropriate item in
the Services menu to operate on the current data selection (or merely insert new data into the document).
See Services Implementation Guide for more information.
Optimize for High Resolution
High-resolution displays provide a rich visual experience, allowing users to see sharper text and more details
in photos than on standard-resolution displays. The high-resolution model for OS X enables your app to draw
into an abstract coordinate space called user space, without regard for the characteristics of the final drawing
destination—printer, display screen, bitmap, or PDF—and without regard to the resolution of the display.
OS X provides much support for high-resolution automatically. For example,standard AppKit views and controls
automatically render correctly at any resolution, vector-based content automatically uses additional pixels to
rendersharper lines and shapes, Cocoa text displayssharper in high-resolution, and AppKit automatically loads
high-resolution variants of your images.
You must do the following things to optimize your app for high-resolution:
● Provide properly-named high-resolution versions of your bitmapped images.
● Use high-resolution-savvy image-loading methods.
● Use the most recent APIs that support high resolution.
These techniques are described in the following sections.
Think About Points, Not Pixels
OS X refers to screen size in points, not pixels. A point is one unit in user space, prior to any transformations
on the space. Because, on a high-resolution display, there are four onscreen pixels for each point, points can
be expressed asfloating-point values. Valuesthat are integersin standard resolution,such as mouse coordinates,
are floating-point values on a high-resolution display, allowing for greater precision for such things as graphics
alignment.
Supporting Common App Behaviors
Use Services to Increase Your App’s Usefulness
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
58Your app draws to a view using points in user space. The window server composites drawing operations to an
offscreen buffer called the backing store. When it comes time to display the contents of the backing store
onscreen, the window server scales the content appropriately, mapping points to onscreen pixels. The result
is that if you draw the same content on two similar devices, and only one of them has a high-resolution screen,
the content appears to be about the same size on both devices, as shown in Figure 4-3.
Figure 4-3 Content appears the same size at standard resolution and high resolution
In some situations you may need to know how points are mapped to pixels, in which case you can obtain the
backing scale factor which is always either 1.0 or 2.0. The backing scale factor is a property of a layer, view,
or window, and it depends on the resolution of the underlying display device.
Provide High-Resolution Versions of Graphics
OS X automatically magnifies standard-resolution bitmapped images so they appear to the user in the correct
size, but they appear fuzzy. To avoid this problem, you must provide high-resolution versions of your graphics,
along with the standard-resolution versions in the app bundle. In addition to any images your app displays,
you must do this for your app’s icons and any custom controls, cursors, and other artwork.
High-resolution graphics must be scaled with twice as many pixels in each dimension to display the same size
in user space. For example, if you supply a standard-resolution image sized at 50x50 pixels, the high-resolution
version must be sized at 100x100 pixels.
For AppKit to recognize and load high-resolution versions of your graphics at appropriate times, you must
adopt the naming convention of appending @2x to the image name. For example, a standard-resolution image
named circle.png would have a high-resolution counterpart named circle@2x.png. Ideally, you can
package both image versions into a single TIFF file. This is most easily done by setting the Xcode option
Combine High Resolution Artwork to Yes in Target Build Settings under Deployment.
Supporting Common App Behaviors
Optimize for High Resolution
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
59You should create a set of icons for your app consisting of standard- and high-resolution versions of each icon
size—16x16, 32x32, 128x128, 256x256, 512x512 appending @2x to the icon image name which, by convention,
specifies the icon size in user space points. For example, an icon named icon_16x16.png would have a
high-resolution counterpart named icon_16x16@2x.png, the icon_32x32.png size would have a version
named icon_32x32@2x.png, and so on.
After you’ve created your set of app icons, place them in folder a named icon.iconset. Then you can use
the iconutil command-line tool to convert your .iconset folder into a single, deployment-ready,
high-resolution .icns file.
Use High-Resolution-Savvy Image-Loading Methods
If you follow the @2x naming convention, there are two methods available that load standard- and
high-resolution versions of an image into an NSImage object, whether or not you provide a multirepresentation
image file:
● The NSImage method imageNamed: finds resources located in the main application bundle, but not in
frameworks or plug-ins.
● The NSBundle method imageForResource: looks for resources outside as well as inside the main
bundle. Framework authors should use this method.
To create an NSImage object from a CGImageRef data type, use the initWithCGImage:size: method,
specifying the image size in points, not pixels. For custom cursors, you can pass a multirepresentation TIFF to
the NSCursor class method initWithImage:hotSpot:.
Use APIs That Support High Resolution
Cocoa apps must replace deprecated APIs with their newer counterparts. Apps that use older technologies
need to replace those technologies with newer ones. NSImage, NSView, NSWindow, and NSScreen classes
have methods that support high resolution, including methods for converting geometry, detecting scaling,
and aligning pixels. These APIs and deprecated technologiesthat youmust avoid are described in High Resolution
Guidelines for OS X .
You should also consider whether your app requires further adjustments for special scenarios, such as using
pixel-based technologies (OpenGL, Quartz bitmaps) or custom Core Animation layers. These advanced
optimization techniques are described in High Resolution Guidelines for OS X , which also provides much more
detailed information about high resolution.
Supporting Common App Behaviors
Optimize for High Resolution
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
60Prepare for Fast User Switching
Fast user switching lets users share a single machine without having to log out every time they want to access
their user account. Users share physical access to the machine, including the same keyboard, mouse, and
monitor. However, instead of logging out, a new user simply logs in and switches out the previous user.
Processes in a switched-out login session continue running as before. They can continue processing data,
communicating with the system, and drawing to the screen buffer as before. However, because they are
switched out, they do not receive input from the keyboard and mouse. Similarly, if they were to check, the
monitor would appear to be in sleep mode. As a result, it may benefit some apps to adjust their behavior while
in a switched-out state to avoid wasting system resources.
While fast userswitching is a convenient feature for users, it does provide several challengesfor app developers.
Apps that rely on exclusive access to certain resources may need to modify their behavior to live in a fast user
switching environment. For example, an app that stores temporary data in /tmp may run into problems when
a second instance running under a different user tries to modify the same files in that directory.
To support fast user switching, there are certain guidelines you should follow in developing your apps, most
of which describe safe waysto identify and share system resources. A summary of these guidelinesis asfollows:
●
Incorporate session ID information into the name of any entity that appears in a global namespace,
including file names, shared memory regions, semaphores, and named pipes.
Tagging such app-specific resources with a session ID is necessary to distinguish them from similar resources
created by apps in a different session. The Security layer of the system assigns a unique ID to each login
session. Incorporating thisID into cache file or temporary directory names can prevent namespace collisions
when creating these files. See “Supporting Fast User Switching” for information on how to get the session
ID.
● Don't assume you have exclusive access to any resource, especially TCP/IP ports and hardware devices.
● Don't assume there is only one instance of a per-user service running.
● Use file-level or range-level locks when accessing files.
● Accept and handle user switch notifications. See “User Switch Notifications” for more information.
For more information on user switching, see Multiple User Environment Programming Topics.
Supporting Common App Behaviors
Prepare for Fast User Switching
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
61Take Advantage of the Dock
The Dock is a desktop app designed to reduce desktop clutter, provide users with feedback about an app, and
allow users to switch easily between tasks. You can customize your app Dock tile by modifying the Dock icon
and adding items to the menu displayed for your app, and you can customize the Dock icon of a minimized
window.
An app’s Dock icon is, by default, the app’s icon. While your app is running, you can modify or replace the
default icon with another image that indicates the current state of your app. For example, the icon for Mail
changes when messages are waiting to be read. A badge—the red circle and number in the figure—is overlaid
onto Mail’s app icon to indicate the number of unread messages. The number changes each time Mail retrieves
more messages.
When the user holds the Control key down and clicks on a Dock tile, a menu appears. If your app does nothing
to customize the menu, the Dock tile’s menu contains a list of the app’s open documents (if any), followed by
the standard menu items Keep in Dock, Open at Login, Show in Finder, Hide, and Quit. You can add other
menu itemsto the Dock menu, eitherstatically by providing a custom menu nib file, or dynamically by overriding
the application delegate’s applicationDockMenu: method.
You can also customize a dock tile when your app is not currently running by creating a Dock tile plug-in that
can update the Dock tile icon and menu. For example, you may want to update the badge text to indicate that
new content will be available the next time the app is launched, and you may want to customize the app’s
Dock menu to deal with the new content.
For information explaining how to customize your app’s Dock icon and menu, see Dock Tile Programming
Guide .
Supporting Common App Behaviors
Take Advantage of the Dock
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
62Configuring your app properly is an important part of the development process. Mac apps use a structured
directory configuration to manage their code and resource files. And although most of the files are custom
and are there to support your app, some are required by the system or the App Store and must be configured
properly.
If you intend to sell your application through the Mac App Store or use iCloud storage, you also need to create
an explicit app ID, create provisioning profiles, and enable the correct entitlements for your application. These
procedures are explained in Tools Workflow Guide for Mac .
All of the requirements for preparing your app and distributing it on the App Store are described in the App
Store Resource Center.
Configuring Your Xcode Project
To develop a Mac app, you create an Xcode project. An Xcode Project is a repository for all the files, resources,
and information required to build your app (or one of a number of othersoftware products). A project contains
all the elements used to build your app and maintains the relationships between those elements. It contains
one or more targets, which specify how to build the software. A project defines default build settings for all
the targets in the project (each target can also specify its own build settings, which override the project build
settings).
You create a new project using the Xcode File > New > New Project menu command, which invokes the New
Project dialog. This dialog enables you to choose a template for your project, such as a Cocoa app, to name it,
and to locate it in your file system. The New Project dialog also provides options, so you can specify whether
your app uses the Cocoa document architecture or the Core Data framework. When you save your project,
Xcode lets you to create a local Git repository to enable source code control for your project.
If you have two or more closely related projects, you should create an Xcode Workspace and add your projects
to it. A workspace groups projects and other documents so you can work on them together. One project can
use the products and shared libraries of another project while building, and Xcode does indexing across the
entire workspace, extending the scope of content-aware features such as code completion.
Once you have created your project, you write and edit your code using the Xcode source editor. You also use
Xcode to build and debug your code, setting breakpoints, viewing the values of variables, stepping through
running code, and reviewing issues found during builds or code analysis.
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
63
Build-Time Configuration DetailsWhen you create a new project, it includes one or more targets, where each target specifies one build product
and the instructions for how the product is to be built. Most developers never need to change the default of
the vast majority of build settings, but there are a few basic settings that you should check for each target,
such as the deployment target (platform, OS version, and architecture), main user interface, and linked
frameworks and libraries.
You also need to set up one or more schemes to specify the targets, build configuration, and executable
configuration to use when the product specified by the target is launched. You use the project editor and the
scheme editing dialog to edit build settings and control what happens when you build and run your code.
“Building and Running Your Code” explains how to work with Xcode build settings and schemes.
For more information about using Xcode to create, configure, build, and debug your project, as well as archiving
your program to package it for distribution or submission to the Mac App Store, see Xcode 4 User Guide .
The Information Property List File
An information property list (Info.plist) file contains essential runtime-configuration information for the app.
Xcode provides a version of this file with every Mac application project and configures it with several standard
keys. Although the default keys cover several important aspects of the app’s configuration, most apps require
additional keys to specify their configuration fully.
Build-Time Configuration Details
The Information Property List File
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
64To view the contents of your Info.plist file, select it in the Groups & Files pane. Xcode displays a property
list editor similar to the one in Figure 5-1 (which is from Xcode 3.2.5). You can use this window to edit the
property values and add new key-value pairs. By default, Xcode displays a more user-friendly version of each
key name. To see the key names that Xcode adds to the Info.plist file, Control-click an item in the editor
and choose Show Raw Keys/Values from the contextual menu that appears.
Figure 5-1 The information property list editor
Xcode automatically addssome important keysto the Info.plist file of all new projects and setstheir initial
values. However, there are several keys that Mac apps use commonly to configure their launch environment
and runtime behavior. Here are some keysthat you might want to add to your app’s Info.plist file specifically
for OS X:
● LSApplicationCategoryType (required for apps distributed using the App Store)
● CFBundleDisplayName
● LSHasLocalizedDisplayName
● NSHumanReadableCopyright
● LSMinimumSystemVersion
● UTExportedTypeDeclarations
● UTImportedTypeDeclarations
Build-Time Configuration Details
The Information Property List File
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
65For detailed information about these and other keys that you can include in your app’s Info.plist file, see
Information Property List Key Reference .
The OS X Application Bundle
When you build your Mac app, Xcode packages it as a bundle. A bundle is a directory in the file system that
groupsrelated resourcestogether in one place. A Mac app bundle contains a single Contents directory, inside
of which are additional files and directories with the app’s code, resources, and localized content. Table 5-1
liststhe contents of a typical Mac app bundle, which for demonstration purposesis called MyApp. This example
is for illustrative purposes only. Some of the files listed in this table may not appear in your own application
bundles.
Table 5-1 A typical application bundle
Files Description
The executable file containing your
app’s code. The name of this file is the
same as your app name minus the
.app extension.
This file is required.
Contents/MacOS/MyApp
Also known as the information property
list file, a file containing configuration
data for the app. The system uses this
data to determine how to interact with
the app at specific times.
This file is required. For more
information, see “The Information
Property List File” (page 64).
Contents/Info.plist
Build-Time Configuration Details
The OS X Application Bundle
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
66Files Description
The English version of the app’s main
nib file. This file contains the default
interface objectsto load at app launch
time. Typically, this nib file contains
the app’s menu bar and application
delegate object. It may also contain
other controller objects that should
always be available at launch time.
(The name of the main nib file can be
changed by assigning a different value
to the NSMainNibFile key in the
Info.plist file.)
Thisfile is optional but recommended.
For more information, see “The
Information Property List File” (page
64)
Contents/Resources/English.lproj/MainMenu.nib
Nonlocalized resources are placed at
the top level of the Resources
directory (sun.png represents a
nonlocalized image file in the
example). The app uses nonlocalized
resources when no localized version
of the same resource is provided. Thus,
you can use these files in situations
where the resource is the same for all
localizations.
Contents/Resources/sun.png (or other resource files)
Build-Time Configuration Details
The OS X Application Bundle
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
67Files Description
Localized resources are placed in
subdirectories with an ISO 639-1
language abbreviation for a name plus
an .lproj suffix. Although more
human readable names (such as
English.lproj, French.lproj, and
German.lproj) can be used for
directory names, the ISO 639-1 names
are preferred because they allow you
to include an ISO 3166-1 regional
designation. (For example, the
en_GB.lproj directory contains
resources localized for English as
spoken in Great Britain, the es.lproj
directory contains resources localized
for Spanish, and the de.lproj
directory contains resources localized
for German.)
Contents/Resources/en_GB.lproj
Contents/Resources/es.lproj
Contents/Resources/de.lproj
Other language-specific project directories
A Mac app should be internationalized and have a language.lproj directory for each language it supports.
Even if you provide localized versions of your resources, though, include a default version of these files at the
top level of your Resources directory. The default version is used when a specific localization is not available.
At runtime, you can access your app’s resource files from your code using the following steps:
1. Obtain a reference to your app’s main bundle object (typically using theNSBundle class).
2. Use the methods of the bundle object to obtain a file-system path to the desired resource file.
3. Open (or access) the file and use it.
You obtain a reference to your app’s main bundle using the mainBundle class method of NSBundle. The
pathForResource:ofType: method is one of several NSBundle methods that you can use to retrieve the
location of resources. The following example shows how to locate a file called sun.png and create an image
object using it. The first line getsthe bundle object and the path to the file. The second line creates an NSImage
object that you could use to display the image in your app.
NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"sun" ofType:@"png"];
NSImage* sunImage = [[NSImage alloc] initWithContentsOfFile:imagePath];
Build-Time Configuration Details
The OS X Application Bundle
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
68Note: If you prefer to use Core Foundation to access bundles, you can obtain a CFBundleRef
opaque type using the CFBundleGetMainBundle function. You can then use that opaque type
plus the Core Foundation functions to locate any bundle resources.
For information on how to access and use resources in your app, see Resource Programming Guide . For more
information about the structure of a Mac app bundle, see Bundle Programming Guide .
Internationalizing Your App
The process of preparing a project to handle content in different languages is called internationalization. The
process of converting text, images, and other content into other languages is called localization. Project
resources that are candidates for localization include:
● Code-generated text, including locale-specific aspects of date, time, and number formatting
● Static text—for example, strings you specify programmatically and display in parts of your user interface
or an HTML file containing app help
●
Icons (including your app icon) and other images when those images either contain text or have some
culture-specific meaning
● Sound files containing spoken language
● Nib files
Build-Time Configuration Details
Internationalizing Your App
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
69Users select their preferred language from the Language and Text system preference, shown in Figure 5-2.
Figure 5-2 The Language preference view
Your application bundle can include multiple language-specific resource directories. The names of these
directories consist of three components: an ISO 639-1 language code, an optional ISO 3166-1 region code, and
a .lproj suffix. For example, to designate resourceslocalized to English, the bundle would be named en.lproj.
By convention, these directories are called lproj directories.
Note: You may use ISO 639-2 language codes instead of those defined by ISO 639-1. For more
information about language and region codes, see “Language and Locale Designations” in
Internationalization Programming Topics.
Each lproj directory contains a localized copy of the app’s resource files. When you request the path to a
resource file using the NSBundle class or the CFBundleRef opaque type, the path you get back automatically
reflects the resource for the user’s preferred language.
For more information about internationalization and how you support localized content in your Mac apps, see
Internationalization Programming Topics.
Build-Time Configuration Details
Internationalizing Your App
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
70As you develop your app and your project code stabilizes, you can begin performance tuning. Of course, you
want your app to launch and respond to the user’s commands as quickly as possible. A responsive app fits
easily into the user’s workflow and feels well crafted.
Speed Up Your App’s Launch Time
You can improve your app’s performance at launch time by minimizing or deferring work until after the launch
sequence has completed. The launch of an app provides users with the first impression of your app, and it’s
something they see on a regular basis.
Your overriding goal during launch should be to display the app’s menu bar and main window and then start
responding to user commands as quickly as possible. Making your app responsive to commands quickly
provides a better experience for the user. The following sections provide some general tips on how to make
your app launch faster.
Delay Initialization Code
Many apps spend a lot of time initializing code that isn’t used until much later. Delaying the initialization of
subsystems that are not immediately needed can speed up your launch time considerably. Remember that
the goal is to display your app interface quickly, so try to initialize only the subsystems related to that goal
initially.
Once you have posted your interface, your app can continue to initialize additional subsystems as needed.
However, remember that just because your app is able to process commands does not mean you need all of
that code right away. The preferred way of initializing subsystems is on an as-needed basis. Wait until the user
executes a command that requires a particular subsystem and then initialize it. That way, if the user never
executes the command, you will not have wasted any time running the code to prepare for it.
Avoid putting a lot of extraneous initialization code in your awakeFromNib methods. The system calls the
awakeFromNib method of your main nib file before your app enters its main event loop. Use that method to
initialize the objects in that nib and to prepare your app interface. For all other initialization, use the
applicationDidFinishLaunching: method of your NSApplicationDelegate object instead. For more
information on nib files and how they are loaded, see Resource Programming Guide .
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
71
Tuning for Performance and ResponsivenessSimplify Your Main Nib File
Loading a nib file is an expensive process that can slow down your app launch time if you are not careful.
When a nib file isloaded, all of the objectsin that file are instantiated and made ready for use. The more objects
you include in your app’s main nib, the more time it takes to load that file and launch your app.
The instantiation process for objects in a nib file requires that any frameworks used by those objects must
themselves reside in memory. Thus loading a nib for a Cocoa app would likely require the loading of both the
AppKit and Foundation frameworks, if they were not already resident in memory. Similarly, if you declare a
custom class in your main nib file and that class relies on other frameworks, the system must load those
frameworks as well.
When designing your app’s main nib file, you should include only those objects needed to display your app’s
initial user interface. Usually, this would involve just your app’s menu bar and initial window. For any custom
classes you include in the nib, make sure their initialization code is as minimal as possible. Defer any
time-consuming operations or memory allocations until after the class is instantiated.
Minimize Global Variables
For both apps and frameworks, be careful not to declare global variables that require significant amounts of
initialization. The system initializes global variables before calling your app’s main routine. If you use a global
variable to declare an object, the system must call the constructor or initialization method for that object during
launch time. In general, it’s best to avoid declaring objects as global variables altogether when you can use a
pointer instead.
If you are implementing a framework or any type of reusable code module, you should also minimize the
number of global variables you declare. Each app that links to a framework acquires a copy of that framework’s
global variables. These variables might require several pages of virtual memory, which then increases the
memory footprint of the app. An increased memory footprint can lead to paging in the app, which has a
tremendous impact on performance.
One way to minimize the global variables in a framework is to store the variables in a malloc-allocated block
of memory instead. In this technique, you access the variables through a pointer to the memory, which you
store as a global variable. Another advantage of this technique is that it allows you to defer the creation of any
global variables until the first time they are actually used. See “Tips for Allocating Memory” in Memory Usage
Performance Guidelines for more information.
Tuning for Performance and Responsiveness
Speed Up Your App’s Launch Time
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
72Minimize File Access at Launch Time
Accessing a file is one of the slowest operations performed on a computer, so it is important that you do it as
little as possible, especially at launch time. There is always some file access that must occur at launch time,
such as loading your executable code and reading in your main nib file, but reducing your initial dependence
on startup files can provide significant speed improvements.
If you can delay the reading of a file until after launch time, do so. The following list includes some files whose
contents you may not need until after launch:
● Frameworks not used directly by your app—Avoid calling code that uses nonessential frameworks until
after launch.
● Nib files whose contents are not displayed immediately—Make sure your nib files and awakeFromNib:
code are not doing too much at launch time. See “Simplify Your Main Nib File” (page 72) for more
information.
● User preference files—User preferences may not be local so read them later if you can.
● Font files—Consider delaying font initialization until after the app has launched.
● Network files—Avoid reading files located on the network if at all possible.
If you must read a file at launch time, do so only once. If you need multiple pieces of data from the same file,
such as from a preferences file, consider reading all of the data once rather than accessing the file multiple
times.
Don’t Block the Main Thread
The main thread is where your app handles user events and other input, so you should keep it free as much
as possible to be responsive to the user. In particular, never use the main thread to perform long-running or
potentially unbounded tasks, such as tasks that require network access. Instead, always move those tasks onto
background threads. The preferred way to do so is to use Grand Central Dispatch (GCD) or operation objects
to perform tasks asynchronously.
For more information about doing work on background threads, see Concurrency Programming Guide .
Decrease Your App’s Code Size
In the context of performance, the more memory your app occupies, the more inefficient it is. More memory
means more memory allocations, more code, and a greater potential for paging.
Tuning for Performance and Responsiveness
Don’t Block the Main Thread
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
73Reducing your code footprint is not just a matter of turning on code optimizations in your compiler, although
that does help. You can also reduce your code footprint by organizing your code so that only the minimum
set of required functions is in memory at any given time. You implement this optimization by profiling your
code.
See “Memory Instruments” in Instruments User Guide for information about profiling your app’s memory
allocations.
Compiler-Level Optimizations
The Xcode compiler supports optimization options that let you choose whether you prefer a smaller binary
size, faster code, or faster build times. For new projects, Xcode automatically disables optimizations for the
debug build configuration and selects the Fastest, Smallest option for the release build configuration. Code
optimizations of any kind result in slower build times because of the extra work involved in the optimization
process. If your code is changing, as it does during the development cycle, you do not want optimizations
enabled. As you near the end of your development cycle, though, the release build configuration can give you
an indication of the size of your finished product, so the Fastest, Smallest option is appropriate.
Table 6-1 lists the optimization levels available in Xcode. When you select one of these options, Xcode passes
the appropriate flags to the compiler for the given group or files. These options are available at the target level
or as part of a build configuration. See the Xcode Build System Guide for information on working with build
settings for your project.
Table 6-1 Compiler optimization options
Xcode setting Description
The compiler does not attempt to optimize code. Use this option during development
when you are focused on solving logic errors and need a fast compile time. Do not
use this option for shipping your executable.
None
The compiler performs simple optimizations to boost code performance while
minimizing the impact to compile time. This option also uses more memory during
compilation.
Fast
The compiler performs nearly all supported optimizations that do not require a
space-time tradeoff. The compiler does not perform loop unrolling or function inlining
with this option. This option increases both compilation time and the performance
of generated code.
Faster
The compiler performs all optimizations in an attempt to improve the speed of the
generated code. This option can increase the size of generated code as the compiler
performs aggressive inlining of functions.
This option is generally not recommended.
Fastest
Tuning for Performance and Responsiveness
Decrease Your App’s Code Size
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
74Xcode setting Description
The compiler performs all optimizations that do not typically increase code size. This
is the preferred option for shipping code because it gives your executable a smaller
memory footprint.
Fastest,
Smallest
As with any performance enhancement, do not make assumptions about which option will give you the best
results. You should always measure the results of each optimization you try. For example, the Fastest option
might generate extremely fast code for a particular module, but it usually doesso at the expense of executable
size. Any speed advantages you gain from the code generation are easily lost if the code needs to be paged
in from disk at runtime.
Use Core Data for Large Data Sets
If your app manipulates large amounts of structured data, store it in a Core Data persistent store or in a SQLite
database instead of in a flat file. Both Core Data and SQLite provide efficient ways to manage large data sets
without requiring the entire set to be in memory all at once. Use SQLite if you deal with low-level data structures,
or an existing SQLite database. Core Data provides a high-level abstraction for efficient object-graph
management with an Objective-C interface; it is, however, an advanced framework and you shouldn't use it
until you have gained adequate experience.
For more information about Core Data, see Core Data Programming Guide and Optimizing Core Data with
Instruments.
Eliminate Memory Leaks
Your app should not have any memory leaks. You can use the Instruments app to track down leaks in your
code, both in the simulator and on actual devices. See “Memory Instruments” in Instruments User Guide for
information about finding memory leaks.
Dead Strip Your Code
For statically linked executables, dead-code stripping is the process of removing unreferenced code from the
executable file. If the code is unreferenced, it must not be used and therefore is not needed in the executable
file. Removing dead code reduces the size of your executable and can help reduce paging.
To enable dead-code stripping in Xcode, in the Linking group of Build Settings, set the Dead Code Stripping
option to Yes.
Tuning for Performance and Responsiveness
Decrease Your App’s Code Size
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
75Strip Symbol Information
Debugging symbols and dynamic-binding information can take up a lot of space and comprise a large
percentage of your executable’s size. Before shipping your code, you should strip out all unneeded symbols.
To strip debugging symbols from your executable, change the Xcode compiler code generation Generate
Debug Symbols option to No. You can also generate debugging symbols on a target-by-target basis if you
prefer. See the Xcode Help for more information on build configurations and target settings.
Tuning for Performance and Responsiveness
Decrease Your App’s Code Size
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
76This table describes the changes to Mac App Programming Guide .
Date Notes
Added a short section on adopting iCloud in a Mac app, “Integrating
iCloud Support Into Your App” (page 30).
2012-07-23
Removed the chapter on iCloud, which is superseded by iCloud Design
Guide .
Rewrote section about supporting high resolution: “Optimize for High
Resolution” (page 58).
Summarized chapter on document-based apps, added iCloud chapter,
and revised sandbox information. Made minor technical and editorial
revisions throughout. Changed title from OS X Application Programming
Guide.
2012-01-09
2011-06-27 New document describing the development process for Mac apps.
2012-07-23 | © 2012 Apple Inc. All Rights Reserved.
77
Document Revision HistoryApple Inc.
© 2012 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, Bonjour, Cocoa, Finder,
Instruments, iPhoto, iTunes, Keychain, Mac,
Macintosh, Objective-C, OS X, Quartz, QuickDraw,
Sand, Spotlight, Time Machine, and Xcode are
trademarks of Apple Inc., registered in the U.S.
and other countries.
Launchpad is a trademark of Apple Inc.
iCloud is a service mark of Apple Inc., registered
in the U.S. and other countries.
App Store and Mac App Store are service marks
of Apple Inc.
OpenGL is a registered trademark of Silicon
Graphics, Inc.
UNIX is a registered trademark of The Open
Group.
iOS is a trademark or registered trademark of
Cisco in the U.S. and other countries and is used
under license.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
Objective-C Runtime
Programming GuideContents
Introduction 5
Organization of This Document 5
See Also 5
Runtime Versions and Platforms 7
Legacy and Modern Versions 7
Platforms 7
Interacting with the Runtime 8
Objective-C Source Code 8
NSObject Methods 8
Runtime Functions 9
Messaging 10
The objc_msgSend Function 10
Using Hidden Arguments 13
Getting a Method Address 14
Dynamic Method Resolution 16
Dynamic Method Resolution 16
Dynamic Loading 17
Message Forwarding 18
Forwarding 18
Forwarding and Multiple Inheritance 20
Surrogate Objects 21
Forwarding and Inheritance 21
Type Encodings 24
Declared Properties 28
Property Type and Functions 28
Property Type String 29
Property Attribute Description Examples 30
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
2Document Revision History 33
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
3
ContentsFigures and Tables
Messaging 10
Figure 3-1 Messaging Framework 12
Message Forwarding 18
Figure 5-1 Forwarding 20
Type Encodings 24
Table 6-1 Objective-C type encodings 24
Table 6-2 Objective-C method encodings 26
Declared Properties 28
Table 7-1 Declared property type encodings 30
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
4The Objective-C language defers as many decisions as it can from compile time and link time to runtime.
Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but
also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system
for the Objective-C language; it’s what makes the language work.
This document looks at the NSObject class and how Objective-C programs interact with the runtime system.
In particular, it examines the paradigms for dynamically loading new classes at runtime, and forwarding
messages to other objects. It also provides information about how you can find information about objects
while your program is running.
You should read this document to gain an understanding of how the Objective-C runtime system works and
how you can take advantage of it. Typically, though, there should be little reason for you to need to know and
understand this material to write a Cocoa application.
Organization of This Document
This document has the following chapters:
●
“Runtime Versions and Platforms” (page 7)
●
“Interacting with the Runtime” (page 8)
●
“Messaging” (page 10)
●
“Dynamic Method Resolution” (page 16)
●
“Message Forwarding” (page 18)
●
“Type Encodings” (page 24)
●
“Declared Properties” (page 28)
See Also
Objective-C Runtime Reference describes the data structures and functions of the Objective-C runtime support
library. Your programs can use these interfaces to interact with the Objective-C runtime system. For example,
you can add classes or methods, or obtain a list of all class definitions for loaded classes.
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
5
IntroductionThe Objective-C Programming Language describes the Objective-C language.
Objective-C Release Notes describes some of the changes in the Objective-C runtime in the latest release of OS
X.
Introduction
See Also
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
6There are different versions of the Objective-C runtime on different platforms.
Legacy and Modern Versions
There are two versions of the Objective-C runtime—“modern” and “legacy”. The modern version wasintroduced
with Objective-C 2.0 and includes a number of new features. The programming interface for the legacy version
of the runtime is described in Objective-C 1 Runtime Reference ; the programming interface for the modern
version of the runtime is described in Objective-C Runtime Reference .
The most notable new feature is that instance variables in the modern runtime are “non-fragile”:
●
In the legacy runtime, if you change the layout of instance variables in a class, you must recompile classes
that inherit from it.
●
In the modern runtime, if you change the layout of instance variables in a class, you do not have to
recompile classes that inherit from it.
In addition, the modern runtime supports instance variable synthesis for declared properties (see Declared
Properties in The Objective-C Programming Language ).
Platforms
iPhone applications and 64-bit programs on OS X v10.5 and later use the modern version of the runtime.
Other programs (32-bit programs on OS X desktop) use the legacy version of the runtime.
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
7
Runtime Versions and PlatformsObjective-C programs interact with the runtime system at three distinct levels: through Objective-C source
code; through methods defined in the NSObject class of the Foundation framework; and through direct calls
to runtime functions.
Objective-C Source Code
For the most part, the runtime system works automatically and behind the scenes. You use it just by writing
and compiling Objective-C source code.
When you compile code containing Objective-C classes and methods, the compiler creates the data structures
and function calls that implement the dynamic characteristics of the language. The data structures capture
information found in class and category definitions and in protocol declarations; they include the class and
protocol objects discussed in Defining a Class and Protocols in The Objective-C Programming Language , as well
as method selectors, instance variable templates, and other information distilled from source code. The principal
runtime function is the one that sends messages, as described in “Messaging” (page 10). It’s invoked by
source-code message expressions.
NSObject Methods
Most objects in Cocoa are subclasses of the NSObject class, so most objects inherit the methods it defines.
(The notable exception is the NSProxy class; see “Message Forwarding” (page 18) for more information.) Its
methods therefore establish behaviors that are inherent to every instance and every class object. However, in
a few cases, the NSObject class merely defines a template for how something should be done; it doesn’t
provide all the necessary code itself.
For example, the NSObject class defines a description instance method that returns a string describing
the contents of the class. This is primarily used for debugging—the GDB print-object command prints the
string returned from this method. NSObject’s implementation of this method doesn’t know what the class
contains,so it returns a string with the name and address of the object. Subclasses of NSObject can implement
this method to return more details. For example, the Foundation class NSArray returns a list of descriptions
of the objects it contains.
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
8
Interacting with the RuntimeSome of the NSObject methodssimply query the runtime system for information. These methods allow objects
to perform introspection. Examples of such methods are the class method, which asks an object to identify
its class; isKindOfClass: and isMemberOfClass:, which test an object’s position in the inheritance
hierarchy; respondsToSelector:, which indicates whether an object can accept a particular message;
conformsToProtocol:, which indicates whether an object claims to implement the methods defined in a
specific protocol; and methodForSelector:, which provides the address of a method’s implementation.
Methods like these give an object the ability to introspect about itself.
Runtime Functions
The runtime system is a dynamic shared library with a public interface consisting of a set of functions and data
structuresin the header fileslocated within the directory /usr/include/objc. Many of these functions allow
you to use plain C to replicate what the compiler does when you write Objective-C code. Others form the basis
for functionality exported through the methods of the NSObject class. These functions make it possible to
develop other interfacesto the runtime system and produce toolsthat augment the development environment;
they’re not needed when programming in Objective-C. However, a few of the runtime functions might on
occasion be useful when writing an Objective-C program. All of these functions are documented in Objective-C
Runtime Reference .
Interacting with the Runtime
Runtime Functions
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
9This chapter describes how the message expressions are converted into objc_msgSend function calls, and
how you can refer to methods by name. It then explains how you can take advantage of objc_msgSend, and
how—if you need to—you can circumvent dynamic binding.
The objc_msgSend Function
In Objective-C, messages aren’t bound to method implementations until runtime. The compiler converts a
message expression,
[receiver message]
into a call on a messaging function, objc_msgSend. This function takes the receiver and the name of the
method mentioned in the message—that is, the method selector—as its two principal parameters:
objc_msgSend(receiver, selector)
Any arguments passed in the message are also handed to objc_msgSend:
objc_msgSend(receiver, selector, arg1, arg2, ...)
The messaging function does everything necessary for dynamic binding:
●
It first finds the procedure (method implementation) that the selector refers to. Since the same method
can be implemented differently by separate classes, the precise procedure that it finds depends on the
class of the receiver.
●
It then calls the procedure, passing it the receiving object (a pointer to its data), along with any arguments
that were specified for the method.
● Finally, it passes on the return value of the procedure as its own return value.
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
10
MessagingNote: The compiler generates calls to the messaging function. You should never call it directly in
the code you write.
The key to messaging lies in the structures that the compiler builds for each class and object. Every class
structure includes these two essential elements:
● A pointer to the superclass.
● A class dispatch table. This table has entries that associate method selectors with the class-specific
addresses of the methods they identify. The selector for the setOrigin:: method is associated with the
address of (the procedure that implements) setOrigin::, the selector for the display method is
associated with display’s address, and so on.
When a new object is created, memory for it is allocated, and its instance variables are initialized. First among
the object’s variables is a pointer to its class structure. This pointer, called isa, gives the object access to its
class and, through the class, to all the classes it inherits from.
Messaging
The objc_msgSend Function
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
11Note: While not strictly a part of the language, the isa pointer is required for an object to work
with the Objective-C runtime system. An object needsto be “equivalent” to a struct objc_object
(defined in objc/objc.h) in whatever fieldsthe structure defines. However, you rarely, if ever, need
to create your own root object, and objects that inherit from NSObject or NSProxy automatically
have the isa variable.
These elements of class and object structure are illustrated in Figure 3-1.
Figure 3-1 Messaging Framework
. . .
superclass
selector...address
selector...address
selector...address
. . .
superclass
selector...address
selector...address
selector...address
. . .
superclass
selector...address
selector...address
selector...address
isa
instance variable
instance variable
. . .
The object’s superclass
The root class (NSObject)
The object’s class
Messaging
The objc_msgSend Function
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
12When a message is sent to an object, the messaging function follows the object’s isa pointer to the class
structure where it looks up the method selector in the dispatch table. If it can’t find the selector there,
objc_msgSend followsthe pointer to the superclass and triesto find the selector in its dispatch table. Successive
failures cause objc_msgSend to climb the class hierarchy until it reaches the NSObject class. Once it locates
the selector, the function callsthe method entered in the table and passesit the receiving object’s data structure.
This is the way that method implementations are chosen at runtime—or, in the jargon of object-oriented
programming, that methods are dynamically bound to messages.
To speed the messaging process, the runtime system caches the selectors and addresses of methods as they
are used. There’s a separate cache for each class, and it can contain selectors for inherited methods as well as
for methods defined in the class. Before searching the dispatch tables, the messaging routine first checks the
cache of the receiving object’s class(on the theory that a method that was used once may likely be used again).
If the method selector is in the cache, messaging is only slightly slower than a function call. Once a program
has been running long enough to “warm up” its caches, almost all the messagesitsendsfind a cached method.
Caches grow dynamically to accommodate new messages as the program runs.
Using Hidden Arguments
When objc_msgSend finds the procedure that implements a method, it calls the procedure and passes it all
the arguments in the message. It also passes the procedure two hidden arguments:
● The receiving object
● The selector for the method
These arguments give every method implementation explicit information about the two halves of the message
expression that invoked it. They’re said to be “hidden” because they aren’t declared in the source code that
defines the method. They’re inserted into the implementation when the code is compiled.
Although these arguments aren’t explicitly declared, source code can still refer to them (just as it can refer to
the receiving object’s instance variables). A method refers to the receiving object as self, and to its own
selector as _cmd. In the example below, _cmd refers to the selector for the strange method and self to the
object that receives a strange message.
- strange
{
id target = getTheReceiver();
SEL method = getTheMethod();
Messaging
Using Hidden Arguments
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
13if ( target == self || method == _cmd )
return nil;
return [target performSelector:method];
}
self is the more useful of the two arguments. It is, in fact, the way the receiving object’s instance variables
are made available to the method definition.
Getting a Method Address
The only way to circumvent dynamic binding is to get the address of a method and call it directly as if it were
a function. This might be appropriate on the rare occasions when a particular method will be performed many
times in succession and you want to avoid the overhead of messaging each time the method is performed.
With a method defined in the NSObject class, methodForSelector:, you can ask for a pointer to the
procedure that implements a method, then use the pointer to call the procedure. The pointer that
methodForSelector: returns must be carefully cast to the proper function type. Both return and argument
types should be included in the cast.
The example below shows how the procedure that implements the setFilled: method might be called:
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)];
for ( i = 0; i < 1000, i++ )
setter(targetList[i], @selector(setFilled:), YES);
The first two arguments passed to the procedure are the receiving object (self) and the method selector
(_cmd). These arguments are hidden in method syntax but must be made explicit when the method is called
as a function.
Using methodForSelector: to circumvent dynamic binding saves most of the time required by messaging.
However, the savings will be significant only where a particular message is repeated many times, as in the for
loop shown above.
Messaging
Getting a Method Address
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
14Note that methodForSelector: is provided by the Cocoa runtime system; it’s not a feature of the Objective-C
language itself.
Messaging
Getting a Method Address
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
15This chapter describes how you can provide an implementation of a method dynamically.
Dynamic Method Resolution
There are situations where you might want to provide an implementation of a method dynamically. For example,
the Objective-C declared propertiesfeature (see Declared Properties in The Objective-C Programming Language )
includes the @dynamic directive:
@dynamic propertyName;
which tells the compiler that the methods associated with the property will be provided dynamically.
You can implement the methods resolveInstanceMethod: and resolveClassMethod: to dynamically
provide an implementation for a given selector for an instance and class method respectively.
An Objective-C method is simply a C function that take at least two arguments—self and _cmd. You can add
a function to a class as a method using the function class_addMethod. Therefore, given the following function:
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
you can dynamically add it to a class as a method (called resolveThisMethodDynamically) using
resolveInstanceMethod: like this:
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
16
Dynamic Method Resolution}
return [super resolveInstanceMethod:aSEL];
}
@end
Forwarding methods (as described in “Message Forwarding” (page 18)) and dynamic method resolution are,
largely, orthogonal. A class has the opportunity to dynamically resolve a method before the forwarding
mechanism kicksin. If respondsToSelector: or instancesRespondToSelector: isinvoked, the dynamic
method resolver is given the opportunity to provide an IMP for the selector first. If you implement
resolveInstanceMethod: but want particular selectors to actually be forwarded via the forwarding
mechanism, you return NO for those selectors.
Dynamic Loading
An Objective-C program can load and link new classes and categories while it’s running. The new code is
incorporated into the program and treated identically to classes and categories loaded at the start.
Dynamic loading can be used to do a lot of different things. For example, the various modules in the System
Preferences application are dynamically loaded.
In the Cocoa environment, dynamic loading is commonly used to allow applications to be customized. Others
can write modules that your program loads at runtime—much as Interface Builder loads custom palettes and
the OS X System Preferences application loads custom preference modules. The loadable modules extend
what your application can do. They contribute to it in ways that you permit but could not have anticipated or
defined yourself. You provide the framework, but others provide the code.
Although there is a runtime function that performs dynamic loading of Objective-C modules in Mach-O files
(objc_loadModules, defined in objc/objc-load.h), Cocoa’s NSBundle class provides a significantly more
convenient interface for dynamic loading—one that’s object-oriented and integrated with related services.
See the NSBundle classspecification in the Foundation framework reference for information on the NSBundle
class and its use. See OS X ABI Mach-O File Format Reference for information on Mach-O files.
Dynamic Method Resolution
Dynamic Loading
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
17Sending a message to an object that does not handle that message is an error. However, before announcing
the error, the runtime system gives the receiving object a second chance to handle the message.
Forwarding
If you send a message to an object that does not handle that message, before announcing an error the runtime
sends the object a forwardInvocation: message with an NSInvocation object as its sole argument—the
NSInvocation object encapsulates the original message and the arguments that were passed with it.
You can implement a forwardInvocation: method to give a default response to the message, or to avoid
the error in some other way. As its name implies, forwardInvocation: is commonly used to forward the
message to another object.
To see the scope and intent of forwarding, imagine the following scenarios: Suppose, first, that you’re designing
an object that can respond to a message called negotiate, and you want itsresponse to include the response
of another kind of object. You could accomplish this easily by passing a negotiate message to the other
object somewhere in the body of the negotiate method you implement.
Take this a step further, and suppose that you want your object’s response to a negotiate message to be
exactly the response implemented in another class. One way to accomplish this would be to make your class
inherit the method from the other class. However, it might not be possible to arrange things this way. There
may be good reasons why your class and the class that implements negotiate are in different branches of
the inheritance hierarchy.
Even if your class can’t inherit the negotiate method, you can still “borrow” it by implementing a version of
the method that simply passes the message on to an instance of the other class:
- negotiate
{
if ( [someOtherObject respondsTo:@selector(negotiate)] )
return [someOtherObject negotiate];
return self;
}
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
18
Message ForwardingThis way of doing things could get a little cumbersome, especially if there were a number of messages you
wanted your object to pass on to the other object. You’d have to implement one method to cover each method
you wanted to borrow from the other class. Moreover, it would be impossible to handle cases where you didn’t
know, at the time you wrote the code, the full set of messages you might want to forward. That set might
depend on events at runtime, and it might change as new methods and classes are implemented in the future.
The second chance offered by a forwardInvocation: message provides a less ad hoc solution to this
problem, and one that’s dynamic rather than static. It workslike this: When an object can’t respond to a message
because it doesn’t have a method matching the selector in the message, the runtime system informsthe object
by sending it a forwardInvocation: message. Every object inherits a forwardInvocation: method from
the NSObject class. However, NSObject’s version of the method simply invokes
doesNotRecognizeSelector:. By overriding NSObject’s version and implementing your own, you can
take advantage of the opportunity that the forwardInvocation: message provides to forward messages
to other objects.
To forward a message, all a forwardInvocation: method needs to do is:
● Determine where the message should go, and
● Send it there with its original arguments.
The message can be sent with the invokeWithTarget: method:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
The return value of the message that’s forwarded is returned to the original sender. All types of return values
can be delivered to the sender, including ids, structures, and double-precision floating-point numbers.
A forwardInvocation: method can act as a distribution center for unrecognized messages, parceling them
out to different receivers. Or it can be a transfer station, sending all messages to the same destination. It can
translate one message into another, or simply “swallow” some messages so there’s no response and no error.
A forwardInvocation: method can also consolidate several messages into a single response. What
forwardInvocation: doesis up to the implementor. However, the opportunity it providesfor linking objects
in a forwarding chain opens up possibilities for program design.
Message Forwarding
Forwarding
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
19Note: The forwardInvocation: method gets to handle messages only if they don’t invoke an
existing method in the nominal receiver. If, for example, you want your object to forward negotiate
messages to another object, it can’t have a negotiate method of its own. If it does, the message
will never reach forwardInvocation:.
For more information on forwarding and invocations, see the NSInvocation class specification in the
Foundation framework reference.
Forwarding and Multiple Inheritance
Forwarding mimics inheritance, and can be used to lend some of the effects of multiple inheritance to
Objective-C programs. As shown in Figure 5-1 (page 20), an object that responds to a message by forwarding
it appears to borrow or “inherit” a method implementation defined in another class.
Figure 5-1 Forwarding
isa
. . .
– forwardInvocation: – negotiate
negotiate isa
. . .
Warrior Diplomat
In this illustration, an instance of the Warrior class forwards a negotiate message to an instance of the
Diplomat class. The Warrior will appear to negotiate like a Diplomat. It will seem to respond to the negotiate
message, and for all practical purposes it does respond (although it’s really a Diplomat that’s doing the work).
The object that forwards a message thus“inherits” methodsfrom two branches of the inheritance hierarchy—its
own branch and that of the object that responds to the message. In the example above, it appears as if the
Warrior class inherits from Diplomat as well as its own superclass.
Message Forwarding
Forwarding and Multiple Inheritance
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
20Forwarding provides most of the features that you typically want from multiple inheritance. However, there’s
an important difference between the two: Multiple inheritance combines different capabilities in a single
object. It tends toward large, multifaceted objects. Forwarding, on the other hand, assigns separate
responsibilitiesto disparate objects. It decomposes problemsinto smaller objects, but associatesthose objects
in a way that’s transparent to the message sender.
Surrogate Objects
Forwarding not only mimics multiple inheritance, it also makes it possible to develop lightweight objects that
represent or “cover” more substantial objects. The surrogate standsin for the other object and funnels messages
to it.
The proxy discussed in “Remote Messaging” in The Objective-C Programming Language is such a surrogate. A
proxy takes care of the administrative details of forwarding messages to a remote receiver, making sure
argument values are copied and retrieved across the connection, and so on. But it doesn’t attempt to do much
else; it doesn’t duplicate the functionality of the remote object but simply gives the remote object a local
address, a place where it can receive messages in another application.
Other kinds of surrogate objects are also possible. Suppose, for example, that you have an object that
manipulates a lot of data—perhaps it creates a complicated image or reads the contents of a file on disk.
Setting this object up could be time-consuming, so you prefer to do it lazily—when it’s really needed or when
system resources are temporarily idle. At the same time, you need at least a placeholder for this object in order
for the other objects in the application to function properly.
In this circumstance, you could initially create, not the full-fledged object, but a lightweight surrogate for it.
This object could do some things on its own, such as answer questions about the data, but mostly it would
just hold a place for the larger object and, when the time came, forward messages to it. When the surrogate’s
forwardInvocation: method first receives a message destined for the other object, it would ensure that
the object existed and would create it if it didn’t. All messages for the larger object go through the surrogate,
so, as far as the rest of the program is concerned, the surrogate and the larger object would be the same.
Forwarding and Inheritance
Although forwarding mimics inheritance, the NSObject class never confuses the two. Methods like
respondsToSelector: and isKindOfClass: look only at the inheritance hierarchy, never at the forwarding
chain. If, for example, a Warrior object is asked whether it responds to a negotiate message,
if ( [aWarrior respondsToSelector:@selector(negotiate)] )
...
Message Forwarding
Surrogate Objects
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
21the answer is NO, even though it can receive negotiate messages without error and respond to them, in a
sense, by forwarding them to a Diplomat. (See Figure 5-1 (page 20).)
In many cases, NO is the right answer. But it may not be. If you use forwarding to set up a surrogate object or
to extend the capabilities of a class, the forwarding mechanism should probably be astransparent asinheritance.
If you want your objects to act as if they truly inherited the behavior of the objects they forward messages to,
you’ll need to re-implement the respondsToSelector: and isKindOfClass: methods to include your
forwarding algorithm:
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}
In addition to respondsToSelector: and isKindOfClass:,the instancesRespondToSelector:method
should also mirror the forwarding algorithm. If protocols are used, the conformsToProtocol: method should
likewise be added to the list. Similarly, if an object forwards any remote messages it receives, it should have a
version of methodSignatureForSelector: that can return accurate descriptions of the methods that
ultimately respond to the forwarded messages; for example, if an object is able to forward a message to its
surrogate, you would implement methodSignatureForSelector: as follows:
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
Message Forwarding
Forwarding and Inheritance
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
22You might consider putting the forwarding algorithm somewhere in private code and have all these methods,
forwardInvocation: included, call it.
Note: Thisis an advanced technique,suitable only forsituations where no othersolution is possible.
It is not intended as a replacement for inheritance. If you must make use of this technique, make
sure you fully understand the behavior of the class doing the forwarding and the class you’re
forwarding to.
The methods mentioned in this section are described in the NSObject class specification in the Foundation
framework reference. For information on invokeWithTarget:, see the NSInvocation class specification
in the Foundation framework reference.
Message Forwarding
Forwarding and Inheritance
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
23To assist the runtime system, the compiler encodes the return and argument types for each method in a
character string and associates the string with the method selector. The coding scheme it uses is also useful
in other contexts and so is made publicly available with the @encode() compiler directive. When given a type
specification, @encode() returns a string encoding that type. The type can be a basic type such as an int, a
pointer, a tagged structure or union, or a class name—any type, in fact, that can be used as an argument to
the C sizeof() operator.
char *buf1 = @encode(int **);
char *buf2 = @encode(struct key);
char *buf3 = @encode(Rectangle);
The table below lists the type codes. Note that many of them overlap with the codes you use when encoding
an object for purposes of archiving or distribution. However, there are codes listed here that you can’t use
when writing a coder, and there are codesthat you may want to use when writing a coder that aren’t generated
by @encode(). (See the NSCoder class specification in the Foundation Framework reference for more
information on encoding objects for archiving or distribution.)
Table 6-1 Objective-C type encodings
Code Meaning
c A char
i An int
s A short
A long
l is treated as a 32-bit quantity on 64-bit programs.
l
q A long long
C An unsigned char
I An unsigned int
S An unsigned short
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
24
Type EncodingsCode Meaning
L An unsigned long
Q An unsigned long long
f A float
d A double
B A C++ bool or a C99 _Bool
v A void
* A character string (char *)
@ An object (whether statically typed or typed id)
# A class object (Class)
: A method selector (SEL)
[array type ] An array
{name=type...} A structure
(name=type...) A union
bnum A bit field of num bits
^type A pointer to type
? An unknown type (among other things, this code is used for function pointers)
Important: Objective-C does not support the long double type. @encode(long double) returns d,
which is the same encoding as for double.
The type code for an array is enclosed within square brackets; the number of elements in the array is specified
immediately after the open bracket, before the array type. For example, an array of 12 pointers to floats
would be encoded as:
[12^f]
Structures are specified within braces, and unions within parentheses. The structure tag is listed first, followed
by an equal sign and the codes for the fields of the structure listed in sequence. For example, the structure
Type Encodings
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
25typedef struct example {
id anObject;
char *aString;
int anInt;
} Example;
would be encoded like this:
{example=@*i}
The same encoding results whether the defined type name (Example) or the structure tag (example) is passed
to @encode(). The encoding for a structure pointer carriesthe same amount of information about the structure’s
fields:
^{example=@*i}
However, another level of indirection removes the internal type specification:
^^{example}
Objects are treated like structures. For example, passing the NSObject class name to @encode() yields this
encoding:
{NSObject=#}
The NSObject class declares just one instance variable, isa, of type Class.
Note that although the @encode() directive doesn’t return them, the runtime system uses the additional
encodings listed in Table 6-2 for type qualifiers when they’re used to declare methods in a protocol.
Table 6-2 Objective-C method encodings
Code Meaning
r const
n in
N inout
Type Encodings
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
26Code Meaning
o out
O bycopy
R byref
V oneway
Type Encodings
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
27When the compiler encounters property declarations (see Declared Properties in The Objective-C Programming
Language ), it generates descriptive metadata that is associated with the enclosing class, category or protocol.
You can accessthis metadata using functionsthatsupport looking up a property by name on a class or protocol,
obtaining the type of a property as an @encode string, and copying a list of a property's attributes as an array
of C strings. A list of declared properties is available for each class and protocol.
Property Type and Functions
The Property structure defines an opaque handle to a property descriptor.
typedef struct objc_property *Property;
You can use the functions class_copyPropertyList and protocol_copyPropertyList to retrieve an
array of the properties associated with a class (including loaded categories) and a protocol respectively:
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
For example, given the following class declaration:
@interface Lender : NSObject {
float alone;
}
@property float alone;
@end
you can get the list of properties using:
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
28
Declared Propertiesobjc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
You can use the property_getName function to discover the name of a property:
const char *property_getName(objc_property_t property)
You can use the functions class_getProperty and protocol_getProperty to get a reference to a property
with a given name in a class and protocol respectively:
objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL
isRequiredProperty, BOOL isInstanceProperty)
You can use the property_getAttributes function to discover the name and the @encode type string of
a property. For details of the encoding type strings, see “Type Encodings” (page 24); for details of this string,
see “Property Type String” (page 29) and “Property Attribute Description Examples” (page 30).
const char *property_getAttributes(objc_property_t property)
Putting these together, you can print a list of all the properties associated with a class using the following
code:
id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property),
property_getAttributes(property));
}
Property Type String
You can use the property_getAttributes function to discover the name, the @encode type string of a
property, and other attributes of the property.
Declared Properties
Property Type String
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
29The string starts with a T followed by the @encode type and a comma, and finishes with a V followed by the
name of the backing instance variable. Between these, the attributes are specified by the following descriptors,
separated by commas:
Table 7-1 Declared property type encodings
Code Meaning
R The property is read-only (readonly).
C The property is a copy of the value last assigned (copy).
& The property is a reference to the value last assigned (retain).
N The property is non-atomic (nonatomic).
The property defines a custom getter selector name. The name follows the G (for
example, GcustomGetter,).
G
The property defines a custom setter selector name. The name follows the S (for
example, ScustomSetter:,).
S
D The property is dynamic (@dynamic).
W The property is a weak reference (__weak).
P The property is eligible for garbage collection.
t Specifies the type using old-style encoding.
For examples, see “Property Attribute Description Examples” (page 30).
Property Attribute Description Examples
Given these definitions:
enum FooManChu { FOO, MAN, CHU };
struct YorkshireTeaStruct { int pot; char lady; };
typedef struct YorkshireTeaStruct YorkshireTeaStructType;
union MoneyUnion { float alone; double down; };
Declared Properties
Property Attribute Description Examples
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
30the following table shows sample property declarations and the corresponding string returned by
property_getAttributes:
Property declaration Property description
@property char charDefault; Tc,VcharDefault
@property double doubleDefault; Td,VdoubleDefault
@property enum FooManChu enumDefault; Ti,VenumDefault
@property float floatDefault; Tf,VfloatDefault
@property int intDefault; Ti,VintDefault
@property long longDefault; Tl,VlongDefault
@property short shortDefault; Ts,VshortDefault
@property signed signedDefault; Ti,VsignedDefault
T{YorkshireTeaStruct="pot"i"lady"c},VstructDefault
@property struct YorkshireTeaStruct
structDefault;
T{YorkshireTeaStruct="pot"i"lady"c},VtypedefDefault
@property YorkshireTeaStructType
typedefDefault;
T(MoneyUnion="alone"f"down"d),VunionDefault
@property union MoneyUnion unionDefault;
@property unsigned unsignedDefault; TI,VunsignedDefault
@property int (*functionPointerDefault)(char T^?,VfunctionPointerDefault
*);
@property id idDefault; T@,VidDefault
Note: the compiler warns: no 'assign', 'retain',
or 'copy' attribute is specified - 'assign'
is assumed"
@property int *intPointer; T^i,VintPointer
@property void *voidPointerDefault; T^v,VvoidPointerDefault
Declared Properties
Property Attribute Description Examples
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
31Property declaration Property description
@property int intSynthEquals; Ti,V_intSynthEquals
In the implementation block:
@synthesize intSynthEquals=_intSynthEquals;
Ti,GintGetFoo,SintSetFoo:
,VintSetterGetter
@property(getter=intGetFoo,
setter=intSetFoo:) int intSetterGetter;
@property(readonly) int intReadonly; Ti,R,VintReadonly
@property(getter=isIntReadOnlyGetter, Ti,R,GisIntReadOnlyGetter
readonly) int intReadonlyGetter;
@property(readwrite) int intReadwrite; Ti,VintReadwrite
@property(assign) int intAssign; Ti,VintAssign
@property(retain)ididRetain; T@,&,VidRetain
@property(copy)ididCopy; T@,C,VidCopy
@property(nonatomic) int intNonatomic; Ti,VintNonatomic
@property(nonatomic, readonly, copy) id T@,R,C,VidReadonlyCopyNonatomic
idReadonlyCopyNonatomic;
T@,R,&,VidReadonlyRetainNonatomic
@property(nonatomic, readonly, retain) id
idReadonlyRetainNonatomic;
Declared Properties
Property Attribute Description Examples
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
32This table describes the changes to Objective-C Runtime Programming Guide .
Date Notes
2009-10-19 Made minor editorial changes.
2009-07-14 Completed list of types described by property_getAttributes.
2009-02-04 Corrected typographical errors.
2008-11-19 New document that describesthe Objective-C 2.0 runtime support library.
2009-10-19 | © 2009 Apple Inc. All Rights Reserved.
33
Document Revision HistoryApple Inc.
© 2009 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, Cocoa, iPhone, Mac,
Objective-C, and OS X are trademarks of Apple
Inc., registered in the U.S. and other countries.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
Universal Binary Programming Guidelines,
Second Edition
(Legacy)Contents
Introduction 8
Who Should Read This Document? 8
Organization of This Document 8
Assumptions 9
Conventions 10
Building a Universal Binary 11
Build Assumptions 11
Building Your Code 12
Debugging 16
Troubleshooting Your Built Application 16
Determining Whether a Binary Is Universal 18
Build Options 18
Default Compiler Options 19
Architecture-Specific Options 19
Autoconf Macros 20
See Also 20
Architectural Differences 21
Alignment 21
Bit Fields 21
Byte Order 21
Calling Conventions 22
Code on the Stack: Disabling Execution 22
Data Type Conversions 23
Data Types 23
Divide-By-Zero Operations 24
Extensible Firmware Interface (EFI) 24
Floating-Point Equality Comparisons 24
Structures and Unions 25
See Also 25
Swapping Bytes 26
Why Byte Ordering Matters 26
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
2Guidelines for Swapping Bytes 28
Byte-Swapping Routines 29
Byte-Swapping Strategies 30
Constants 30
Custom Apple Event Data 31
Custom Resource Data 31
Floating-Point Values 32
Integers 33
Network-Related Data 34
OSType-to-String Conversions 35
Unicode Text Files 36
Values in an Array 38
Writing a Callback to Swap Data Bytes 38
See Also 45
Guidelines for Specific Scenarios 46
Aliases 46
Archived Bit Fields 46
Automator Scripts 46
Bit Shifting 47
Bit Test, Set, and Clear Functions: Carbon and POSIX 47
CPU Subtype 47
Dashboard Widgets 48
Deprecated Functions 48
Disk Partitions 48
Double-Precision Values: Bit-by-Bit Sensitivity 48
Finder Information and Low-Level File System Operations 49
FireWire Device Access 49
Font-Related Resources 50
GWorlds 50
Java Applications 51
Java I/O API (NIO) 51
Machine Location Data Structure 52
Mach Processes: The Task for PID Function 52
Metrowerks PowerPlant 53
Multithreading 53
Objective-C: Messages to nil 53
Objective-C Runtime: Sending Messages 54
Open Firmware 54
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
3
ContentsOpenGL 55
OSAtomic Functions 58
Pixel Data 58
PostScript Printing 59
Quartz Bitmap Data 59
QuickDraw Routines 60
QuickTime Components 60
QuickTime Metadata Functions 61
Runtime Code Generation 61
Spotlight Importers 61
System-Specific Predefined Macros 62
USB Device Access 62
See Also 62
Preparing Vector-Based Code 63
Accelerate Framework 63
Rewriting AltiVec Instructions 64
See Also 64
Rosetta 65
What Can Be Translated? 65
How It Works 66
Special Considerations 66
Forcing an Application to Run Translated 68
Make a Setting in the Info Window 69
Use Terminal 69
Modify the Property List 69
Use the sysctlbyname Function 70
Preventing an Application from Opening Using Rosetta 71
Programmatically Detecting a Translated Application 71
Troubleshooting 73
Architecture-Independent Vector-Based Code 76
Architecture-Specific Code 76
Architecture-Independent Matrix Multiplication 82
32-Bit Application Binary Interface 84
64-Bit Application Binary Interface 85
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
4
ContentsDocument Revision History 86
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
5
ContentsFigures, Tables, and Listings
Building a Universal Binary 11
Figure 1-1 The Build pane 14
Figure 1-2 Architectures settings 15
Figure 1-3 The Chess application has a Universal binary 18
Table 1-1 Default values for compiler flags on an Intel-based Macintosh computer 19
Architectural Differences 21
Listing 2-1 Code that illustrates byte-ordering differences 22
Listing 2-2 Architecture-dependent code 23
Listing 2-3 A union whose components can be affected by byte order 25
Swapping Bytes 26
Figure 3-1 Big-endian byte ordering compared to little-endian byte ordering 27
Table 3-1 Byte order marks 36
Listing 3-1 A data structure that contains multibyte and single-byte data 26
Listing 3-2 Encoding a 64-bit floating-point value 32
Listing 3-3 Decoding a 32-bit floating-point value 33
Listing 3-4 Swapping a 16-bit integer from big-endian to host-endian 34
Listing 3-5 Swapping integers from little-endian to host-endian 34
Listing 3-6 A routine for swapping the bytes of the values in an array 38
Listing 3-7 A declaration for a custom resource 40
Listing 3-8 A flipper function for RGBColor data 41
Listing 3-9 A flipper for the custom 'PREF' resource 41
Guidelines for Specific Scenarios 46
Figure 4-1 A test image that can help locate the source of color problems 58
Table 4-1 Quartz constants that specify byte ordering 59
Rosetta 65
Figure A-1 The Info window for the Calculator application 69
Figure A-2 Rosetta listens for a port connection 74
Figure A-3 Terminal windows with the commands for debugging a PowerPC binary on an Intel-based
Macintosh computer 75
Listing A-1 A structure whose endian format depends on the architecture 67
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
6Listing A-2 A routine that controls the preferred CPU type for sublaunched processes 70
Listing A-3 A utility routine for calling the sysctlbyname function 71
Listing A-4 A routine that determines whether a process is running natively or translated 72
Architecture-Independent Vector-Based Code 76
Listing B-1 Architecture-specific code needed to support matrix multiplication 77
Listing B-2 Architecture-independent code that performs matrix multiplication 82
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
7
Figures, Tables, and ListingsUniversal Binary Programming Guidelines will assist experienced developers to build and modify their Mac OS
X applications to run as universal binaries. Universal binaries run natively on Macintosh computers using
PowerPC or Intel microprocessors and deliver optimal performance for both architectures in a single package.
Important: This document may not represent best practices for current development. Links to downloads
and other resources may no longer be valid.
This document is designed to help developers determine exactly how much work needs to be done and
provides useful tips for general as well as specific code modification scenarios. It describes the prerequisites
for building code as a universal binary and shows how to do so using Xcode 2.2. It also discussesthe differences
between the Intel and PowerPC architecturesthat can affect code behavior and provides guidelinesfor ensuring
that universal binary code builds correctly.
This version of Universal Binary Programming Guidelines represents a significant update since its introduction
at the Apple Worldwide Developers Conference in June, 2005. It brings together all the information that
developers need to make the transition to Intel-based Macintosh computers. This version includes pointers to
newly revised tools documentation—“Building Universal Binaries” in Xcode Project Management Guide , GCC
Porting Guide , Cross-Development Programming Guide , and more—as well as improved guidelines and tips.
Anyone who has an older version of Universal Binary Programming Guidelines will want to replace it with this
version.
Who Should Read This Document?
Any developer who currently has an application that runs in Mac OS X will want to read this document to learn
how to modify their code so that it runs natively on all current Apple hardware. Developers who have not yet
written an application for the Macintosh, but are planning to do so, will want to follow the guidelines in the
document to ensure that their code can run as a universal binary.
Organization of This Document
This document is organized into the following chapters:
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
8
Introduction●
“Building a Universal Binary” (page 11) shows how to use Xcode 2.2 to build native and universal binaries,
describes build options, and provides troubleshooting information for code that doesn’t run properly on
an Intel-based Macintosh computer.
●
“Architectural Differences” (page 21) outlines the major differences between the x86 and PowerPC
architectures. Understanding the differences will help you to write portable code.
●
“Swapping Bytes” (page 26) describes byte-ordering differences in detail, provides a list of byte-swapping
routines, and discusses strategies for a number of scenarios that require you to swap bytes. This is a
must-read chapter for all Mac OS X developers. It will help you understand how to avoid byte-ordering
issues when transferring data and data files between architectures.
●
“Guidelines for Specific Scenarios” (page 46) contains tips for a variety of situations that are not common
to most applications.
●
“Preparing Vector-Based Code” (page 63) discusses the options available for those developers who have
high-performance computing needs.
This document contains the following appendixes:
●
“Rosetta” (page 65) describesthe translation processthat allows PowerPC binariesto run on an Intel-based
Macintosh computer.
●
“Architecture-Independent Vector-Based Code” (page 76) uses matrix multiplication as an example to
show how to write vector code with a minimum amount of architecture-specific coding.
●
“32-Bit Application Binary Interface” (page 84) provides information on where to find details.
●
“64-Bit Application Binary Interface” (page 85) provides information on where to find details.
Assumptions
The document assumes the following:
● Your application runs in Mac OS X.
Your application can use any of the Mac OS X development environments: Carbon, Cocoa, Java, or BSD
UNIX.
If your application runs in the UNIX operating system but not specifically in Mac OS X, you should first
read Porting UNIX/Linux Applications to Mac OS X .
If your application runs only in the Windows operating system, you should first read Porting to Mac OS X
from Windows Win32 API .
If you are new to Mac OS X, you should take a look at Mac OS X Technology Overview.
● You know how to use Xcode.
Introduction
Assumptions
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
9Currently Xcode is the only GUI tool available that compiles code to run universally.
If you are unfamiliar with Xcode, you might want to take a look at Xcode Workspace Guide .
If you have been using CodeWarrior, you should read Porting CodeWarrior Projects to Xcode .
Conventions
The term x86 is a generic term used in some parts of this book to refer to the class of microprocessors
manufactured by Intel. This book uses the term x86 as a synonym for IA-32 (Intel Architecture 32-bit).
Introduction
Conventions
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
10Architectural differences between Macintosh computersthat use Intel and PowerPC microprocessors can cause
existing PowerPC code to behave differently when built and run natively on a Macintosh computer that uses
an Intel microprocessor. The extent to which architectural differences affect your code depends on the level
of your source code. Most existing code is high-level source code that is not specific to the processor. If your
application falls into this category, you’ll find that creating a universal binary involves adjusting code in a few
places. Cocoa developers may need to make fewer adjustments than Carbon developers whose code was
ported from Mac OS 9 to Mac OS X.
Most code that uses high-level frameworks and that builds with GCC 4.0 in Mac OS X v10.4 will build with few,
if any, changes on an Intel-based Macintosh computer. The best approach for any developer in that situation
isto build the existing code as a universal binary, as described in this chapter, and then see how the application
runs on an Intel-based Macintosh. Find the places where the code doesn’t behave as expected and consult
the sections in this document that cover those issues.
Developers who use AltiVec instructions in their code or who intentionally exploit architectural differences for
optimization or other purposes will need to make the most code adjustments. These developers will probably
want to consult the rest of this document before building a universal binary. AltiVec programmers should read
“Preparing Vector-Based Code” (page 63).
This chapter describes how to use Xcode 2.2 to create a universal binary, providestroubleshooting information,
and listsrelevant build options. You’ll find that the software development workflow on an Intel-based Macintosh
computer is exactly the same as the software development workflow on a PowerPC-based Macintosh.
Build Assumptions
Before you build your code as a universal binary, you must ensure that:
● Your application already builds for Mac OS X. Your application can use any of the Mac OS X development
environments: Carbon, Cocoa, Java, or BSD UNIX.
● Your application uses the Mach-O executable format. Mach-O binaries are the only type of binary that run
natively on an Intel-based Macintosh computer. If you are already using the Xcode compilers and linkers,
your application is a Mach–O binary. Carbon applications based on the Code Fragment Manager Preferred
Executable Format (PEF) must be changed to Mach-O.
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
11
Building a Universal Binary● Your Xcode target is a native Xcode target. If it isn’t, in Xcode you can choose Project > Upgrade All Targets
in Project to Native.
● Your code project is ported to GCC 4.0. Xcode uses GCC 4.0 for targeting Intel-based Macintosh computers.
You may want to look at the document GCC Porting Guide to assess whether you need to make any
changes to your code to allow it to compile using GCC 4.0.
● You installed the Mac OS X v10.4 universal SDK. The installer places the SDK in this location:
/Developer/SDKs/MacOSX10.4u.sdk
Building Your Code
If you have already been using Xcode to build applications on a PowerPC-based Macintosh, you’ll see that
building your code on an Intel-based Macintosh computer is accomplished in the same way. By default, Xcode
compiles code to run on the architecture on which you build your Xcode project. Note that your Xcode target
must be a native target.
Tip: CodeWarrior users can read Xcode From a CodeWarrior Perspective for a discussion of the similarities
and differences between the two. Thisinformation can help you to put your CodeWarrior experience
to work in Xcode.
When you are in the process of developing your project, you’ll want to use the following settingsfor the Default
and Debug configurations:
● Keep the Architectures settings set to $(NATIVE_ARCH).
● Change the Mac OS X Deployment Target settings to Mac OS X 10.4.
● Make sure the SDKROOT setting is /Developer/SDKs/MacOSX10.4u.sdk.
You can set the SDK root for the project by following these steps:
1. Open your project in Xcode 2.2 or later.
Make sure that your Xcode target is a native target. If it isn’t, you can choose Project > Upgrade All Targets
in Project to Native.
2. In the Groups & Files list, click the project name.
3. Click the Info button to open the Info window.
4. In the General pane, in the Cross-Develop Using Target SDK pop-up menu, choose Mac OS X 10.4 (Universal).
Building a Universal Binary
Building Your Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
12If you don’t see Mac OS X 10.4 (Universal) as a choice, look in the following directory to make sure that
the universal SDK is installed:
/Developer/SDKs/MacOSX10.4u.sdk
If it’s not there, you’ll need to install this SDK before you can continue.
5. Click Change in the sheet that appears.
The Debug build configuration turns on ZeroLink, Fix and Continue, and debug-symbol generation, among
other settings, and turns off code optimization.
When you are ready to test your application on both architectures, you’ll want to use the Release configuration.
This configuration turns off ZeroLink and Fix and Continue. It also sets the code-optimization level to optimize
for size. As with the Default and Debug configurations, you’ll want to set the Mac OS X Deployment Target to
Mac OS X 10.4 and the SDK root to MacOSX10.4u.sdk. To build a universal binary, the Architecturessetting
for the Release configuration must be set to build on Intel and PowerPC.
You can change the Architectures setting by following these steps:
1. Open your project in Xcode 2.2 or later.
2. In the Groups & Files list, click the project name.
3. Click the Info button to open the Info window.
Building a Universal Binary
Building Your Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
134. In the Build pane (see Figure 1-1), choose Release from the Configuration pop-up menu.
Figure 1-1 The Build pane
Building a Universal Binary
Building Your Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
145. Select the Architectures setting and click Edit. In the sheet that appears, select the PowerPC and Intel
options, as shown in Figure 1-2.
Figure 1-2 Architectures settings
6. Close the Info window.
7. Build and run the project.
If your application doesn’t build, see “Debugging” (page 16).
If your application builds but does not behave as expected when you run it as a native binary on an Intel-based
Macintosh computer, see “Troubleshooting Your Built Application” (page 16).
If your application behaves as expected, don’t assume that it also works on the other architecture. You need
to test your application on both PowerPC Macintosh computers and Intel-based Macintosh computers. If your
application reads data from and writes data to disk, you should make sure that you can save files on one
architecture and open them on the other.
Building a Universal Binary
Building Your Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
15Note: Xcode 2.x has per-architecture SDK support. For example, you can target Mac OS X versions
10.3 and 10.4 for PowerPC while also targeting Mac OS X v10.4 and later for Intel-based Macintosh
computers.
For information on default compiler settings, architecture-specific options, and Autoconf macros, see “Build
Options” (page 18).
For information on building with version-specific SDKs for PowerPC (Mac OS X v10.3, v10.2, and so forth) while
still building a universal binary for both PowerPC and Intel-based Macintosh computers, see the following
resources:
● Using Cross Development in Xcode.
● Cross-Development and Universal Binaries in the Cross-Development Programming Guide provides details on
to create executable files that contain object code for both Intel-based and PowerPC-based Macintosh
computers.
Debugging
Xcode uses GDB for debugging,so you’ll wantto review the XcodeDebuggingGuide document. Xcode provides
a powerful user interface to GDB that lets you step through your code, set breakpoints and view variables,
stack frames, and threads.
Debugging with GDB—an Open Source document that explains how to use GDB—is another useful resource
that you’ll want to look at. It provides a lot of valuable information, including how to get a list of breakpoints
for debugging.
If you are moving code to GCC 4.0, you can find remedies for most linking issues and compiler warnings by
consulting GCC Porting Guide . You can find additional information on the GCC options you can use to request
or suppress warnings in Section 3.8 of the GNU C/C++/Objective-C 4.0.1 Compiler User Guide .
Troubleshooting Your Built Application
Here are the most typical behavior problems you’ll observe when your application runs natively on an Intel-based
Macintosh computer:
● The application crashes.
● There are unexpected numerical results.
Building a Universal Binary
Debugging
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
16● Color is displayed incorrectly.
● Text is not displayed properly—characters from the Last Resort font or unexpected Chinese or Japanese
characters appear.
● Files are not read or written correctly.
● Network communication does not work properly.
The first two problems in the list are typically caused by architecture-dependent code. On an Intel-based
Macintosh computer, an integer divide-by-zero exception resultsin a crash, but on PowerPC the same operation
returns zero. In these cases, the code must be rewritten in an architecture-independent manner. “Architectural
Differences” (page 21) discusses the major differences between Macintosh computers that use PowerPC and
Intel microprocessors. That chapter can help you determine which code is causing the crash or the unexpected
numerical results.
The last four problems in the list are most often caused by byte-ordering differences between architectures.
These problems are easily remedied by taking the byte order into account when you read and write data. The
strategies available for handling byte ordering, as well as an in-depth discussion of byte-ordering differences,
are provided in “Swapping Bytes” (page 26). Keep in mind that Mac OS X ensures that byte-ordering is correct
for anything it is responsible for. Apple-defined resources (such as menus) won’t result in problem behavior.
Custom resources provided by your application, however, can result in problem behavior. For example, if
images in your application seem to have a cyan tint, it’s quite likely that your application is writing alpha
channel data to the blue channel. For this specific issue, depending on the APIs that you are using, you’d want
to consult the sections “GWorlds” (page 50), “Pixel Data ” (page 58), or other graphics-related sections in
“Guidelines for Specific Scenarios” (page 46).
Apple engineers prepared a lot of code to run natively on an Intel-based Macintosh computer—including the
operating system, most Apple applications, and Apple tools. The guidelines in this book are the result of their
work. In addition to the more common issues discussed in “Architectural Differences” (page 21) and “Swapping
Bytes” (page 26), the engineers identified a number of narrowly focused issues. These are described in
“Guidelines for Specific Scenarios” (page 46). You will want to at least glance at this chapter to see if your code
can benefit from any of the information.
Building a Universal Binary
Troubleshooting Your Built Application
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
17Determining Whether a Binary Is Universal
You can determine whether an application has a universal binary by looking at the Kind entry in the General
section of the Info window for the application (see Figure 1-3). To open the Info window, click the application
icon and press Cmd-I.
Figure 1-3 The Chess application has a Universal binary
On an Intel-based Macintosh computer, when you double-click an application that doesn’t have an executable
for the native architecture, it might launch. Whether or not it launches depends on how compatible the
application is with Rosetta. For more information, see “Rosetta” (page 65).
Build Options
This section contains information on the build options that you need to be aware of when using Xcode 2.2
and later on an Intel-based Macintosh computer. It lists the default compiler options, discusses how to set
architecture-specific options, and provides information on using GNU Autoconf macros.
Building a Universal Binary
Determining Whether a Binary Is Universal
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
18Default Compiler Options
In Xcode 2.2 and later on an Intel-based Macintosh computer, the defaults for compiler flags that differ from
standard GCC distributions are listed in Table 1-1.
Table 1-1 Default values for compiler flags on an Intel-based Macintosh computer
Compiler flag Default value Specifies to
-mfpmath sse Use SSE instructions for floating-point math.
Enable the MMX, SSE, and SSE2 extensions in the Intel instruction
set architecture.
-msse2 On by default
Architecture-Specific Options
Most developers don’t need to use architecture-specific options for their projects.
In Xcode, to set one flag for an Intel-based Macintosh and another for PowerPC, you use the
PER_ARCH_CFLAGS_i386 and PER_ARCH_CFLAGS_ppc build settings variables to supply the
architecture-specific settings.
For example to set the architecture-specific flags -faltivec and -msse3, you would add the following build
settings:
PER_ARCH_CFLAGS_i386 = -msse3
PER_ARCH_CFLAGS_ppc = -faltivec
Similarly, you can supply architecture-specific linker flags using the OTHER_LDFLAGS_i386 and
OTHER_LDFLAGS_ppc build settings variables.
You can pass the -arch flag to gcc, ld, and as. The allowable values are i386 and ppc. You can specify both
flags as follows:
-arch ppc -arch i386
Formore information on architecture-specific options,see“BuildingUniversal Binaries”in Xcode Project Management
Guide .
Building a Universal Binary
Build Options
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
19Autoconf Macros
If you are compiling a project that uses GNU Autoconf and trying to build it for both PowerPC-based and
Intel-based Macintosh computers, you need to make sure that when the project configures itself, it doesn't
use Autoconf macros to determine the endian type of the runtime system. For example, if your project uses
the Autoconf AC_C_BIGENDIAN macro, the program won't work correctly when it is run on the opposite
architecture from the one you are targeting when you configure the project. To correctly build for both
PowerPC-based and Intel-based Macintosh computers, use the compiler-defined __BIG_ENDIAN__ and
__LITTLE_ENDIAN__ macros in your code.
For more information, see Using GNU Autoconf in Porting UNIX/Linux Applications to Mac OS X .
See Also
These resources provide information related to compiling and building applications, andmeasuring performance:
● Xcode Project Management Guide contains all the instructions needed to compile and debug any type of
Xcode project (C, C++, Objective C, Java, AppleScript, resource, nib files, and so forth).
● GCC Porting Guide provides advice for how to modify your code in ways that make it more compatible
with GCC 4.0.
● GNU C/C++/Objective-C 4.0.1 Compiler User Guide provides details about the GCC implementation. Xcode
uses the GNU compiler collection (GCC) to compile code.
The assembler (as) used by Xcode supports AT&T System V/386 assembler syntax in order to maintain
compatibility with the output from GCC. The AT&T syntax is quite different from Intel syntax. The major
differences are discussed in the GNU documentation.
● C++ Runtime Environment Programming Guide provides information on the GCC 4.0 shared C++ runtime
that is available in Panther 10.3.9 and later.
● Porting UNIX/Linux Applications to Mac OS X . Developers porting from UNIX and Linux applications who
want to compile a universal binary, will want to read the section Compiling for Multiple Architectures.
● Kernel Extension Programming Topics containsinformationaboutdebuggingKEXTsonIntel-basedMacintosh
computers.
● Performance tools. Shark, MallocDebug, ObjectAlloc, Sampler, Quartz Debug, Thread Viewer, and other
Apple-developed tools (some command-line, others use a GUI) are in the /Developer directory.
Command-line performance tools are in the /usr/bin directory.
● Code Size Performance Guidelines and Code Speed Performance Guidelines discuss optimization strategies
for a Mach-O executable.
Building a Universal Binary
See Also
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
20The PowerPC and the x86 architectures have some fundamental differences that can prevent code written for
one architecture from running properly on the other architecture. The extent to which you need to change
your PowerPC code so that it runs natively on an Intel-based Macintosh computer depends on how much of
your code is processor specific. This chapter describes the major differences between architectures, organized
alphabetically by topic. You can use the information to identify the parts of your code that are likely to be
problematic.
Alignment
All PowerPC instructions are 4 bytes in size and must be 4-byte aligned. x86 instructions are variable in size
(from 1 to >10 bytes), and as a consequence do not need to be aligned.
Bit Fields
The value of a signed, 1-bit bit field is either 0, 1, or –1, depending on the compiler, architecture, optimization
level, and so forth. Code that compares the value of a bit field to 1 may not work if the bit field is signed, so
you will want to use unsigned 1-bit bit fields. Keep in mind that the order of bit fields in memory can be
reversed between architectures.
For more information on issues related to endian format, see “Swapping Bytes” (page 26). See also “Archived
Bit Fields” (page 46) and “Structures and Unions” (page 25).
Byte Order
Microprocessor architectures commonly use two different byte-ordering methods(little-endian and big-endian)
to store the individual bytes of multibyte data formats in memory. This difference becomes critically important
if you try to read data from filesthat were created on a computer that uses a different byte ordering than yours.
You also need to consider byte ordering when you send and receive data through a network connection and
handle networking data. The difference in byte ordering can produce incorrect results if you do not account
for this difference. For example, the order of bytes in memory of a scalar type is architecture-dependent, as
shown in Listing 2-1 (page 22).
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
21
Architectural DifferencesListing 2-1 Code that illustrates byte-ordering differences
unsigned char charVal;
unsigned long value = 0x12345678;
unsigned long *ptr = &value;
charVal = *(unsigned char*)ptr;
On a processor that useslittle-endian addressing the variable charVal takes on the value 0x78. On a processor
that uses big-endian addressing the variable charVal takes on the value 0x12. To make this code
architecture-independent, change the last line in Listing 2-1 to the following:
charVal = (unsigned char)*ptr;
For a detailed discussion of byte ordering and strategies that you can use to account for byte-ordering
differences, see “Swapping Bytes” (page 26).
Calling Conventions
The x86 C-language calling convention (application binary interface, or ABI)specifiesthat argumentsto functions
are passed on the stack. The PowerPC ABI specifies that arguments to functions are passed in registers. Also,
x86 has far fewer registers, so many local variables use the stack for their storage. Thus, programming errors,
or other operationsthat access past the end of a local variable array or otherwise incorrectly manipulate values
on the stack may be more likely to crash applications on x86 systems than on PowerPC.
For detailed information about the IA-32 ABI, see Mac OS X ABI Function Call Guide . This document describes
the function-calling conventions used in all the architecturessupported by Mac OS X. See also “32-Bit Application
Binary Interface” (page 84).
Code on the Stack: Disabling Execution
Intel processors include a bit that prevents code from being executed on the stack. On Intel-based Macintosh
computers, this bit is always set to On.
Architectural Differences
Calling Conventions
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
22Data Type Conversions
For some data type conversions, such as casting a string to a long and converting a floating-point type to an
integer type, the PowerPC and x86 architectures perform differently. When the microprocessor converts a
floating-point type to an integer type, it discards the fractional part of the value. The behavior is undefined if
the value of the integral part cannot be represented by the integer type.
Listing 2-2 shows an example of the sort of code that is architecture-dependent. You would need to modify
this code to make it architecture-independent. On a PowerPC microprocessor, the variable x shown in the
listing is equal to 7fffffff or INTMAX. On an x86 microprocessor, the variable x is equal to 80000000 or
INTMIN.
Listing 2-2 Architecture-dependent code
int main (int argc, const char * argv[])
{
double a;
int x;
a = 5000000.0 * 6709000.5; // or any really big value
x = a;
printf("x = %08x \n",x);
return 0;
}
Data Types
A long double is 16 bytes on both architectures, but only 80 bits are significant in long double data types
on Intel-based Macintosh computers.
A bool data type is a single byte on an x86 system, but four bytes on a PowerPC architecture. Thissize difference
can cause alignment problems. You should use fixed-size data types to avoid alignment problems. (The bool
data type is not the Carbon Boolean type, which is a fixed size of 1 byte.)
Existing document formats that include the bool data type as part of a data structure that is written directly
to disk can be problematic because the data structure might not be laid out the same on both architectures.
If you update the data structure definition to use the UInt32 data type or another fixed-size four-byte data
type, the structure should then be portable, although you must swap bytes appropriately.
Architectural Differences
Data Type Conversions
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
23Divide-By-Zero Operations
An integer divide-by-zero operation isfatal on an x86 system but the operation continues on a PowerPC system,
where it returns zero. (A floating point divide-by-zero behaves the same on both architectures.) If you get a
crash log that mentions EXC_I386_DIV (divide by zero), your program divided by zero. Mod operations
perform a divide, so a mod-by-zero operation produces a divide-by-zero exception. To fix a divide-by-zero
exception, find the place in your program corresponding to that operation. Then add code that checks for a
denominator of zero before performing the divide operation.
For example, change this:
int a = b % c; // Divide by zero can happen here;
to this:
int a;
if (c != 0) {
a = b % c;
} else {
a = 0;
}
Extensible Firmware Interface (EFI)
Intel-based Macintosh computers use extensible firmware interface (EFI). EFI provides a flexible and adaptable
interface between Mac OS X and the platform firmware. This change should be transparent to most developers,
but may affect some, such as those who write boot drivers.
For more information on the EFI specification, see http://www.intel.com/technology/efi/
Floating-Point Equality Comparisons
The results of a floating-point equality comparison are architecture-dependent. Whether the comparison works
depends on a number of things, including the compiler, the surrounding code, all compiler flags in use
(particularly optimization flags), and the current floating-point mode for the thread. If your floating-point
comparison is currently working on PowerPC, you may need to inspect it on an Intel-based Macintosh computer.
Architectural Differences
Divide-By-Zero Operations
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
24You can use the GCC flag -Wfloat-equal to receive a warning for floating-point equality comparisons. For
details on this option, see Section 3.8 of the GNU C/C++/Objective-C 4.0.1 Compiler User Guide
Structures and Unions
The fields in a structure can be sensitive to their defined order. Structures must either be properly ordered or
accessed by the field name directly.
When a union has components that could be affected by byte order, use a form similar to that shown in Listing
2-3. Code that sets wch and then reads hi and lo as the high and low bytes of wch will work correctly. The
same is true for the reverse direction. Code that sets hi and lo and then reads wch will get the same value on
both architectures. For another example, see the WideChar union that’s defined in the IntlResources.h
header file.
Listing 2-3 A union whose components can be affected by byte order
union WChar{
unsigned short wch;
struct {
#if __BIG_ENDIAN__
unsigned char hi;
unsigned char lo;
#else
unsigned char lo;
unsigned char hi;
#endif
} s;
}
See Also
The ISO standard for the C programming language—ISO/IEC 9899—is a valuable reference that you can use
to investigate code portability issues, many of which may not be immediately obvious. You can find this
reference in a number of locations on the web, including:
http://www.iso.org/
Architectural Differences
Structures and Unions
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
25Two primary byte-ordering methods (or endian formats) exist in the world of computing. An endian format
specifies how to store the individual bytes of multibyte numerical data in memory. Big-endian byte ordering
specifies to store multibyte data with its most significant byte first. Little-endian byte ordering specifies to
store multibyte data with its least significant byte first. The PowerPC processor uses big-endian byte ordering.
The x86 processor family uses little-endian byte ordering. By convention, multibyte data sent over the network
uses big-endian byte ordering.
If your application assumes that data is in one endian format, but the data is actually in another, then it will
interpret the data incorrectly. You will want to analyze your code for routines that read multibyte data (16 bits,
32 bits, or 64 bits) from, or write multibyte data to, disk or to the network, as these routines are sensitive to
byte-ordering format. There are two general approaches for handling byte ordering differences: swap bytes
when necessary or use XML or another byte-order-independent data format such as those offered by Core
Foundation (CFPreferences, CFPropertyList, CFXMLParser).
Whether you should swap bytes or use a byte-order-independent data format depends on how you use the
data in your application. If you have an existing file format to support, the binary-compatible solution is to
accept the big-endian file format you have been using in your application, and write code that swaps bytes
when the file isread or written on an Intel-based Macintosh. If you don’t have legacy filesto support, you could
consider redesigning your file format to use XML (extensible markup language), XDR (external data
representation), or NSCoding (Objective C) to represent data.
This chapter describes why byte ordering matters, gives guidelines for swapping bytes, describes the
byte-swapping APIs available in Mac OS X, and providessolutionsfor most of the situations where byte ordering
matters.
Why Byte Ordering Matters
The example in this section is designed to show you why byte ordering matters. Take a look at the C data
structure defined in Listing 3-1. It contains a four-byte integer, a character string, and a two-byte integer. The
listing also initializes the structure.
Listing 3-1 A data structure that contains multibyte and single-byte data
typedef struct {
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
26
Swapping Bytesuint32_t myOptions;
char myStringArray [7];
short myVariable;
} myDataStructure;
myDataStructure aStruct;
aStruct.myOptions = 0xfeedface;
strcpy(aStruct.myStringArray, "safari");
aStruct.myVariable = 0x1234;
Figure 3-1 compares how this data structure is stored in memory on big-endian and little-endian systems. In
a big-endian system, memory is organized with the address of each data byte increasing from most significant
to leastsignificant. In a little-endian system, memory is organized with the address of each data byte increasing
from the least significant to the most significant.
Figure 3-1 Big-endian byte ordering compared to little-endian byte ordering
0x00000004
0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
0x0000000F
fe
fe
ed
fa ed
fa
ce
ce
's' 's'
'a'
'a' 'a'
'a'
'f' 'f'
'r' 'r'
'i' 'i'
12
34 12
34
\0 \0
0x00000000
0x00000001
0x00000002
0x00000003
Address Data
*
*
*
Big-endian
0x00000004
0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
0x0000000F
0x00000000
0x00000001
0x00000002
0x00000003
Address Data
*
*
*
Little-endian
Padding bytes used to
maintain alignment
As you look at Figure 3-1, note the following:
Swapping Bytes
Why Byte Ordering Matters
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
27● Multibyte data, such as the 32-bit and 16-bit variables shown in the figure, are stored differently between
big-endian and little-endian systems. As you can see in the figure, big-endian systemsstore data in memory
so that the most significant byte of the data is stored in the address with the lowest value. Little-endian
systems store data in memory so that the most significant byte of the data is in the address with the
highest value. Hence, the least significant byte of the myOptions variable (0xce) is stored in memory
location 0x00000003 on the big-endian system while it is stored in memory location 0x00000000 on
the little-endian system.
● Single-byte data, such as the char values in the myStringArray character array, are stored in the same
memory location on either system regardless of the byte ordering format of the system.
● Each system pads bytes to maintain four-byte data alignment. Padded bytes in the figure are designated
by a shaded box that contains an asterisk.
The byte ordering of multibyte data in memory matters if you are reading data written on one architecture
from a system that uses a different architecture and you access the data on a byte-by-byte basis. For example,
if your application is written to access the second byte of the myOptions variable, then when you read the
data from a system that uses the opposite byte ordering scheme, you’ll end up retrieving the first byte of the
myOptions variable instead of the second one.
Suppose the example data values that are initialized by the code shown in Listing 3-1 are generated on a
little-endian system and saved to disk. Assume that the data is written to disk in byte-address order. When
read from disk by a big-endian system, the data is again laid out in memory asshown in Figure 3-1. The problem
is that the data is still in little-endian byte order even though it is interpreted on a big-endian system. This
difference causes the values to be evaluated incorrectly. In this example, the value of the field myOptions
should be 0xfeedface, but because of the incorrect byte ordering it is evaluated as 0xcefaedfe.
Note: The terms big-endian and little-endian come from Jonathan Swift’s eighteenth-century satire
Gulliver’s Travels. The subjects of the empire of Blefuscu were divided into two factions: those who
ate eggs starting from the big end and those who ate eggs starting from the little end.
Guidelines for Swapping Bytes
The following guidelines, along with the strategies provided later in this chapter, will help ensure optimal
byte-swapping code in your application.
● Keep data structures in native byte-order while in memory. Only swap bytes when you read data from
disk or write it to disk.
Swapping Bytes
Guidelines for Swapping Bytes
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
28● When possible, let the compiler do the work for you. For example, when you use function calls such as
the Core Foundation function CFSwapInt16BigToHost, the compiler determines whether the function
call does something for the processor you are targeting. If the code does nothing, the compiler won’t call
the function. Letting the compiler do the work is more efficient than using #ifdef statements.
●
If you must access a large file, consider arranging the data in a way that limits the byte swapping that you
must perform. For example, you can arrange the most frequently accessed data contiguously in the file.
Then, you need to read and swap bytes only for that chunk of data instead of for the entire data file.
● Use the __BIG_ENDIAN__ and __LITTLE_ENDIAN__ macros only if you must. Do not use macros that
check for a specific processor type, such as __i386__ and __ppc__.
● Choose a consistent byte-order approach and stick with it. That is, if you are reading and writing data from
disk on a regular basis, choose the endian format you want to use. This eliminates the need for you to
check the byte ordering of the data, and then to possibly have to swap the byte order.
● Be aware of which functions return big-endian data, and use them appropriately. These include the BSD
Sockets networking functions, the DNSServiceDiscovery functions (for example, TCP and UDP ports
are specified in network byte order), and the ColorSync profile functions (for which all data is big-endian).
The IconFamilyElement and IconFamilyResource data types (which also include the data types
IconFamilyPtr and IconFamilyHandle) are always big-endian. There may be other functions and
data types that are not listed here. Consult the appropriate API reference for information on data returned
by a function. For more information see “Network-Related Data” (page 34).
● Keep in mind that swapping bytes comes at a performance cost so swap them only when absolutely
necessary.
Byte-Swapping Routines
The APIs that provide byte-swapping routines are listed below. For most situations it’s best to use the routines
that match the framework you’re programming in. The Core Foundation and Foundation APIs have functions
for swapping floating-point values, while the other APIs listed do not.
● POSIX (Portable Operating System Interface) byte ordering functions (ntohl, htonl, ntohs, and htons)
are documented in Mac OS X Man Pages.
● Darwin byte ordering functions and macros are defined in the header file libkern/OSByteOrder.h.
Even though this header is in the kernel framework, it is acceptable to use it from high-level applications.
● Core Foundation byte-order functions are defined in the header file CoreFoundation/CFByteOrder.h
and described in the Byte-Order Utilities Reference . For details on using these functions, see the Byte
Swapping article in Memory Management Programming Guide for Core Foundation .
● Foundation byte-order functions are defined in the header file Foundation/NSByteOrder.h and
described in Foundation Framework Reference .
Swapping Bytes
Byte-Swapping Routines
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
29● The Core Endian API is defined in the header file CarbonCore/Endian.h and described in Core Endian
Reference .
Note: When you use byte-swapping routines, the compiler optimizes your code so that the routines
are executed only if they are needed for the architecture on which your code is running.
Byte-Swapping Strategies
The strategy for swapping bytes depends on the format of the data; there is no universal routine that can take
care of all byte ordering differences. Any program that needsto swap data must know the data type, the source
data endian order, and the host endian order.
This section lists byte-swapping strategies, organized alphabetically, for the following data:
●
“Constants” (page 30)
●
“Custom Apple Event Data” (page 31)
●
“Custom Resource Data” (page 31)
●
“Floating-Point Values” (page 32)
●
“Integers” (page 33)
●
“Network-Related Data” (page 34)
●
“OSType-to-String Conversions” (page 35)
●
“Unicode Text Files” (page 36)
●
“Values in an Array” (page 38)
Constants
Constants that are part of a compiled executable are in host byte order. You need to swap bytes for a constant
only if it is part of data that is not maintained natively or if the constant travels between hosts. In most cases
you can either swap bytes ahead of time or let the preprocessor perform any needed math by using shifts or
other simple operators.
If you are defining and populating a structure that must use data of a specific endian format in memory, use
the OSSwapConst macros and the OSSwap*Const variants defined in the libkern/OSByteOrder.h header
file. These macros can be used from high-level applications.
Swapping Bytes
Byte-Swapping Strategies
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
30Custom Apple Event Data
An Apple event is a high-level event that conforms to the Apple Event Interprocess Messaging Protocol. The
Apple Event Managersends Apple events between applications on the same computer or between applications
on remote computers. You can define your own Apple event data types, and send and receive Apple events
using the Apple Event Manager API.
Mac OS X manages system-defined Apple event data types for you, handling them appropriately for the
currently executing code. You don't need to perform any special tasks. When the data that your application
extracts from an Apple event is system-defined, the system swaps the data for you before giving the event to
your application to process. You will want to treat system-defined data types from Apple events as native
endian. Similarly, if you put native-endian data into an Apple event that you are sending, and it is a
system-defined data type, the receiver will be able to interpret the data in its own native endian format.
However, you must account for byte-ordering differences for the custom Apple event data types that you
define. You can accomplish this in one of the following ways:
● Write a byte-swapping callback routine (also known as a flipper) and provide it to the system. Whenever
the system determines that your Apple event data needs to be byte swapped it invokes your flipper to
ensure that the recipient of the data gets the data in the correct endian format. For details, see “Writing
a Callback to Swap Data Bytes” (page 38).
● Choose one endian format to use, regardless of architecture. Then, when you read or write your custom
Apple event data, use big-to-host and host-to-big routines,such asthe Core Foundation Byte Order Utilities
functions CFSwapInt16BigToHost and CFSwapInt16HostToBig.
Custom Resource Data
In Mac OS X, the preferred way to supply resources is to provide files in your application bundle that define
resources such as image files, sounds, localized text, and archived user-interface definitions. The resource data
types discussed in this section are those defined in Resource Manager-style files supported by Carbon. The
Resource Manager was created prior to Mac OS X. If your application uses Resource Manager-style resource
files, you should consider moving towards Mac OS X–style resources in your application bundle instead.
Resources typically include data that describes menus, windows, controls, dialogs, sounds, fonts, and icons.
Although the system defines a number ofstandard resource types(such as 'moov', used to specify a QuickTime
movie, and 'MENU', used to define menus) you can also create your own private resource types for use in your
application. You use the Resource Manager API to define resource data types and to get and set resource data.
Mac OS X keepstrack of resourcesin memory and allows your application to read or write resources. Applications
and system software interpret the data for a resource according to its resource type. Although you'll typically
let the operating system read resources (such as your application icon) for you, you can also call Resource
Manager functions directly to read and write resources.
Swapping Bytes
Byte-Swapping Strategies
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
31Mac OS X manages the system-defined resources for you, handling them appropriately for the currently
executing code. That is, if your application runs on an Intel-based Macintosh, Mac OS X swaps bytes so that
your application icon, menus, and other standard resources appear correctly. You don't need to perform any
special tasks. But if you define your own private resource data types for use in your application, you need to
account for byte-ordering differences between architectures when you read or write resource data from disk.
You can use either of the following strategies to handle custom Resource Manager-style resource data. Notice
that these are the same strategies used to handle custom Apple event data:
● Provide a byte-swapping callback routine for the system to invoke whenever the system determines your
resource data must be byte swapped. For details, see “Writing a Callback to Swap Data Bytes” (page 38).
● Always write your data using the same endian format, regardless of the architecture. Then, when you read
or write your custom resource data, use big-to-host and host-to-big routines, such as the Core Foundation
Byte Order Utilities CFSwapInt16BigToHost and CFSwapInt16HostToBig.
Note: If you are revising old code that marks resources with a preload bit, you should remove the
preload bit from any resources that must be byte swapped. In Mac OS X, the preload bit is almost
always unnecessary. If you cannot remove the preload bit, you should swap the resource data after
you read the resource. You will not be able to use a flipper callback to swap bytes automatically
because in Mac OS X a preload bit causes the resources to be read before any of the application
code runs.
Floating-Point Values
Core Foundation defines a set of functions and two special data types to help you work with floating-point
values. These functions allow you to encode 32- and 64-bit floating-point values in such a way that they can
later be decoded and byte swapped if necessary. Listing 3-2 shows you how to encode a 64-bit floating-point
number and Listing 3-3 shows how to decode it.
Listing 3-2 Encoding a 64-bit floating-point value
double d = 3.0;
CFSwappedFloat64 swappedDouble;
// Encode the floating-point value.
swappedDouble = CFConvertFloat64HostToSwapped(d);
// Call the appropriate routine to write swappedDouble to disk,
// send it to another process, etc.
write(myFile, &swappedDouble, sizeof(swappedDouble));
Swapping Bytes
Byte-Swapping Strategies
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
32The data types CFSwappedFloat32 and CFSwappedFloat64 contain floating-point values in a canonical
representation. A CFSwappedFloat data type is not itself a floating-point value, and should not be directly
used as one. You can however send one to another process, save it to disk, or send it over a network. Because
the format is converted to and from the canonical format by the conversion functions, there is no need for
explicit swapping. Bytes are swapped for you during the format conversion if necessary.
Listing 3-3 Decoding a 32-bit floating-point value
float f;
CFSwappedFloat32 swappedFloat;
// Call the appropriate routine to read swappedFloat from disk,
// receive it from another process, etc.
read(myFile, &swappedFloat, sizeof(swappedFloat));
f = CFConvertFloat32SwappedToHost(swappedFloat)
The NSByteOrder.h header file defines functions that are comparable to the Core Foundation functions
discussed here.
Integers
The system library byte-access functions, such as OSReadLittleInt16 and OSWriteLittleInt16, provide
generic byte swapping. These functions swap bytes if the native endian format is different from the endian
format of the destination. They are defined in the libkern/OSByteOrder.h header file.
Note: The OSReadXXX and OSWriteXXX functions provide higher performance than the OSSwapXXX
functions or any other functions in the higher-level frameworks.
Core Foundation provides three optimized primitive functions for swapping bytes— CFSwapInt16,
CFSwapInt32, and CFSwapInt64. All of the other swapping functions use these primitives to accomplish
their work. In general you don’t need to use these primitives directly.
Although the primitive swapping functions swap unconditionally, the higher-level swapping functions are
defined in such a way that they do nothing when swapping bytes is not required—in other words, when the
source and host byte orders are the same. For the integer types, these functions take the forms
CFSwapXXXBigToHost, CFSwapXXXLittleToHost, CFSwapXXXHostToBig, and CFSwapXXXHostToLittle,
where XXX is a data type such as Int32. For example, on a little-endian machine you use the function
CFSwapInt16BigToHost to read a 16-bit integer value from a network whose data is in network byte order
(big-endian). Listing 3-4 demonstrates this process.
Swapping Bytes
Byte-Swapping Strategies
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
33Listing 3-4 Swapping a 16-bit integer from big-endian to host-endian
SInt16 bigEndian16;
SInt16 swapped16;
// Swap a 16-bit value read from the network.
swapped16 = CFSwapInt16BigToHost(bigEndian16);
Suppose the integers are in the fields of a data structure. Listing 3-5 demonstrates how to swap bytes.
Listing 3-5 Swapping integers from little-endian to host-endian
// Swap the bytes of the values if necessary.
aStruct.int1 = CFSwapInt32LittleToHost(aStruct.int1)
aStruct.int2 = CFSwapInt32LittleToHost(aStruct.int2)
The code swaps bytes only if necessary. If the host is a big-endian architecture, the functions used in the code
sample swap the bytesin each field. The code does nothing when run on a little-endian machine—the compiler
ignores the code.
Network-Related Data
Network-related data typically uses big-endian format (also known as network byte order), so you may need
to swap bytes when communicating between the network and an Intel-based Macintosh computer. You
probably never had to adjust your PowerPC code when you transmitted data to, or received data from, the
network. On an Intel-based Macintosh computer you must look closely at your networking code and ensure
that you always send network-related data in the appropriate byte order. You must also handle data received
from the network appropriately, swapping the bytes of values to the endian format appropriate to the host
microprocessor.
You can use the following POSIX functions to convert between network byte order and host byte order. (Other
byte-swapping functions, such as those defined in the OSByteOrder.h and CFByteOrder.h header files,
can also be useful for handling network data.)
● network to host:
uint32_t ntohl (uint32_t netlong);
uint16_t ntohs (uint16_t netshort);
● host to network:
uint32_t htonl (uint32_t hostlong);
Swapping Bytes
Byte-Swapping Strategies
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
34uint16_t htons (uint16_t hostshort);
These functions are documented in Mac OS X Man Pages.
The sin_saddr.s_addr and sin_port fields of a sockaddr_in structure should always be in network byte
order. You can find out the appropriate endian format of any argument to a BSD networking function by
reading the man page documentation.
When advertising a service on the network, you use getsockname to get the local TCP or UDP port that your
socket is bound to, and then pass my_sockaddr.sin_port unchanged, without any byte swapping, to the
DNSServiceRegister function.
In CoreFoundation code, you can use the same approach. Use the CFSocketCopyAddress function as shown
below, and then pass my_sockaddr.sin_port unchanged, without any byte swapping, to the
DNSServiceRegister function.
CFDataRef addr = CFSocketCopyAddress(myCFSocketRef);
struct sockaddr_in my_sockaddr;
memmove(&my_sockaddr, CFDataGetBytePtr(addr), sizeof(my_sockaddr));
DNSServiceRegister( ... , my_sockaddr.sin_port, ...);
When browsing and resolving, the process is similar. The DNSServiceResolve function and the BSD Sockets
calls such as gethostbyname and getaddrinfo all return IP addresses and ports already in the correct byte
order so that you can assign them directly to your struct sockaddr_in and call connect to open a TCP
connection. If you byte-swap the address or port, then your program will not work.
The important point is that when you use the DNSServiceDiscovery API with the BSD Sockets networking APIs,
you do not need to swap anything. Your code will work correctly on both PowerPC and Intel-based Macintosh
computers as well as on Linux, Solaris, and Windows.
OSType-to-String Conversions
You can use the functions UTCreateStringForOSType and UTGetOSTypeFromString to convert an OSType
data type to or from a CFString object (CFStringRef data type). These functions are discussed in Uniform
Type Identifiers Overview and defined in the UTType.h header file, which is part of the Launch Services
framework.
When you use four-character literals, keep in mind that "abcd" != 'abcd'. Rather 'abcd' == 0x61626364.
You must treat 'abcd' as an integer and not string data, as 'abcd' is a shortcut for a 32-bit integer. (A
FourCharCode data type is a UInt32 data type.) The compiler does not swap this for you. You can use the
shift operator if you need to deal with individual characters.
Swapping Bytes
Byte-Swapping Strategies
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
35For example, if you currently print an OSType or FourCharCode type using the standard C printf-style
semantics, use
printf("%c%c%c%c", (char) (val >> 24), (char) (val >> 16),
(char) (val >> 8), (char) val)
instead of the following:
printf("%4.4s", (const char*) &val)
Unicode Text Files
Mac OS X often uses UTF-16 to encode Unicode; a UniChar data type is a double-byte value. As with any
multibyte data, Unicode characters are sensitive to the byte ordering method used by the microprocessor. A
byte order mark written to the beginning of a file informs the program reading the data which byte ordering
method was used to write the data. The Unicode standard states that in the absence of a byte order mark
(BOM) the data in a Unicode data file is to be taken as big-endian. Although a BOM is not mandatory, you
should make use of it to ensure that a file written on one architecture can be read from the other architecture.
The program can then act accordingly to make sure the byte ordering of the Unicode text is compatible with
the host.
Table 3-1 lists the standard byte order marks for UTF-8, UTF-16, and UTF-32. (Note that the UTF-8 BOM is not
used for endian issues, but only as a tag to indicate that the file is UTF-8.)
Table 3-1 Byte order marks
Byte order mark Encoding form
EF BB BF UTF-8
FF FE UTF-16/UCS-2, little endian
FE FF UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian
00 00 FE FF UTF-32/UCS-4, big endian
In practice, when your application reads a file, it does not need to look for a byte order mark nor does it need
to swap bytes as long as you follow these steps to read a file:
1. Map the file using mmap to get a pointer to the contents of the file (or string).
Swapping Bytes
Byte-Swapping Strategies
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
36Reading the entire file into memory ensures the best performance and is a prerequisite for the next step.
2. Generate a CFString object by calling the function CFStringCreateWithBytes with the
isExternalRepresentation parameter set to true, or call the function
CFStringCreateWithExternalRepresentation to generate a CFString, passing in an encoding of
kCFStringEncodingUnicode (for UTF-16) or kCFStringEncodingUTF8 (for UTF-8).
Either function interprets a BOM swaps bytes as necessary. Note that a BOM should not be used in memory;
its use is solely for data transmission (files, pasteboard, and so forth).
In summary, with respect to Unicode files, your application performs best when you follow these guidelines:
● Accept the BOM when taking UTF-16 or UTF-8 encoded files from outside the application.
● Use native-endian UniChar data types internally.
● Generate a BOM when writing UTF-16 to a file. Ideally, you only need to generate a BOM for an architecture
that uses little-endian format, but it is also acceptable to generate a BOM for an architecture that uses
big-endian format.
● When you put data on the Clipboard, make sure that 'utxt' data does not have a BOM. Only 'ut16'
data should have a BOM. If you use Cocoa to put an NSString object on the pasteboard, you don’t need
to concern yourself with a BOM.
For more information, see “UTF & BOM,” available from the Unicode website:
http://www.unicode.org/faq/utf_bom.html
The Apple Event Manager provides text constants that you can use to specify the type of your data. As of Mac
OS X v10.4, only two text constants are recommended:
● typeUTF16ExternalRepresentation, which specifies Unicode text in 16-bit external representation
with optional byte order mark (BOM). The presence of this constant guarantees that either there is a BOM
or the data is in UTF-16 big-endian format.
● typeUTF8Text, which specifies 8-bit Unicode (UTF-8 encoding).
The constant typeUnicodeText indicates utxt text data, in native byte ordering format, with an optional
BOM. This constant does not specify an explicit Unicode encoding or byte order definition.
The Scrap Manager provides the flavor type constant kScrapFlavorTypeUTF16External which specifies
Unicode text in 16-bit external representation with optional byte order mark (BOM).
Swapping Bytes
Byte-Swapping Strategies
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
37Values in an Array
The routine in Listing 3-6 shows an approach that you can use to swap the bytes of values in an array. On a
big-endian system, the compiler optimizes away the entire function; you don’t need to use #ifdef statements
to swap these sorts of arrays.
Listing 3-6 A routine for swapping the bytes of the values in an array
static inline void SwapUInt32ArrayBigToHost(UInt32 *array, UInt32 count) {
int i;
for(i = 0; i < count; i++) {
array[i] = CFSwapInt32BigToHost(array[i]);
}
}
Writing a Callback to Swap Data Bytes
You can provide a byte-swapping callback routine, also referred to as a flipper, to the system for custom
resource data, custom pasteboard data, and custom Apple event data. When you install a byte-swapping
callback, you specify which domain that the data type belongs to. There are two data domains—Apple event
and resource. The resource data domain specifies custom pasteboard data or custom resource data. If the
callback can be applied to either domain (Apple event and resource), you can specify that as well.
The Core Endian API defines a callback that you provide to swap bytes for custom resource and Apple event
data. You must provide one callback for each type of data you want to swap bytes. The prototype for the
CoreEndianFlipProc callback is:
typedef CALLBACK_API (OSStatus, CoreEndianFlipProc)
(OSType dataDomain,
OSType dataType,
short id,
void *dataPtr,
UInt32 dataSize,
Boolean currentlyNative,
void *refcon
);
Swapping Bytes
Writing a Callback to Swap Data Bytes
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
38The callback takes the following parameters:
● dataDomain—An OSType value thatspecifiesthe domain to which the flipper callback applies. The value
kCoreEndianResourceManagerDomain signifies that the domain is resource or pasteboard data. The
value kCoreEndianAppleEventManagerDomain signifies that the domain is Apple event data.
● dataType—The type of data that needs the callback to swap bytes for. This is the four-character code of
the resource type, pasteboard type, or Apple event.
● id—The resource id of the data type. This field is ignored if the dataDomain parameter is not
kCoreEndianResourceManagerDomain.
● dataPtr—On input, points to the data to be flipped. On output, points to the byte swapped data.
● dataSize—The size of the data pointed to by the dataPtr parameter.
● currentlyNative—A Boolean value that indicatesthe direction to swap bytes. The value true specifies
the data pointed to by the dataPtr parameter uses the byte ordering of the currently executing code.
On a PowerPC Macintosh, true specifiesthat the data isin big-endian format. On an Intel-based Macintosh,
true specifies that the data is in little-endian format.
● refcon—A 32-bit value that contains, or refers to, data needed by the callback.
The callback returns a result code that indicates whether bytes are swapped successfully. Your callback should
return noErr if the data is byte swapped without error and the appropriate result code to indicate an error
condition—errCoreEndianDataTooShortForFormat, errCoreEndianDataTooLongForFormat, or
errCoreEndianDataDoesNotMatchFormat. The result code you return is propagated through the appropriate
manager (Resource Manager (ResError) or Apple Event Manager) to the caller.
You do not need to swap bytes for quantities that are not numerical (such as strings, byte streams, and so
forth). You need to provide a callback only to swap bytes data types for which the order of bytes in a word or
long word are important. (For the preferred way to handle Unicode strings, see “Unicode Text Files” (page
36).)
Your callback should traverse the data structure that contains the data and swap bytes for:
● All counts and lengths so that array indexes are associated with the appropriate value
● All integers and longs so that when you read them into variables of a compatible type, you can operate
correctly on the values (such as numerical, offset, and shift operations)
The Core Endian API provides these functions for working with your callback:
● CoreEndianInstallFlipper registers your callback for the specified data type (custom resource or
custom Apple Event). After you register a byte-swapping callback for an application-defined resource data
type, then any time you call a Resource Manager function that operates on that resource type, the system
invokes your callback if it is appropriate to do so. (If your callback operates on pasteboard data, the system
Swapping Bytes
Writing a Callback to Swap Data Bytes
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
39also invokes the callback at the appropriate time.) Similarly, if you specify Apple event as the domain for
your callback, then any time you call an Apple Event Manager function that operates on that data type,
your callback is invoked when it is appropriate to do so.
● CoreEndianGetFlipper obtains the callback that is registered for the specified data type. You can call
this function to determine whether a flipper is available for a given data type.
● CoreEndianFlipData invokes the callback associated with the specified data type. You shouldn’t need
to call this function, because the system invokes your callback whenever it’s needed.
As an example, look at a callback for the custom resource type ('PREF') defined in Listing 3-7. The
MyPreferences structure is used to store preferences data on disk. The structure contains a number of values
and includes two instances of the RGBColor data type and an array of RGBColor values.
Listing 3-7 A declaration for a custom resource
#define kMyPreferencesType 'PREF'
struct MyPreferences {
SInt32 fPrefsVersion;
Boolean fHighlightLinks;
Boolean fUnderlineLinks;
RGBColor fHighlightColor;
RGBColor fUnderlineColor;
SInt16 fZoomValue;
char fCString[32];
SInt16 fCount;
RGBColor fPalette[];
};
You can handle the RGBColor data type by writing a function thatswaps bytesin an RGBColor data structure,
such as the function MyRGBSwap, shown in Listing 3-8. This function calls the Core Endian macro
EndianS16_Swap to swap bytes for each of the values in the RGBColor data structure. The function doesn’t
need to check for the currently executing system because the function is never called unless the values in the
Swapping Bytes
Writing a Callback to Swap Data Bytes
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
40RGBColor data type need to have their bytesswapped. The MyRGBSwap function is called by the byte-swapping
callback routine (shown in Listing 3-9 (page 41)) that’s provided to handle the custom 'PREF' resource (that
is defined in Listing 3-7 (page 40)).
Listing 3-8 A flipper function for RGBColor data
static void MyRGBSwap (RGBColor *p)
{
p->red = Endian16_Swap(p->red);
p->blue = Endian16_Swap(p->blue);
p->green = Endian16_Swap(p->green);
}
Listing 3-9 shows a byte-swapping callback for the custom 'PREF' resource. An explanation for each numbered
line of code appears following the listing. Note that the flipper checks for data that is malformed or is of an
unexpected length. If the data passed into the flipper routine is a shorter length than the flipped type is
normally, or (for example) contains garbage data instead of an array count, the flipper must be careful not to
read or write data beyond the end of the passed-in data. Instead, the routine returns an error.
Listing 3-9 A flipper for the custom 'PREF' resource
#define kCurrentVersion 0x00010400
static OSStatus MyFlipPreferences (OSType dataDomain, // 1
OSType dataType, // 2
short id, // 3
void * dataPtr, // 4
UInt32 dataSize, // 5
Boolean currentlyNative, // 6
void* refcon) // 7
{
UInt32 versionNumber;
OSStatus status = noErr;
MyPreferences* toFlip = (MyPreferences*) dataPtr; // 8
int count, i;
Swapping Bytes
Writing a Callback to Swap Data Bytes
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
41if (dataSize < sizeof(MyPreferences))
return errCoreEndianDataTooShortForFormat; // 9
if (currentlyNative) // 10
{
count = toFlip->fCount;
versionNumber = toFlip->fPrefsVersion;
toFlip->fPrefsVersion = Endian32_Swap (toFlip->fPrefsVersion);
toFlip->fCount = Endian16_Swap (toFlip->fCount);
toFlip->fZoomValue = Endian16_Swap (toFlip->fZoomValue);
}
else // 11
{
toFlip->fPrefsVersion = Endian32_Swap (toFlip->fPrefsVersion);
versionNumber = toFlip->fPrefsVersion;
toFlip->fCount = Endian16_Swap (toFlip->fCount);
toFlip->fZoomValue = Endian16_Swap (toFlip->fZoomValue);
count = toFlip->fCount;
}
if (versionNumber != kCurrentVersion) // 12
return errCoreEndianDataDoesNotMatchFormat;
MyRGBSwap (&toFlip->fHighlightColor); // 13
MyRGBSwap (&toFlip->fUnderlineColor); // 14
if (dataSize < sizeof(MyPreferences) + count * sizeof(RGBColor))
return errCoreEndianDataTooShortForFormat; // 15
for(i = 0; i < count; i++)
{
MyRGBSwap (&toFlip->fPalette[i]); // 16
}
return status; // 17
}
Swapping Bytes
Writing a Callback to Swap Data Bytes
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
42Here’s what the code does:
1. The system passesto your callback the domain to which the callback applies. You define the domain when
you register the callback using the function CoreEndianInstallFlipper.
2. The system passesto your callback the resource type you defined for the data. In this example, the resource
type is 'PREF'.
3. The system passes to your callback the resource ID of the data type. If the data is not a resource, this value
is 0.
4. The system passes to your callback a pointer to the resource data that needs to have its bytes swapped.
In this case, the pointer refers to a MyPreferences data structure.
5. The system passes to your callback the size of the data pointed to by the pointer described in the previous
step.
6. The system passes to your callback true if the data in the buffer passed to the callback is in the byte
ordering of the currently executing code. On a PowerPC Macintosh, when currentlyNative is true,
the data isin big-endian order. On a Macintosh that uses an Intel microprocessor, when currentlyNative
is true, the data is in little-endian order. Your callback needs to know this value, because if your callback
uses a value in the data buffer to decide how to process other data in the buffer (for example, the count
variable shown in the code), you must know whether that value needs to be flipped before the value can
be used by the callback.
7. The system passes to your callback a pointer that refers to application-specific data. In this example, the
callback doesn’t require any application-specific data.
8. Defines a variable for the MyPreferences data type and assigns the contents of the data pointer to the
newly-defined toFlip variable.
9. Checks the static-length portion of the structure. If the size is less than it should be, the routine returns
the error errCoreEndianDataTooLongForFormat.
10. If currentlyNative is true, saves the count value to a local variable and then swaps the bytes for the
other values in the MyPreferences data structure. You must save the count value before you swap
because you need it for an iteration later in the function. The fact that currentlyNative is true indicates
that the value does not need to be byte swapped if it is used in the currently executing code. However,
the value does need to be swapped to be stored to disk.
The values are swapped using the appropriate Core Endian macros.
11. If currentlyNative is false, flips the values in the MyPreferences data structure before it saves the
count value to a local variable. The fact that currentlyNative is false indicates that the count value
needs to have its bytes swapped before it can be used in the callback.
12. Checks to make sure the version of the data structure is supported by the application. If the version is not
supported, then your callback would not swap bytes for the data and would return the result
errCoreEndianDataDoesNotMatchFormat.
Swapping Bytes
Writing a Callback to Swap Data Bytes
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
4313. Callsthe MyRGBSwap function (shown in Listing 3-8 (page 41)) to swap the bytes of the fHighlightColor
field of the data structure.
14. Calls the MyRGBSwap function to swap the bytes of the fUnderlineColor field of the data structure.
15. Checks the data size to make sure that it is less than it should be. If not, the routine returns the error
errCoreEndianDataTooLongForFormat.
16. Iterates through the elements in the fPalette array, calling the MyRGBSwap function to swap the bytes
of the data in the array.
17. Returns noErr to indicate that the data is flipped without error.
Although the sample performs some error checking, it does not include all the error-handling code that it
could. When you write a flipper you may want to include such code.
Note: The callback does not flip any of the Boolean values in the MyPreferences data structure
because these are single character values. The callback also ignores the C string.
You register a byte-swapping callback routine by calling the function CoreEndianInstallFlipper. You
should register the callback when your application callsitsinitialization routine or when you open your resources.
For example, you would register the flipper callback shown in Listing 3-9 (page 41) using the following code:
OSStatus status = noErr;
status = CoreEndianInstallFlipper (kCoreEndianResourceManagerDomain,
kMyPreferencesType,
MyFlipPreferences,
NULL);
The system invokes the callback for the specified resource type and data domain when currentlyNative is
false at the time a resource is loaded and true at the time the resource is set to be written. For example,
the sample byte-swapping callback gets invoked any time the following line of code is executed in your
application:
MyPreferences** hPrefs = (MyPreferences**) GetResource ('PREF', 128);
After swapping the bytes of the data, you can modify it as much as you’d like.
Swapping Bytes
Writing a Callback to Swap Data Bytes
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
44When the Resource Manager reads a resource from disk, it looks up the resource type (for example, 'PREF')
in a table of byte-swapping routines. If a callback is installed for that resource type, the Resource Manager
invokes the callback if it is appropriate to do so. Similar actions are taken when the Resource Manager writes
a resource to disk. It finds the appropriate routine and invokes the callback to swap the bytes of the resource
if it is appropriate to do so.
When you copy or drag custom data from an application that has a callback installed for pasteboard data, the
system invokes your callback at the appropriate time. If you copy or drag custom data to a native application,
the data callback is not invoked. If you copy or drag custom data to a nonnative application, the system invokes
your callback to swap the bytes of the custom data. If you paste or drop custom data into your application
from a nonnative application, and a callback exists for that custom data, the system invokes the callback at
the time of the paste or drop. If the custom data is copied or dragged from another native application, the
callback is not invoked.
Note that different pasteboard APIs use different type specifiers. The Scrap Manager and Drag Manager use
OSType data types. The Pasteboard Manager uses Uniform Type Identifiers(UTI), and the NSPasteboard class
uses its own type mechanism. In each case, the type is converted by the system to an OSType data type to
discover if there is a byte-swapping callback for that type.
Apple event data types are typically swapped to network byte order when sent over a network. The callback
you install is called only if a custom data type that you define issent to another machine, or if another machine
sends Apple event data to your application. The byte ordering of Apple events on the network is big-endian.
For casesin which the system would not normally invoke your byte-swapping callback, you can call the function
CoreEndianFlipData to invoke the callback function installed for the specified data type and domain.
See Also
The following resources are available in the ADC Reference Library:
● Byte-Order Utilities Reference describes the Core Foundation byte order utilities API.
● Byte Swapping, in Core Foundation Memory Management, shows how to swap integers and floating-point
values using Core Foundation byte-order utilities.
● File-System Performance Guidelines provides information useful for mapping Unicode files to memory.
Swapping Bytes
See Also
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
45This chapter lists an assortment ofscenariosthat relate to a specific technology or API. Although many of these
scenarios are uncommon, you will want to at least glance at the topics to determine whether anything applies
to your application. The topics are organized alphabetically.
Aliases
Aliases are big-endian on all systems. Applications that add extra information to the end of an AliasHandle
must ensure that the extra data is always endian-neutral or of a defined endian type, preferably big-endian.
The AliasRecord data structure is opaque when building your application with the Mac OS X v10.4(Universal)
SDK. Code that formerly accessed the userType field of an AliasRecord must use the Alias Manager functions
GetAliasUserType, GetAliasUserTypeFromPtr, SetAliasUserType, or SetAliasUserTypeFromPtr.
Code that formerly accessed the aliasSize field of an AliasRecord must use the functions GetAliasSize
or GetAliasSizeFromPtr.
These Alias Manger functions are available in Mac OS X v10.4 and later. For more information,see Alias Manager
Reference .
Archived Bit Fields
For cross platform portability, avoid using bit fields. It’s best not to use the NSArchiver class to archive any
structures that contain bit fields as integers. Individual values are stored in the archives in an architecture and
compiler dependent manner. In cases where archives already contain such structures, you can read a structure
correctly by changing its declaration so that the bit fields are swapped appropriately
Automator Scripts
AppleScript actions are platform-independent and do not need any changes to run on Intel-based Macintosh
computers. However, any action that contains Cocoa code, whether it is a solely Cocoa action or an action that
uses both AppleScript and Cocoa code, must be built as a universal binary to run correctly on both architectures.
For more information, see Automator Programming Guide .
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
46
Guidelines for Specific ScenariosBit Shifting
When you shift a value by the width of itstype or more, the fill bits are undefined regardless of the architecture.
In fact, two different compilers on the same architecture could differ on the value of y after these two statements:
uint32_t x = 0xDEADBEEF;
uint32_t y = x >> 32;
Bit Test, Set, and Clear Functions: Carbon and POSIX
Don’t mix using the C bitwise operators with the Carbon functions BitTst, BitSet, and BitClr and the
POSIX macros setbit, clrbit, isset, and isclr. If you consistently use the Carbon and POSIX functions
and avoid the C bitwise operators, your code will function properly. Keep in mind, however, that you must use
the Carbon and POSIX functions on the correct kind of data. The Carbon and POSIX functions perform a
byte-by-byte traversal, which causes problems on an Intel-based Macintosh when they operate on data types
that are larger than 1 byte. You can use these functions only on a pointer to a string of endian-neutral bytes.
When you need to perform bit manipulation on integer values you should use functions such as (int32 &
(1 << 26)) instead of BitTst(&int32, 5L).
You’ll encounter problems when you use the function BitTst to test for 24-bit mode. For example, the
following bit test returns false, which indicates that the process is running in 24-bit mode, or at least that
the code is not running in 32-bit mode. The POSIX equivalents perform similarly:
Gestalt(gestaltAddressingModeAttr, &gestaltResult);
if (!(BitTst(&gestaltResult,31L)) ) /*If 24 bit
You can use any of the bit testing, setting, and clearing functions if you pass a pointer to data whose byte
order is fixed. Used in this way, these functions behave the same on both architectures.
For more information, see the ToolUtils.h header file in the Core Services framework and Mathematical
and Logical Utilities Reference .
CPU Subtype
Don't try to build a binary for a specific CPU subtype. Since the CPU subtype for Intel-based Macintosh computers
is generic, you can't use it to check for specific functionality. If your application requires information about
specific CPU functionality, use the sysctlbyname function, providing an appropriate selector. See Mac OS X
Man Pages for information on using sysctlbyname.
Guidelines for Specific Scenarios
Bit Shifting
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
47Dashboard Widgets
Dashboard widgetstypically contain platform-independent elementssuch as HTML,JavaScript, CSS, and image
files. If you create a widget that contains only these elements, it should work on both PowerPC and Intel-based
Macintosh computers without any modification on your part. However, if your widget contains a plug-in, you
must build the plug-in as a universal binary for it to run natively on an Intel-based Macintosh computer.
For more information, see Dashboard Programming Topics.
Deprecated Functions
Many deprecated functions, such as those that use PICT + PS data, have byte swapping issues. You may
want to replace deprecated functions at the same time you prepare your code to run as a universal binary.
You’ll not only solve byte swapping issues, but your code will use functions that ultimately benefit future
development.
A function that is deprecated has an availability statement in its header file that states the version of Mac OS
X in which the function is deprecated. Many API reference documents provide a list of deprecated functions.
In addition, compiler warnings for deprecated functions are on by default in Xcode 2.2 and later.
Disk Partitions
The standard disk partition format on an Intel-based Macintosh computer differsfrom the disk partition format
of a PowerPC-based Macintosh computer. If your application depends on the partitioning details of the disk,
it may not behave as expected. Partitioning details can affect tools that examine the hard disk at a low level.
By default, internal hard drives on Intel-based Macintosh computers use the GUID Partition Table (GPT)scheme
and external drives use the Apple Partition Map (APM) partition scheme. To create an external USB or FireWire
disk that can boot an Intel-based Macintosh computer,select the GPT disk partition scheme option using Apple
Disk Utility. Starting up an Intel-based Macintosh using an APM disk is not supported.
Double-Precision Values: Bit-by-Bit Sensitivity
Although both architectures are IEEE 754 compliant, there are differences in the rounding procedure used by
each when operating on double-precision numbers. If your application is sensitive to bit-by-bit values in
double-precision numbers, be aware that the same computation performed on each architecture may produce
a different numerical result.
Guidelines for Specific Scenarios
Dashboard Widgets
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
48For more information, see Volume 1 of the Intel developer software manuals, available from the following
website:
http://developer.intel.com/design/Pentium4/documentation.htm
Finder Information and Low-Level File System Operations
If your code operates on the file system at a low level and handles Finder information, keep in mind that the
file system does not swap bytes for the following information:
● The finderInfo field in the HFSPlus data structures HFSCatalogFolder, HFSPlusCatalogFolder,
HFSCatalogFile, HFSPlusCatalogFile, and HFSPlusVolumeHeader.
● The FSPermissionInfo data structure, which is used when the constant kFSCatInfoPermissions is
passed to the HFSPlus functions FSGetCatalogInfo and FSGetCatalogInfoBulk.
The value of multibyte fields on disk always uses big-endian format. When running on a little-endian system,
you must swap the bytes of any multibyte fields.
The getattrlist function retrieves the metadata associated with a file. The getxattr function, added in
Mac OS X v10.4, retrieves extended attributes—those that are an extension of the basic set of attributes. When
using the getxattr function to access the legacy attribute "com.apple.FinderInfo", note that as with
getattrlist, the information returned by this call is not byte swapped. (For more information on the
getxattr and getattrlist functions see Mac OS X Man Pages.)
Note: This issue pertains only to code that operates below CarbonCore. Calls to Carbon functions
such as FSGetCatalogInfo are not affected.
FireWire Device Access
The FireWire bus uses big-endian format. If you are developing a universal binary version of an application
that accesses a FireWire device, see “FireWire Device Access on an Intel-Based Macintosh” in FireWire Device
Interface Guide for a discussion of the issues you can encounter.
Guidelines for Specific Scenarios
Finder Information and Low-Level File System Operations
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
49Font-Related Resources
Font-related resource types (FOND, NFNT, sfnt, and so forth) are in big-endian format on both PowerPC and
Intel-based Macintosh computers. If your application accesses font-related resource types directly, you must
swap the fields of font-related resource types yourself.
The following functionsfrom the ATS for Fonts API obtain font resourcesthat are returned in big-endian format:
● ATSFontGetTableDirectory
● ATSFontGetTable
● ATSFontGetFontFamilyResource
The following functions from the Font Manager API obtain font resources that are returned in big-endian
format. Note that Font Manager API is based on QuickDraw technology, which was deprecated in Mac OS X
v10.4.
● FMGetFontTableDirectory
● FMGetFontTable
● FMGetFontFamilyResource
GWorlds
When the QuickDraw function NewGWorld allocates storage for the pixel buffer, and the depth parameter is
16 or 32 bits, the byte ordering within each pixel matters. The pixelFormat field of the PixMap data structure
can have the values k16BE555PixelFormat or k16LE555PixelFormat for 2-byte pixels, and
k32ARGBPixelFormat or k32BGRAPixelFormat for 4-byte pixels. (These constants are defined in the
Quickdraw.h header file.) By default, NewGWorld always creates big-endian pixel formats
(k16BE555PixelFormat or k32ARGBPixelFormat), regardless of the endian format of the system.
For best performance, it is generally preferable for you to use a pixel format that corresponds to the native
byte ordering of the system. When you pass kNativeEndianPixMap in the flags parameter to NewGWorld,
the byte ordering of the pixel format is big-endian on big-endian systems, and little-endian on little-endian
systems.
Guidelines for Specific Scenarios
Font-Related Resources
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
50Note: QuickDraw does not support little-endian pixel formats on big-endian systems.
You can use the GWorld pixel storage as input to the Quartz function CGBitmapContextCreate or as a data
provider for the Quartz function CGImageCreate. The byte ordering of the source pixel format needs to be
communicated to Quartz through additional flags in the bitmapInfo parameter. These flags are defined in
the CGImage.h header file. Assuming that your bitmapInfo parameter is already set up, you now need to
combine it (by using a bitwise OR operator) with kCGBitmapByteOrder16Host or
kCGBitmapByteOrder32Host if you created the GWorld with a kNativeEndianPixMap flag. Similarly, you
should use kCGBitmapByteOrder16Big or kCGBitmapByteOrder32Big when you know that your pixel
byte order is big-endian.
Java Applications
Pure Java applications do not require any code changes to run on Intel-based Macintosh computers. However,
Java applications that interface with PowerPC-based native code will not run successfully using Rosetta on
Intel-based Macintosh computers.
Specifically, the following must be built as universal binaries:
●
JNI libraries built for PowerPC-based Macintosh computers are not loaded using Rosetta because the Java
Virtual Machine has already launched without using Rosetta.Java applicationsfail on Intel-based Macintosh
computers when trying to load PowerPC-only binaries.
● Native applications that use the VM Invocation Interface to start a Java Virtual Machine must be built as
universal binaries to run on Intel-based Macintosh computers. The Java VM must run natively; attempts
by an application running using Rosetta to instantiate a JVM fail.
Formoreinformation,seeTechnicalQ&AQA1295:JavaonIntel-basedMacintoshComputers intheADCReference
Library.
Java I/O API (NIO)
The I/O API (NIO) that was introduced in JDK 1.4 allows the use of native memory buffers. If you are a Java
programmer who uses this API, you may need to revise your code. NIO byte buffers have a byte ordering that
by default is big-endian. If you have Java code originally written for Mac OS X on PowerPC, when you create
java.nio.ByteBuffers you should call the function ByteBuffer.order(ByteOrder.nativeOrder())
to set the byte order of the buffers to the native byte order for the current architecture. If you fail to do this,
you will obtain flipped data when you read multibyte data from the buffer using JNI.
Guidelines for Specific Scenarios
Java Applications
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
51Machine Location Data Structure
The Memory Management Utilities data type MachineLocation containsinformation about the geographical
location of a computer. The ReadLocation and WriteLocation functions use the geographic location record
to read and store the geographic location and time zone information in extended parameter RAM.
If your code uses the MachineLocation data structure, you need to change it to use the
MachineLocation.u.dls.Delta field that was added to the structure in Mac OS X version 10.0.
To be endian-safe, change code that uses the old field:
MachineLocation.u.dlsDelta = 1;
to use the new field:
MachineLocation.u.dls.Delta = 1;
The gmtDelta field remains the same—the low 24 bits are used. The order of assignment is important. The
following is incorrect because it overwrites results:
MachineLocation.u.dls.Delta = 0xAA; // u = 0xAAGGGGGG; G=Garbage
MachineLocation.u.gmtDelta = 0xBBBBBB; // u = 0x00BBBBBB;
This is the correct way to assign the values:
MachineLocation.u.gmtDelta = 0xBBBBBB; // u = 0x00BBBBB;
MachineLocation.u.dls.Delta = 0xAA; // u = 0xAABBBBBB;
For more details see Memory Management Utilities Reference .
Mach Processes: The Task for PID Function
The task_for_pid function returns the task associated with a process ID (PID). This function can be called
only if the process is owned by the procmod group or if the caller is root.
Guidelines for Specific Scenarios
Machine Location Data Structure
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
52Metrowerks PowerPlant
You can use PowerPlant on an Intel-based Macintosh computer by downloading the PowerPlant framework
available from http://sourceforge.net/projects/open-powerplant. This Open Source version of the PowerPlant
Framework for Mac OS X includes support for Intel and GCC 4.0.
Multithreading
Multithreading is a technique used to improve performance and enhance the perceived responsiveness of
applications. On computers with one processor, this technique can allow a program to execute multiple pieces
of code independently . On computers with more than one processor, multithreading can allow a program to
execute multiple pieces of code simultaneously . If your application is single-threaded, consider threading your
application to take advantage of hardware multithreading processor capabilities. If your application is
multithreaded, you’ll want to ensure that the number of threads is not hard coded to a fixed number of
processors.
Dual-core technology improves performance by providing two physical cores within a single physical processor
package. Multiprocessor and dual-core technology all exploit thread-level parallelism to improve application
and system responsiveness and to boost processor throughput.
When you prepare code to run as a universal binary, the multithreading capabilities of the microprocessor are
transparent to you. This is true whether your application is threaded or not. However, you can optimize your
code to take advantage of the specific way hardware multithreading is implemented for each architecture.
Objective-C: Messages to nil
In Objective-C, it is valid to send a message to a nil object. The Objective-C runtime assumes that the return
value of a message sent to a nil object is nil, as long as the message returns an object or any integer scalar
of size less than or equal to sizeof(void*).
On Intel-based Macintosh computers, messages to a nil object always return 0.0 for methods whose return
type is float, double, long double, or long long. Methods whose return value is a struct, as defined
by the Mac OS X ABI Function Call Guide to be returned in registers, will return 0.0 for every field in the data
structure. Other struct data types will not be filled with zeros. This is also true under Rosetta. On PowerPC
Macintosh computers, the behavior is undefined.
Guidelines for Specific Scenarios
Metrowerks PowerPlant
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
53Objective-C Runtime: Sending Messages
The information in this section is only for developers who use the Objective-C runtime library, which is used
primarily for developing bridge layers between Objective-C and other languages, or for low-level debugging.
Most developers do not need to use the Objective-C runtime library directly when programming in Objective-C.
If your application directly callsthe Objective-C runtime function objc_msgSend_stret, you need to change
your code to have it work correctly on an Intel-based Macintosh.
The x86 ABI for struct-return functions differs from the ABI for struct-address-as-first-parameter
functions, but the two ABIs are identical on PowerPC. When you call objc_msgSend_stret, you must cast
the function to a function pointer type that uses the expected struct return type. The same applies for calls
to objc_msgSendSuper_stret.
For other details on the ABI, see “32-Bit Application Binary Interface” (page 84).
If your application directly calls the Objective-C runtime function objc_msgSend, you should always cast to
the appropriate return value. For instance, for a method that returns a BOOL data type, the following code
executes properly on a PPC Macintosh but might not on an Intel-based Macintosh computer:
BOOL isEqual = objc_msgSend(string, @selector("isEqual:"), otherString);
To ensure that the code does executes properly on an Intel-based Macintosh computer, you would change
the code to the following:
BOOL isEqual = ((BOOL (*)(id, SEL, id))objc_msgSend)(object, @selector("isEqual:"),
otherString);
Open Firmware
Macintosh computers that use an Intel microprocessor do not use Open Firmware. Although many parts of
the I/O registry are present and work as expected, information that is provided by Open Firmware on a PowerPC
Macintosh (such as a complete device tree) is not available in the I/O registry on a Macintosh that uses an Intel
microprocessor. You can obtain some of the information from IODeviceTree by using the sysctlbyname or
sysctl commands.
Guidelines for Specific Scenarios
Objective-C Runtime: Sending Messages
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
54OpenGL
When defining an OpenGL image or texture, you need to provide a type thatspecifiesto OpenGL which format
the texture is in. Most of these functions (for example, glTexImage2D) take format and type_ parameters
that specify how the texture is laid out on disk or in memory. OpenGL supports a number of different image
types; some are endian-neutral but others are not.
Note: The advice in this section is for applications that can not reorder their pixel data because of
the type of image loaders they are using.
For example, a common image format is GL_RGBA with a type of GL_UNSIGNED_BYTE. This means that the
image has a byte that specifies the red color data followed by a byte that specifies the green color data, and
so forth. Thisformat is not endian-specific; the bytes are in the same order on all architectures. Another common
image format is GL_BGRA, often specified by the type GL_UNSIGNED_INT_8_8_8_8_REV. This type means
that every 4 bytes of image data are interpreted as an unsigned int, with the most significant 8 bits
representing the alpha data, the next most significant 8 bits representing the red color data, and so forth.
Because this format is specific to the integer format of the host, the format is interpreted differently on
little-endian systemsthan on big-endian systems. When using GL_UNSIGNED_INT_8_8_8_8_REV, the OpenGL
implementation expects to find data in byte order ARGB on big-endian systems, but BGRA on little-endian
systems.
Because there is no explicit way in OpenGL to specify a byte order of ARGB with 32-bit or 16-bit packed pixels
(which are common image formats on Macintosh PowerPC computers), many applications specify GL_BGRA
with GL_UNSIGNED_INT_8_8_8_8_REV. This practice works on a big-endian system such as PowerPC, but
the format is interpreted differently on a little-endian system and causes images to be rendered with incorrect
colors.
Applications that have this problem are those that use the OpenGL host-order format types, but assume that
the data referred to is always big-endian. These types include, but are not limited to the following:
GL_SHORT
GL_UNSIGNED_SHORT
GL_INT
GL_UNSIGNED_INT
GL_FLOAT
GL_DOUBLE
GL_UNSIGNED_BYTE_3_3_2
GL_UNSIGNED_SHORT_4_4_4_4
GL_UNSIGNED_SHORT_5_5_5_1
Guidelines for Specific Scenarios
OpenGL
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
55GL_UNSIGNED_INT_8_8_8_8
GL_UNSIGNED_INT_10_10_10_2
GL_UNSIGNED_SHORT_5_6_5
GL_UNSIGNED_BYTE_2_3_3_REV
GL_UNSIGNED_SHORT_5_6_5_REV
GL_UNSIGNED_SHORT_4_4_4_4_REV
GL_UNSIGNED_SHORT_1_5_5_5_REV
GL_UNSIGNED_INT_8_8_8_8_REV
GL_UNSIGNED_INT_2_10_10_10_REV
If your application does not use any of these types, it is unlikely to have any problems with OpenGL. Note that
an application is not necessarily incorrect to use one of these types. Many applications might already present
host-order data tagged with one of these formats, especially with existing cross-platform code, because the
Mac OS X implementation behaves the same way as a Windows implementation.
If an application incorrectly uses one of these types, its OpenGL textures and images are rendered with incorrect
colors. For example, red might appear green, or the image might appear to be tinted purple.
You can fix this problem in one of the following ways:
1. If the images are generated or loaded algorithmically, change the code to generate the texturesin host-order
format that matches what OpenGL expects. For example, a JPEG decoder can be modified to store its
output in 32-bit integers instead of four 8-bit bytes. The resulting data is identical on big-endian systems,
but on a little-endian system, the bytes are in a different order. This matches the OpenGL expectation,
and the existing OpenGL code continues to work on both architectures. This is the preferred approach.
In many cases, rewriting the algorithms may prove a significant amount of work to implement and debug.
If that’s the case, an approach that asks OpenGL to interpret the texture data differently might be a better
approach for you to take.
2. If the application uses GL_UNSIGNED_INT_8_8_8_8_REV or GL_UNSIGNED_INT_8_8_8_8, it can switch
between them based on the architecture. Since these two types are exactly byte swapped versions of the
same format, using GL_UNSIGNED_INT_8_8_8_8_REV on a big-endian system is equivalent to using
GL_UNSIGNED_INT_8_8_8_8 on a little-endian system and vice versa. Code might look as follows:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGRA_EXT,
#if __BIG_ENDIAN__
GL_UNSIGNED_INT_8_8_8_8_REV,
#else
Guidelines for Specific Scenarios
OpenGL
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
56GL_UNSIGNED_INT_8_8_8_8,
#endif
data);
If this is a common idiom, it might be easiest to define it as a macro that can be used multiple times:
#if __BIG_ENDIAN__
#define ARGB_IMAGE_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
#else
#define ARGB_IMAGE_TYPE GL_UNSIGNED_INT_8_8_8_8
#endif
/* later on, use it like this */
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0, GL_BGRA_EXT,
ARGB_IMAGE_TYPE, data);
Note that switching between GL_UNSIGNED_INT_8_8_8_8_REV and GL_UNSIGNED_INT_8_8_8_8
works only for this particular 32-bit packed-pixel data type. For 16-bit ARGB data stored using
GL_UNSIGNED_SHORT_1_5_5_5_REV, there is no corresponding byte swapped type. Keep in mind that
GL_UNSIGNED_SHORT_5_5_5_1 is not a replacement for GL_UNSIGNED_SHORT_1_5_5_5_REV on an
Intel-based Macintosh computer. The format isinterpreted as bit-order arrrrrbbbbbggggg on a big-endian
system, and as bit order ggrrrrrabbbbbggg on a little-endian system.
3. If you can’t use the previous approaches, you should either generate/load your data in the native endian
format of the systemand use the same pixel type on both architectures or use the GL_UNPACK_SWAP_BYTES
pixel store setting to instruct OpenGL to swap the bytes of any texture loaded on a little-endian system.
This setting applies to all texture or image calls made with the current OpenGL context, so it needs to be
set only once per OpenGL context, for example:
#if __LITTLE_ENDIAN__
glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
#endif
This method causes images that use the problematic formats to be loaded as they would be on PowerPC.
You should consider this option only if no other option is available. Enabling this option causes OpenGL
to use a slower rendering path than normal. Performance-sensitiveOpenGL applications may be significantly
Guidelines for Specific Scenarios
OpenGL
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
57slower with this option enabled than with it off. Although this method can get an OpenGL-based program
up and running in as little time as possible, it is highly recommended that you use one of the other two
methods.
Note: Using the GL_UNSIGNED_INT_8_8_8_8 format for GL_BGRA data is not necessarily faster
than using GL_UNPACK_SWAP_BYTES. In some cases, performance decreases for rendering textures
that use either of those two methods compared to using a data type such as
GL_UNSIGNED_INT_8_8_8_8_REV. It’s advisable that you use Shark or other tools to analyze the
performance of your OpenGL code and make sure that you are not encountering particularly bad
cases.
OSAtomic Functions
The kernel extension functions OSDequeueAtomic and OSEnqueueAtomic are not available on an Intel-based
Macintosh.
For more information on these functions, see Kernel Framework Reference .
Pixel Data
Applications that store pixel data in memory using ARGB format must take care in how they read data. If the
code is not written correctly, it’s possible to misread the data; the result is colors or alpha that appear wrong.
If you see colors that appear wrong when your application runs on an Intel-based Macintosh computer, the
following strategy may help you identify where pixel data is being read incorrectly.
Create a test image whose pixel data is easy to identify. For example, set each pixel so that alpha is ff, red is
aa, green is bb, and blue is cc. Then read that image into your application. Figure 4-1 shows such an image.
Figure 4-1 A test image that can help locate the source of color problems
It's also helpful to go through your code and cast pixel data to the unsigned char data type.
Guidelines for Specific Scenarios
OSAtomic Functions
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
58Start with the portion of your code that reads the image. Use the following GDB command to examine the
pixel data as hexadecimal bytes:
x/xb
This command prints the specified number of bytes, starting with the first byte of the first pixel. You should
easily be able to see whether what’s displayed onscreen matches the values of the pixels in the test image. If
the values you see do not match the test image, then you've identified the misreading problem. If the values
match, then you need to identify other portions of your code that modify or transform pixel data, and inspect
the pixel data after each transformation.
PostScript Printing
If you are using the Carbon Printing Manager, note that the PICT with PostScript ('pictwps') printing path
is not available on Intel-based Macintosh computers except under Rosetta. If you need only to support EPS
data you can use Quartz drawing together with the function PMCGImageCreateWithEPSDataProvider to
allow the inclusion of EPS data as part of your Quartz drawing. If you need to generate the PostScript code for
your application drawing you should use the function PMPrinterPrintWithFile.
Quartz Bitmap Data
The Quartz constants shown in Table 4-1 specify the byte ordering of pixel formats. These constants, which
are defined in the CGImage.h header file, are used in the bitmapInfo parameter. To specify byte ordering
to Quartz, use a bitwise OR operator to combine the appropriate constant with the bitmapInfo parameter.
Table 4-1 Quartz constants that specify byte ordering
Constant Specifies
kCGBitmapByteOrderMask The byte order mask
kCGBitmapByteOrder16Big 16-bit, big-endian format
kCGBitmapByteOrder32Big 32-bit, big-endian format
kCGBitmapByteOrder16Little 16-bit, little-endian format
kCGBitmapByteOrder32Little 32-bit, little-endian format
kCGBitmapByteOrder16Host 16-bit, host-endian format
kCGBitmapByteOrder32Host 32-bit, host-endian format
Guidelines for Specific Scenarios
PostScript Printing
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
59QuickDraw Routines
If you have existing code that directly accesses the picFrame field of the QuickDraw Picture data structure,
you should use the QuickDraw function QDGetPictureBounds to get the appropriately swapped bounds for
a Picture. This function is available in Mac OS X version 10.3 and later. Its prototype is as follows:
Rect * QDGetPictureBounds(
PicHandle picH,
Rect *outRect)
If you have existing code that uses the QuickDraw DeltaPoint function or the HIToolbox PinRect function
(defined in MacWindows.h), make sure that you do not cast the function result to a Point data structure. The
horizontal difference is returned in the low 16 bits, and the vertical difference is returned in the high 16 bits.
You can obtain the horizontal and vertical values by using code similar to the following:
Point pointDiff;
SInt32 difference = DeltaPoint (p1, p2);
pointDiff.h = LoWord (difference);
pointDiff.v = HiWord (difference);
Tip: The best solution is to convert your QuickDraw code to Quartz 2D. QuickDraw was deprecated
starting in Mac OS X v10.4. For help with converting to Quartz 2D, see Quartz Programming Guide
for QuickDraw Developers.
QuickTime Components
The Component Manager recognizes which architectures are supported by a component by looking at the
'thng' resource for the component, not the architecture of the file. You must specify the appropriate
architectures in the 'thng' resource. To accomplish this, in the .r file where you define the 'thng' resource,
modify your ComponentPlatformInfo array to look similar to the following:
#if defined(__ppc__)
kMyComponentFlags, kMyCodeType, kMyCodeID, platformPowerPCNativeEntryPoint,
#endif
#if defined(__i386__)
kMyComponentFlags, kMyCodeType, kMyCodeID, platformIA32NativeEntryPoint,
Guidelines for Specific Scenarios
QuickDraw Routines
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
60#endif
Then, rebuild your component. For details, see “Building a Universal Binary” (page 11).
QuickTime Metadata Functions
When you call the function QTMetaDataGetItemProperty and the type of the key whose value you are
retrieving is code, the data returned is an OSType, not a buffer of four characters. (You can determine the key
type by calling the function QTMetaDataGetItemPropertyInfo.) To ensure that your code runs properly
on both PowerPC and Intel-based Macintosh computers, you must use a correctly-typed buffer so that the
endian format of the data returned to you is correct. If you supply a buffer of the wrong type, for example a
buffer of UInt8 instead of a buffer of OSType, the endian format of the data returned in the buffer will be
wrong on Intel-based Macintosh Computers.
Runtime Code Generation
If your application generates code at runtime, keep in mind that the compiler assumes that the stack must be
16-byte aligned when calling into Mac OS X libraries or frameworks. 16-byte stack alignment is enforced on
Intel-based Macintosh computers, which means that you need to ensure that your code is 16-byte aligned to
avoid having your application crash.
For more information, see Mac OS X ABI Function Call Guide .
Spotlight Importers
A Spotlight importer is a plug-in bundle that extracts information from files created by an application. The
Spotlight engine uses importers to gather information about new and existing files. Spotlight importers are
not compatible with Rosetta. To run an importer on an Intel-based Macintosh as well as on a PowerPC-based
Macintosh, you must compile it as a universal binary.
For more information on Spotlight, see Spotlight Overview andSpotlight Importer Programming Guide .
Guidelines for Specific Scenarios
QuickTime Metadata Functions
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
61System-Specific Predefined Macros
The C preprocessor hasseveral predefined macros whose purpose isto indicate the type ofsystem and machine
in use. If your code uses system-specific predefined macros, evaluate whether you really need to use them. In
most cases applications need to know the capabilities available on a computer and not the specific system or
machine on which the application is running. For example, if your application needs to know whether it is
running on a little-endian or big-endian microprocessor, you should use the __BIG_ENDIAN__ or
__LITTLE_ENDIAN__ macros or the Core Foundation function CFByteOrderGetCurrent. Do not use the
__i386__ and __ppc__ macros for this purpose.
See GNU C 4.0 Preprocessor User Guide for additional information.
USB Device Access
USB uses little-endian format. If you are developing a universal binary version of an application that accesses
a USB device,see “USB Device Accessin an Intel-Based Macintosh”in USBDevice Interface Guide for a discussion
of the issues you may encounter.
See Also
In addition to the following resources, check the ADC website periodically for updates and technical notes that
might address other specific situations:
● Quartz Programming Guide for QuickDraw Developers which provides information on moving code from
the deprecated QuickDraw API to Quartz
●
IA-32 Intel Architecture Optimization Reference Manual , available from:
http://developer.intel.com/design/pentium4/manuals/index_new.htm
Guidelines for Specific Scenarios
System-Specific Predefined Macros
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
62This chapter is relevant only for those developers who want to start writing vector-based code or whose
applications already directly use the AltiVec extension to the PowerPC instruction set. AltiVec instructions,
because they are processor specific, must be replaced on Intel-based Macintosh computers. You can choose
from these two options:
● Use the Accelerate framework. This is the recommended option because the framework provides a layer
of abstraction that lets you perform vector-based operations without needing to use low-level vector
instructions yourself. See “Accelerate Framework” (page 63).
● Port AltiVec code to the Intel instruction set architecture (ISA). This solution is available for developers
who have performance needs that can’t be met by using the Accelerate framework. See “Rewriting AltiVec
Instructions” (page 64).
Accelerate Framework
The Accelerate framework, introduced in Mac OS X v10.3 and expanded in v10.4, is a set of high-performance
vector-accelerated libraries. You don’t need to be concerned with the architecture of the target machine
because the routines in this framework abstract the low-level details. The system automatically invokes the
appropriate instruction set for the architecture that your code runs on.
This framework contains the following libraries:
● vImage is the Apple image processing framework that includes high-level functions for image
manipulation—convolutions, geometric transformations, histogram operations, morphological
transformations, and alpha compositing—as well as utility functions that convert formats and perform
other operations. See vImage Programming Guide .
● vDSP provides mathematical functions that perform digital signal processing (DSP) for applications such
asspeech,sound, audio, and video processing, diagnostic medical imaging, radarsignal processing,seismic
analysis, and scientific data processing. The vDSP functions operate on real and complex data types and
include data type conversions, fast Fourier transforms (FFTs), and vector-to-vector and vector-to-scalar
operations.
● vMathLib contains vector-accelerated versions of all routines in the standard math library. See vecLib
Framework Reference .
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
63
Preparing Vector-Based Code● LAPACK is a linear algebra package that solves simultaneous sets of linear equations, tackles eigenvalue
and singular solution problems, and determines least-squares solutions for linear systems.
● BLAS (Basic Linear Algebra Subroutines) performs basic vector and matrix computations.
● vForce contains routines that take matrices as input and output arguments, rather than single variables.
Rewriting AltiVec Instructions
Most of the tasksrequired to vectorize for AltiVec—restructuring data structures, designing parallel algorithms,
eliminating branches, and so forth— are the same as those you’d need to perform for the Intel architecture.
If you already have AltiVec code, you’ve already completed the fundamental vectorization work needed to
rewrite your application for the Intel architecture. In many casesthe translation process will be smooth, involving
direct or nearly direct substitution of AltiVec intrinsics with Intel equivalents.
The MMX, SSE, SSE2, and SSE3 extensions provide analogous functionality to AltiVec. Like the AltiVec unit,
these extensions are fixed-sized SIMD (Single Instruction Multiple Data) vector units, capable of a high degree
of parallelism. Just as for AltiVec, code that is written to use the Intel ISA typically performs many times faster
than scalar code.
Before you start rewriting AltiVec instructionsfor the Intel instruction set architecture, read AltiVec/SSE Migration
Guide . It outlines the key differences between architectures in terms of vector-based programming, gives an
overview of the SIMD extensions on x86, lists what you need to do to build your code, and provides an in-depth
discussion on alignment and other relevant issues.
See Also
The following resources are relevant for rewriting AltiVec instructions for the Intel architecture:
●
“Architecture-Independent Vector-Based Code” (page 76) shows how to write a fast matrix-multiplication
function with a minimum of architecture-specific coding.
●
Intel software manuals describe the x86 vector extensions:
http://developer.intel.com/design/Pentium4/documentation.htm
● Perf-Optimization-dev is a list for discussions on analyzing and optimizing performance in Mac OS X. You
can subscribe at:
http://lists.apple.com/mailman/listinfo/perfoptimization-devlists.apple.com
Preparing Vector-Based Code
Rewriting AltiVec Instructions
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
64Rosetta is a translation process that runs a PowerPC binary on an Intel-based Macintosh computer—it allows
applications to run as nonnative binaries. Many, but not all, applications can run translated. Applications that
run translated will never run as fast as they run as a native binary because the translation process itself incurs
a processing cost.
How compatible your application is with Rosetta depends on the type of application it is. An application such
as a word processor that has a lot of user interaction and low computational needs is quite compatible. An
application that requires a moderate amount of user interaction and has some high computational needs or
that uses OpenGL is most likely also quite compatible. One that has intense computing needs isn’t compatible.
Thisincludes applicationsthat need to repeatedly compute fast Fourier transforms(FFTs), that compute complex
models for 3-D modeling, or that compute ray tracing.
To the user, Rosetta istransparent. Unlike Classic, when the user launches an application, there aren’t any visual
cues to indicate that the application is translated. The user may perceive that the application is slow to start
up or that the performance is slower than it is on a PowerPC-based Macintosh. The user can discover whether
an application has only a PowerPC binary by looking at the Finder information for the application. (See
“Determining Whether a Binary Is Universal” (page 18).)
This appendix discusses the sorts of applications that can run translated, describes how Rosetta works, points
out special considerations for translated applications, shows how to force an application to run translated
using Rosetta, describes how to programmatically detect whether an application is running nonnatively, and
provides troubleshooting information if your application won’t run translated but you think that it should.
What Can Be Translated?
Rosetta is designed to translate currently shipping applicationsthat run on a PowerPC with a G3 or G4 processor
and that are built for Mac OS X. That includes CFM as well as Mach-O PowerPC applications.
Rosetta does not run the following:
● Applications built for any version of the Mac OS earlier than Mac OS X —that means Mac OS 9, Mac OS 8,
Mac OS 7, and so forth
● The Classic environment
● Screen savers written for the PowerPC architecture
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
65
Rosetta● Code that inserts preferences in the System Preferences pane
● Applications that require a G5 processor
● Applications that depend on one or more PowerPC-only kernel extensions
● Kernel extensions
●
Java applications with JNI libraries
●
Java applets in applications that Rosetta can translate; that means a web browser that Rosetta can run
translated will not be able to load Java applets.
Rosetta does not support precise exceptions. Any application that relies on register states being accurate in
exception handlers or signal handlers will not function properly running with Rosetta.
For more information on the limitations of Java applications using Rosetta, see “Java Applications” (page 51)
and Technical Q &A QA1295, Java on Intel-based Macintosh Computers, which is in the ADC Reference Library.
How It Works
When an application launches on an Intel-based Macintosh computer, the kernel detects whether the application
has a native binary. If the binary is not native, the kernel launches the binary using Rosetta. If the application
is one of those that can be translated, it launches and runs, although not as fast as it would as a native binary.
Behind the scenes, Rosetta translates and executes the PowerPC binary code.
Rosetta runs in the same thread of control as the application. When Rosetta starts an application, it translates
a block of application code and executes that block. As Rosetta encounters a call to a routine that it has not
yet translated, it translatesthe needed routine and continuesthe execution. The result is a smooth and continual
transitioning between translation and execution. In essence, Rosetta and your application work together in a
kind of symbiotic relationship.
Rosetta optimizes translated code to deliver the best possible performance on the nonnative architecture. It
uses a large translation buffer, and it caches code for reuse. Code that getsreused repeatedly in your application
benefits the most because it needs to be translated only once. The system uses the cached translation, which
is faster than translating the code again.
Special Considerations
Rosetta must run the entire process when it translates. This hasimplicationsfor applicationsthat use third-party
plug-ins or any other component that must be loaded at the time your application launches. All parts
(application, plug-ins, or other components needed at launch time) must run either nonnatively or natively.
Rosetta
How It Works
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
66For example, if your application is built as a universal binary, but it uses a plug-in that has only a PowerPC
binary, then your application needs to run nonnatively on an Intel-based Macintosh computer to use the
nonnative plug in.
Rosetta takes endian issuesinto account when it translates your application. Multibyte data that moves between
your application and any system processis automatically handled for you—you don’t need to concern yourself
with the endian format of the data.
The following kinds of multibyte data can have endian issues if the data moves between:
● Your translated application and a native process that’s not a system process
● A custom pasteboard provided by your translated application and a custom pasteboard provided by a
native application
● Data files or caches provided by your translated application and a native application
You might encounter thisscenario while developing a universal binary. For example, if you’ve created a universal
binary for a server processthat your application relies on, and then test that process by running your application
as a PowerPC binary, the endian format of the data passed from the server to your application would be wrong.
You encounter the same problem if you create a universal binary for your application, but have not yet done
so for a server process needed by the application.
Structures that the system defines and that are written using system routines will work correctly. But consider
the code in Listing A-1.
Listing A-1 A structure whose endian format depends on the architecture
typedef struct
{
int x;
int y;
} data_t
void savefile(data_t data, int filehandle)
{
write(filehandle, &data, sizeof(data));
}
Rosetta
Special Considerations
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
67When run using Rosetta, the application will write a big-endian structure; x and y are both written as big-endian
integers. When the application runs natively on an Intel-based Macintosh, it will write out a little-endian
structure; x and y are written as little-endian integers. It is up to you to define data formats on disk to be of a
canonical endian format. Endian-specific data formats are fine as long as any application that reads or write
the data understands what the endian format of the data is and treats the data appropriately.
Keep in mind that private frameworks and plug-ins can also encounter these sorts of endian issues. If a private
framework creates a cache or data file, and the framework is a universal binary, then it will try to access the
cache from both native and PPC processes. The framework either needs to account for the endian format of
the cache when reading or writing data or needs to have two separate caches.
Forcing an Application to Run Translated
Assuming that the application meetsthe criteria described in “What Can Be Translated?” (page 65), applications
that have only a PowerPC binary automatically run as translated on an Intel-based Macintosh. For testing
purposes, there are several ways that you can force applications that have a universal binary to launch as a
PowerPC binary on an Intel-based Macintosh:
● For applications, “Make a Setting in the Info Window” (page 69)
● For command-line tools “Use Terminal” (page 69)
● For an application that you are writing, “Modify the Property List” (page 69)
● Programmatically, “Use the sysctlbyname Function” (page 70)
Each of these methods is described in this section.
Rosetta
Forcing an Application to Run Translated
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
68Make a Setting in the Info Window
You can manually set which binary to execute on an Intel-based Macintosh computer by selecting the “Open
using Rosetta” option in the Info window of the application. To set the option, click the application icon, then
press Command-I to open the Info window. Make the setting, as shown in Figure A-1.
Figure A-1 The Info window for the Calculator application
Use Terminal
You can force a command-line tool to run translated by entering the following in Terminal:
ditto -arch ppc /tmp/toolname
/tmp/toolname
Modify the Property List
You can set the default setting for the “Open using Rosetta” option by adding the following key to the
Info.plist of your application bundle:
Rosetta
Forcing an Application to Run Translated
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
69LSPrefersPPC
This key informs the system that the application should launch as a PowerPC binary and causes the “Open
using Rosetta” checkbox to be selected. You might find this useful if you ship an application that has plug-ins
that are not native at the time of shipping.
Use the sysctlbyname Function
The exec_affinity routine in Listing A-2 controls the preferred CPU type for sublaunched processes. You
might find this routine useful if you are using fork and exec to launch applications from your application.
The routine calls the sysctlbyname function with the "sysctl.proc_exec_affinity" string, passing a
constant that specifies the CPU type. Pass CPU_TYPE_POWERPC to launch the PPC executable in a universal
binary. (For information on sysctlbyname see Mac OS X Man Pages.)
Listing A-2 A routine that controls the preferred CPU type for sublaunched processes
cpu_type_t exec_affinity (cpu_type_t new_cputype)
{
cpu_type_t ret;
cpu_type_t *newp = NULL;
size_t sz = sizeof (cpu_type_t);
if (new_cputype != 0)
newp = &new_cputype;
if (sysctlbyname("sysctl.proc_exec_affinity",
&ret, &sz, newp, newp ? sizeof(cpu_type_t) : 0) == -1) {
fprintf(stderr, "exec_affinity: sysctlbyname failed: %s\n",
strerror(errno));
return -1;
}
return ret;
}
Rosetta
Forcing an Application to Run Translated
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
70Preventing an Application from Opening Using Rosetta
To prevent an application from opening using Rosetta, add the following key to the Info.plist:
LSRequiresNativeExecution
Programmatically Detecting a Translated Application
Some developers may want to determine programmatically whether an application is running using Rosetta.
For example, a developer writing device interface code may need to determine whether the user client is using
the same endian format as the kernel.
Listing A-3 is a utility routine that can call the sysctlbyname function on a process ID (pid). If you pass a
process ID of 0 to the routine, it performs the call on the current process. Otherwise it performs the call on the
processspecified by the pid value that you pass. (For information on sysctlbyname see MacOS XMan Pages.)
Listing A-3 A utility routine for calling the sysctlbyname function
static int sysctlbyname_with_pid (const char *name, pid_t pid,
void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
if (pid == 0) {
if (sysctlbyname(name, oldp, oldlenp, newp, newlen) == -1) {
fprintf(stderr, "sysctlbyname_with_pid(0): sysctlbyname failed:"
"%s\n", strerror(errno));
return -1;
}
} else {
int mib[CTL_MAXNAME+1];
size_t len = CTL_MAXNAME;
if (sysctlnametomib(name, mib, &len) == -1) {
fprintf(stderr, "sysctlbyname_with_pid: sysctlnametomib failed:"
"%s\n", strerror(errno));
return -1;
}
Rosetta
Preventing an Application from Opening Using Rosetta
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
71mib[len] = pid;
len++;
if (sysctl(mib, len, oldp, oldlenp, newp, newlen) == -1) {
fprintf(stderr, "sysctlbyname_with_pid: sysctl failed:"
"%s\n", strerror(errno));
return -1;
}
}
return 0;
}
The is_pid_native routine shown in Listing A-4 (page 72) calls the sysctlbyname_with_pid routine,
passing the string "sysctl.proc_native". The is_pid_native routine determines whether the specified
process is running natively or translated. The routine returns:
● 0 if the process is running translated using Rosetta
● 1 if the process is running natively on a PowerPC- or Intel-based Macintosh
● –1 if an unexpected error occurs
Listing A-4 A routine that determines whether a process is running natively or translated
int is_pid_native (pid_t pid)
{
int ret = 0;
size_t sz = sizeof(ret);
if (sysctlbyname_with_pid("sysctl.proc_native", pid,
&ret, &sz, NULL, 0) == -1) {
if (errno == ENOENT) {
return 1;
}
fprintf(stderr, "is_pid_native: sysctlbyname_with_pid failed:"
"%s\n", strerror(errno));
return -1;
}
Rosetta
Programmatically Detecting a Translated Application
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
72return ret;
}
Note: On Mac OS X v10.4, the proc_native call fails if the current user doesn't own the process
being checked.
Troubleshooting
If you are convinced that your application falls into the category of those that should be able to run using
Rosetta but it doesn’t run or it has unexpected behavior, you can follow the procedure in this section to debug
your application. This procedure works only for PowerPC binaries—not for a universal binary—and is the only
way you can debug a PowerPC binary on an Intel-based Macintosh. Xcode debugging does not work for
translated applications.
To debug a PowerPC binary on an Intel-based Macintosh, follow these steps:
1. Open Terminal.
2. Enter the following two lines:
For tcsh:
setenv OAH_GDB YES
//.app/Contents/MacOS/
For bash:
export OAH_GDB=YES
//.app/Contents/MacOS/
Rosetta
Troubleshooting
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
73You’ll see the Rosetta process launch and wait for a port connection (Figure A-2).
Figure A-2 Rosetta listens for a port connection
3. Open a second terminal window and start up GDB with the following command:
gdb --oah
Using GDB on an Intel-based Macintosh computer is just like using GDB on a PowerPC Macintosh.
4. Attach your application.
attach
5. Press Tab.
GDB automatically appends the process ID (pid) to your application name.
6. Press Return.
7. Type c to execute your application.
Important: Do not type run. Typing run will not execute your code. It will leave your application
in a state that requires you to start over from the first step.
Figure A-3 shows the commands for initiating a debugging session for a PowerPC binary. After you start the
session, you can debug in much the same way as you would debug a native process except that you can’t call
functions—either explicitly or implicitly—from within GDB. For example, you can’t inspect CF objects by calling
CFShow.
Rosetta
Troubleshooting
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
74Keep in mind that symbol files aren’t loaded at the start of the debugging session. They are loaded after your
application is up and running. This means that any breakpoints you set are “pending breakpoints” until the
executable and libraries are loaded.
Figure A-3 Terminal windows with the commands for debugging a PowerPC binary on an Intel-based Macintosh
computer
Note: Debugging Rosetta applications from within either CodeWarrior or Xcode is not supported.
Rosetta
Troubleshooting
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
75The intention of this appendix isto show how to factor a mathematical calculation into architecture-independent
and architecture-specific parts. Using matrix multiplication as an example, you’ll see how to write a function
that works for both the PowerPC and the x86 architectures with a minimum of architecture-specific coding.
You can then apply this approach to other, more complex mathematical calculations.
The following basic operations are available on both architectures:
● Vector loads and stores
● Multiplication
● Addition
● An instruction to splat a float across a vector
For other types of calculations, you may need to write separate versions of code. Because of the differences
in the number of registers and the pipeline depths between the two architectures, it is often advantageous to
provide separate versions.
Note: There is a function for 4x4 matrix multiplication in the Accelerate framework (vecLib) that
is tuned for both architectures. You can also call sgemm from Basic Linear Algebra Subprograms
(BLAS) (also available in the Accelerate framework) to operate on larger matrices.
Architecture-Specific Code
Listing B-1 (page 77)showsthe architecture-specific code you need to support matrix multiplication. The code
calls the architecture-independent function MyMatrixMultiply, which is shown in Listing B-2 (page 82).
The code shown in Listing B-1 works properly for both instruction set architectures only if you build the code
as a universal binary. For more information, see “Building a Universal Binary” (page 11).
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
76
Architecture-Independent Vector-Based CodeNote: The sample code makes use of a GCC extension to return a result from a code block ({}). The
code may not compile correctly on other compilers. The extension is necessary because you cannot
pass immediate values to an inline function, meaning that you must use a macro.
Listing B-1 Architecture-specific code needed to support matrix multiplication
#include
#include
#include
// For each vector architecture...
#if defined( __VEC__ )
// AltiVec
// Set up a vector type for a float[4] array for each vector type
typedef vector float vFloat;
// Define some macros to map a virtual SIMD language to
// each actual SIMD language. For matrix multiplication, the tasks
// you need to perform are essentially the same between the two
// instruction set architectures (ISA).
#define vSplat( v, i ) ({ vFloat z = vec_splat( v, i );
/* return */ z; })
#define vMADD vec_madd
#define vLoad( ptr ) vec_ld( 0, ptr )
#define vStore( v, ptr ) vec_st( v, 0, ptr )
#define vZero() (vector float) vec_splat_u32(0)
#elif defined( __SSE__ )
// SSE
// The header file xmmintrin.h defines C functions for using
// SSE and SSE2 according to the Intel C programming interface
#include
// Set up a vector type for a float[4] array for each vector type
typedef __m128 vFloat;
Architecture-Independent Vector-Based Code
Architecture-Specific Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
77// Also define some macros to map a virtual SIMD language to
// each actual SIMD language.
// Note that because i MUST be an immediate, it is incorrect here
// to alias i to a stack based copy and replicate that 4 times.
#define vSplat( v, i )({ __m128 a = v; a = _mm_shuffle_ps( a, a, \
_MM_SHUFFLE(i,i,i,i) ); /* return */ a; })
inline __m128 vMADD( __m128 a, __m128 b, __m128 c )
{
return _mm_add_ps( c, _mm_mul_ps( a, b ) );
}
#define vLoad( ptr ) _mm_load_ps( (float*) (ptr) )
#define vStore( v, ptr ) _mm_store_ps( (float*) (ptr), v )
#define vZero() _mm_setzero_ps()
#else
// Scalar
#warning To compile vector code, you must specify -faltivec,
-msse, or both- faltivec and -msse
#warning Compiling for scalar code.
// Some scalar equivalents to show what the above vector
// versions accomplish
// A vector, declared as a struct with 4 scalars
typedef struct
{
float a;
float b;
float c;
float d;
}vFloat;
// Splat element i across the whole vector and return it
Architecture-Independent Vector-Based Code
Architecture-Specific Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
78#define vSplat( v, i ) ({ vFloat z; z.a = z.b = z.c = z.d = ((float*)
&v)[i]; /* return */ z; })
// Perform a fused-multiply-add operation on architectures that support it
// result = X * Y + Z
inline vFloat vMADD( vFloat X, vFloat Y, vFloat Z )
{
vFloat result;
result.a = X.a * Y.a + Z.a;
result.b = X.b * Y.b + Z.b;
result.c = X.c * Y.c + Z.c;
result.d = X.d * Y.d + Z.d;
return result;
}
// Return a vector that starts at the given address
#define vLoad( ptr ) ( (vFloat*) ptr )[0]
// Write a vector to the given address
#define vStore( v, ptr ) ( (vFloat*) ptr )[0] = v
// Return a vector full of zeros
#define vZero() ({ vFloat z; z.a = z.b = z.c = z.
d = 0.0f; /* return */ z; })
#endif
// Prototype for a vector matrix multiply function
void MyMatrixMultiply( vFloat A[4], vFloat B[4], vFloat C[4] );
int main( void )
{
// The vFloat type (defined previously) is a vector or scalar array
Architecture-Independent Vector-Based Code
Architecture-Specific Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
79// that contains 4 floats
// Thus each one of these is a 4x4 matrix, stored in the C storage order.
vFloat A[4];
vFloat B[4];
vFloat C1[4];
vFloat C2[4];
int i, j, k;
// Pointers to the elements in A, B, C1 and C2
float *a = (float*) &A;
float *b = (float*) &B;
float *c1 = (float*) &C1;
float *c2 = (float*) &C2;
// Initialize the data
for( i = 0; i < 16; i++ )
{
a[i] = (double) (rand() - RAND_MAX/2) / (double) (RAND_MAX );
b[i] = (double) (rand() - RAND_MAX/2) / (double) (RAND_MAX );
c1[i] = c2[i] = 0.0;
}
// Perform the brute-force version of matrix multiplication
// and use this later to check for correctness
printf( "Doing simple matrix multiply...\n" );
for( i = 0; i < 4; i++ )
for( j = 0; j < 4; j++ )
{
float result = 0.0f;
for( k = 0; k < 4; k++ )
result += a[ i * 4 + k] * b[ k * 4 + j ];
c1[ i * 4 + j ] = result;
Architecture-Independent Vector-Based Code
Architecture-Specific Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
80}
// The vector version
printf( "Doing vector matrix multiply...\n" );
MyMatrixMultiply( A, B, C2 );
// Make sure that the results are correct
// Allow for some rounding error here
printf( "Verifying results..." );
for( i = 0 ; i < 16; i++ )
if( fabs( c1[i] - c2[i] ) > 1e-6 )
printf( "failed at %i,%i: %8.17g %8.17g\n", i/4,
i&3, c1[i], c2[i] );
printf( "done.\n" );
return 0;
}
The 4x4 matrix multiplication algorithm shown in Listing B-2 (page 82) is a simple matrix multiplication
algorithm performed with four columns in parallel. The basic calculation is as follows:
C[i][j] = sum( A[i][k] * B[k][j], k = 0... width of A )
It can be rewritten in mathematical vector notation for rows of C as the following:
C[i][] = sum( A[i][k] * B[k][], k = 0... width of A )
Where:
C[i][] is the ith row of C
A[i][k] is the element of A at row i and column k
B[k][] is the k th row of B
An example calculation for C[0][] is as follows:
C[0][] = A[0][0] * B[0][] + A[0][1] * B[1][] + A[0][2] * B[2][] + A[0][3] * B[3][]
Architecture-Independent Vector-Based Code
Architecture-Specific Code
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
81This calculation is simply a multiplication of a scalar times a vector, followed by addition of similar elements
between two vectors, repeated four times, to get a vector that contains four sums of products. Performing the
calculation in this way saves you from transposing B to obtain the B columns, and also saves you from adding
across vectors, which is inefficient. All operations occur between similar elements of two different vectors.
Architecture-Independent Matrix Multiplication
Listing B-2 (page 82) shows architecture-independent vector code that performs matrix multiplication. This
code compiles as scalar if you do not set up the appropriate compiler flags for PowerPC (-faltivec) or x86
(-msse), or if AltiVec is unavailable on the PowerPC. The matrices used in the MyMatrixMultply function
assume the C storage order for 2D arrays, not the FORTRAN storage order.
Listing B-2 Architecture-independent code that performs matrix multiplication
void MyMatrixMultiply( vFloat A[4], vFloat B[4], vFloat C[4] )
{
vFloat A1 = vLoad( A ); //Row 1 of A
vFloat A2 = vLoad( A + 1 ); //Row 2 of A
vFloat A3 = vLoad( A + 2 ); //Row 3 of A
vFloat A4 = vLoad( A + 3); //Row 4 of A
vFloat C1 = vZero(); //Row 1 of C, initialized to zero
vFloat C2 = vZero(); //Row 2 of C, initialized to zero
vFloat C3 = vZero(); //Row 3 of C, initialized to zero
vFloat C4 = vZero(); //Row 4 of C, initialized to zero
vFloat B1 = vLoad( B ); //Row 1 of B
vFloat B2 = vLoad( B + 1 ); //Row 2 of B
vFloat B3 = vLoad( B + 2 ); //Row 3 of B
vFloat B4 = vLoad( B + 3); //Row 4 of B
//Multiply the first row of B by the first column of A (do not sum across)
C1 = vMADD( vSplat( A1, 0 ), B1, C1 );
C2 = vMADD( vSplat( A2, 0 ), B1, C2 );
C3 = vMADD( vSplat( A3, 0 ), B1, C3 );
C4 = vMADD( vSplat( A4, 0 ), B1, C4 );
Architecture-Independent Vector-Based Code
Architecture-Independent Matrix Multiplication
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
82// Multiply the second row of B by the second column of A and
// add to the previous result (do not sum across)
C1 = vMADD( vSplat( A1, 1 ), B2, C1 );
C2 = vMADD( vSplat( A2, 1 ), B2, C2 );
C3 = vMADD( vSplat( A3, 1 ), B2, C3 );
C4 = vMADD( vSplat( A4, 1 ), B2, C4 );
// Multiply the third row of B by the third column of A and
// add to the previous result (do not sum across)
C1 = vMADD( vSplat( A1, 2 ), B3, C1 );
C2 = vMADD( vSplat( A2, 2 ), B3, C2 );
C3 = vMADD( vSplat( A3, 2 ), B3, C3 );
C4 = vMADD( vSplat( A4, 2 ), B3, C4 );
// Multiply the fourth row of B by the fourth column of A and
// add to the previous result (do not sum across)
C1 = vMADD( vSplat( A1, 3 ), B4, C1 );
C2 = vMADD( vSplat( A2, 3 ), B4, C2 );
C3 = vMADD( vSplat( A3, 3 ), B4, C3 );
C4 = vMADD( vSplat( A4, 3 ), B4, C4 );
// Write out the result to the destination
vStore( C1, C );
vStore( C2, C + 1 );
vStore( C3, C + 2 );
vStore( C4, C + 3 );
}
Architecture-Independent Vector-Based Code
Architecture-Independent Matrix Multiplication
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
83Mac OS X ABI Function Call Guide describes the function-calling conventions used in all the architectures
supported by Mac OS X. For detailed information about the IA-32 ABI, read the section “IA-32 Function Calling
Conventions,” which:
● Lists data types, sizes, and natural alignment
● Describes stack structure
● Discusses prologs and epilogs
● Provides details on how arguments are passed and results are returned
● Tells which registers preserve their value after a procedure call and which ones are volatile
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
84
32-Bit Application Binary InterfaceFor information on the Apple x86-64 ABI, see:
● Mac OS X ABI Function Call Guide
● Mac OS X ABI Mach-O File Format Reference
● Mach-O Programming Topics
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
85
64-Bit Application Binary InterfaceThis table describes the changes to Universal Binary Programming Guidelines, Second Edition .
Date Notes
2009-02-04 Made minor content additions.
Updated “Programmatically Detecting a Translated Application ” (page
71) with details about the behavior of the sysctl call when working
with the proc_native variable.
2007-02-26 Updated for Mac OS X v10.5.
Removed the Appendix “Using PowerPlant” because an Open Source
version that supports Intel-based Macintosh computers is available. See
“Metrowerks PowerPlant” (page 53).
Replaced the content in “64-Bit Application Binary Interface” (page 85)
with cross-referencesto documentsthat are more thorough at describing
the ABI.
2007-01-08 Added information on 64-bit and made technical corrections.
Added “64-Bit Application Binary Interface” (page 85).
Added a note to “OpenGL” (page 55).
Revised the explanation of the return values for the code in Listing
A-4 (page 72).
Removed the code example in “Archived Bit Fields” (page 46) because it
was incorrect.
2006-07-24 Made a few minor technical corrections.
Revised “Network-Related Data” (page 34).
Clarified how Listing A-4 (page 72) works.
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
86
Document Revision HistoryDate Notes
2006-06-28 Fixed link.
Added “PostScript Printing” (page 59).
Redirected link from Kernel Extensions Reference to Kernel Framework
Reference .
2006-05-23 Removed outdated links and made a few other minor changes.
Revised code regarding flippers to use an explicit UInt16 pointer and to
assign back to dataptr the advanced countPtr.
Updated instructions in “Troubleshooting” (page 73).
Added information about the CCSResourcesFileMapped flag to “using
PowerPlant”.
Removed links to documentation that is no longer relevant.
Added a note to “LStream.h” concerning reading and writing bool values.
2006-04-04 Corrected two function names.
Revised information in “32-Bit Application Binary Interface” (page 84) so
that it now only provides a link to the primary ABI reference.
2006-03-08 Improved wording and added information on Spotlight importers.
Added information to “Objective-C Runtime: Sending Messages” and
“Objective-C: Messages to nil.”
2006-02-07 Improved the wording in several sections.
Revised wording in “Bit Shifting” (page 47), “Bit Test, Set, and Clear
Functions: Carbon and POSIX” (page 47), “Troubleshooting” (page 73),
and “Guidelines for Swapping Bytes” (page 28).
Revised code in Listing A-4 (page 72) by adding a statement to handle
versions of Mac OS that pre-date Rosetta.
2006-01-10 Updated content for Mac OS X v10.4.4.
Removed the note about preliminary documentation from “Introduction
to Universal Binary Programming Guidelines” (page 8).
Document Revision History
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
87Date Notes
Changed Xcode 2.1 to Xcode 2.2 in various places throughout the
document because this is the recommended version for building a
universal binary.
Updated screenshots.
Updated information in “Disk Partitions” (page 48), “Finder Information
and Low-Level File System Operations” (page 49), “Multithreading” (page
53), “Objective-C: Messages to nil” (page 53), “QuickTime
Components” (page 60), “Runtime Code Generation” (page 61), and
“Values in an Array” (page 38).
Added the sections “Code on the Stack: Disabling Execution” (page 22),
“Extensible Firmware Interface (EFI)” (page 24), and “Mach Processes: The
Task for PID Function” (page 52).
In “Rosetta” (page 65), updated the sections “What Can Be
Translated?” (page 65) and “Forcing an Application to Run
Translated” (page 68).
In “Rosetta” (page 65), added the section “Programmatically Detecting a
Translated Application ” (page 71).
2005-12-06 Made refinements to existing content.
Added code that shows how to swap bytes for values in an array. See
“Values in an Array” (page 38).
Added “Automator Scripts” (page 46), “Dashboard Widgets” (page 48),
and “QuickTime Metadata Functions” (page 61).
Updated for Xcode 2.2; includes pointers to newly revised tools
documentation as well as improved guidelines and tips.
2005-11-09
Revised “Building Your Code” (page 12).
Added “Debugging” (page 16).
Added information to “Pixel Data ” (page 58) on how to track down color
problems.
Added the section “Quartz Bitmap Data” (page 59).
Document Revision History
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
88Date Notes
Added information about IP addresses and other “false” numerical values.
In several places throughout the book, added cross references to newly
revised, relevant documentation.
Added clarification on the long double data type. See “Data Types” (page
23).
Added information about using the PinRect function. See “QuickDraw
Routines” (page 60).
Added information about the need for Xcode targets to be native. See
“Build Assumptions” (page 11) and “Building Your Code” (page 12).
Corrected information about how ATS for Fonts handles font resources.
See “Font-Related Resources” (page 50).
Changed extended markup language to extensible markup language.
Improved the grammar in “Objective-C: Messages to nil” (page 53).
Fixed a link to information on Hyper-Threading Technology. See the “See
Also” (page 62) section in “Guidelines for Specific Scenarios” (page 46).
Made numerous editorial changes throughout.
2005-10-04 Made technical improvements and minor editorial changes throughout.
Added a few resources to See Also in “Building a Universal Binary” (page
11).
Changed the title of the Appendix Fast Matrix Multiplication to
“Architecture-Independent Vector-Based Code” (page 76).
Added new sections to the chapter “Guidelines for Specific
Scenarios” (page 46). See “FireWire Device Access” (page 49) and “USB
Device Access” (page 62).
Added information about a relevant technical note to “QuickTime
Components” (page 60).
Added an example of a color issue to “Troubleshooting Your Built
Application” (page 16).
Revised the section “Objective-C: Messages to nil” (page 53).
Document Revision History
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
89Date Notes
Revised the code for swapping floating-point values. See “Floating-Point
Values” (page 32).
Add a reference to Cross-Development Programming Guide in the chapter
“Building a Universal Binary” (page 11).
Made corrections to the section “OpenGL” (page 55).
2005-09-08 Updated a substantial amount of task and conceptual information.
Completely replaced information related to PowerPlant.
Removed most of the content from “Preparing Vector-Based Code” (page
63) because the document AltiVec/SSE Migration Guide provides a more
complete discussion of porting AltiVec code to SSE.
Removed most of the content from the appendix titled Application Binary
Interface becausethedocumentMacOSXABIFunctionCallGuide provides
a more complete description of the IA-32 ABI for Intel-based Macintosh
computers.
Added a section—“Java Applications” (page 51)—that provides
information about Java on Intel-based Macintosh computers, including
what happens under Rosetta. Added cross-references to a technical note
on this topic to “Rosetta” (page 65).
2005-08-11 Numerous minor technical and editorial changes throughout.
Removed the appendix titled x86 Equivalent Instructions for AltiVec
Instructions.”
Made numerousminortechnical refinements and fixed a few typographical
errors.
2005-07-07
Fixed typographical and linking errors. Made several improvements to
technical content.
2005-06-17
New document that describes the architectural differences between
PowerPC and Intel and providestipsfor writing code that can run on both.
2005-06-07
Document Revision History
Retired Document | 2009-02-04 | © 2005, 2009 Apple Inc. All Rights Reserved.
90Apple Inc.
© 2005, 2009 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, AppleScript, Carbon,
Cocoa, ColorSync, eMac, Finder, FireWire, Logic,
Mac, MacOS, Macintosh,Objective-C,OS X, Pages,
Panther, Quartz, QuickDraw, QuickTime, Rosetta,
Spotlight, and Xcode are trademarks of Apple
Inc., registered in the U.S. and other countries.
Intel and Intel Core are registered trademarks of
Intel Corporation or its subsidiaries in the United
States and other countries.
Java is a registered trademark of Oracle and/or
its affiliates.
MMX is a trademark of Intel Corporation or its
subsidiaries in the United States and other
countries.
OpenGL is a registered trademark of Silicon
Graphics, Inc.
PowerPC and the PowerPC logo are trademarks
of International Business Machines Corporation,
used under license therefrom.
UNIX is a registered trademark of The Open
Group.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
Object-Oriented
Programming with
Objective-CContents
Introduction 5
Who Should Read This Document 5
Organization of This Document 6
See Also 6
Why Objective-C? 7
Object-Oriented Programming 8
Data and Operations 8
Interface and Implementation 9
The Object Model 12
The Messaging Metaphor 13
Classes 15
Modularity 16
Reusability 16
Mechanisms of Abstraction 18
Encapsulation 18
Polymorphism 19
Inheritance 20
Class Hierarchies 21
Subclass Definitions 21
Uses of Inheritance 22
Dynamism 23
Dynamic Typing 24
Dynamic Binding 25
Dynamic Loading 27
Structuring Programs 29
Outlet Connections 29
Extrinsic and Intrinsic Connections 30
Activating the Object Network 31
Aggregation and Decomposition 31
Models and Frameworks 32
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
2Structuring the Programming Task 34
Collaboration 34
Organizing Object-Oriented Projects 35
Designing on a Large Scale 35
Separating the Interface from the Implementation 35
Dividing the Work into Modules 35
Keeping the Interface Simple 36
Making Decisions Dynamically 36
Inheriting Generic Code 36
Reusing Tested Code 36
Document Revision History 38
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
3
ContentsFigures
Object-Oriented Programming 8
Figure 2-1 Interface and implementation 9
The Object Model 12
Figure 3-1 An object 12
Figure 3-2 Objects in a network 13
Figure 3-3 An inheritance hierarchy 21
Structuring Programs 29
Figure 4-1 Outlets 30
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
4An object-oriented approach to application development makes programs more intuitive to design, faster to
develop, more amenable to modification, and easier to understand. Most object-oriented development
environments consist of at least three parts:
● A library of objects
● A set of development tools
● An object-oriented programming language and support library
The Objective-C language is a programming language designed to enable sophisticated object-oriented
programming. Objective-C is defined as a small but powerfulset of extensionsto the standard ANSI C language.
Its additions to C are mostly based on Smalltalk, one of the first object-oriented programming languages.
Objective-C is designed to give C full object-oriented programming capabilities and to do so in a simple and
straightforward way.
Important: This document does not describe the Objective-C language itself. To learn about the language,
see The Objective-C Programming Language .
Every object-oriented programming language and environment has a different perspective on what
object-oriented means, how objects behave, and how programs might be structured. This document offers
the Objective-C perspective.
Who Should Read This Document
For those who have never used object-oriented programming to create applications, this document is designed
to help you become familiar with object-oriented development. It spells out some of the implications of
object-oriented design and gives you a flavor of what writing an object-oriented program is really like.
If you have developed applications using an object-oriented environment, this document will help you
understand the fundamental concepts that are essential to understanding how to use Objective-C effectively
and how to structure a program that uses Objective-C.
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
5
IntroductionBecause this isn’t a document about C, it assumes some prior acquaintance with that language. However, it
doesn’t have to be an extensive acquaintance. Object-oriented programming in Objective-C is sufficiently
different from procedural programming in ANSI C that you won’t be hampered if you’re not an experienced
C programmer.
Organization of This Document
This document is divided into several chapters:
●
“Why Objective-C?” (page 7) explains why Objective-C was chosen as the development language for the
Cocoa frameworks.
●
“Object-Oriented Programming” (page 8) discusses the rationale for object-oriented programming
languages and introduces much of the terminology. It develops the ideas behind object-oriented
programming techniques. Even if you’re already familiar with object-oriented programming, you are
encouraged to read this chapter to gain a sense of the Objective-C perspective on object orientation and
its use of terminology.
●
“The Object Model” (page 12) describes how you can think of a program in terms of units that combine
state and behavior—objects. It then explains how you characterize these objects as belonging to a particular
class, how one class can inheritstate and behavior from another class, and how objects can send messages
to other objects.
●
“Structuring Programs” (page 29) explains how you think about designing an object-oriented program
by creating connections between objects. It introducesthe techniques of aggregation and decomposition,
which divide responsibility between differentsorts of object, and the role of frameworksin defining libraries
of objects designed to work together.
●
“Structuring the Programming Task” (page 34) discusses issues of project management related to
collaboration among programmers and to code implementation.
See Also
The Objective-C Programming Language describes the Objective-C programming language.
Objective-C Runtime Programming Guide describes how you can interact with the Objective-C runtime.
Objective-C Runtime Reference describes the data structures and functions of the Objective-C runtime support
library. Your programs can use these interfaces to interact with the Objective-C runtime system. For example,
you can add classes or methods, or obtain a list of all class definitions for loaded classes.
Introduction
Organization of This Document
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
6The Objective-C language was chosen for a variety of reasons. First and foremost, it’s an object-oriented
language. The kind of functionality that’s packaged in the Cocoa frameworks can only be delivered through
object-oriented techniques. Second, because Objective-C is an extension ofstandard ANSI C, existing C programs
can be adapted to use the software frameworks without losing any of the work that went into their original
development. Because Objective-C incorporates C, you get all the benefits of C when working within Objective-C.
You can choose when to do something in an object-oriented way (define a new class, for example) and when
to stick to procedural programming techniques (define a structure and some functions instead of a class).
Moreover, Objective-C is a fundamentally simple language. Its syntax is small, unambiguous, and easy to learn.
Object-oriented programming, with its self-conscious terminology and emphasis on abstract design, often
presents a steep learning curve to new recruits. A well-organized language like Objective-C can make becoming
a proficient object-oriented programmer that much less difficult.
Compared to other object-oriented languages based on C, Objective-C is very dynamic. The compiler preserves
a great deal of information about the objects themselves for use at runtime. Decisions that otherwise might
be made at compile time can be postponed until the program isrunning. Dynamism gives Objective-C programs
unusual flexibility and power. For example, it yields two big benefits that are hard to get with other nominally
object-oriented languages:
● Objective-C supports an open style of dynamic binding, a style that can accommodate a simple architecture
for interactive user interfaces. Messages are not necessarily constrained by either the class of the receiver
or even the method name, so a software framework can allow for user choices at runtime and permit
developers freedom of expression in their design. (Terminology such as dynamic binding , message , class,
and receiver are explained in due course in this document.)
● Dynamism enables the construction of sophisticated development tools. An interface to the runtime
system provides access to information about running applications, so it’s possible to develop tools that
monitor, intervene, and reveal the underlying structure and activity of Objective-C applications.
Historical note: As a language, Objective-C has a long history. It was created at the Stepstone
company in the early 1980s by Brad Cox and Tom Love. It was licensed by NeXT Computer Inc. in
the late 1980s to develop the NeXTStep frameworks that preceded Cocoa. NeXT extended the
language in several ways, for example, with the addition of protocols.
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
7
Why Objective-C?As humans, we’re constantly faced with myriad facts and impressions that we must make sense of. To do so,
we must abstract underlying structure away from surface details and discover the fundamental relations at
work. Abstractions reveal causes and effects, expose patterns and frameworks, and separate what’s important
from what’s not. Object orientation provides an abstraction of the data on which you operate; moreover, it
provides a concrete grouping between the data and the operations you can perform with the data—in effect
giving the data behavior.
Data and Operations
Programming languages have traditionally divided the world into two parts—data and operations on data.
Data is static and immutable, except as the operations may change it. The procedures and functions that
operate on data have no lasting state of their own; they’re useful only in their ability to affect data.
This division is, of course, grounded in the way computers work, so it’s not one that you can easily ignore or
push aside. Like the equally pervasive distinctions between matter and energy and between nouns and verbs,
it forms the background against which we work. At some point, all programmers—even object-oriented
programmers—must lay out the data structures that their programs will use and define the functions that will
act on the data.
With a procedural programming language like C, that’s about all there is to it. The language may offer various
kinds of support for organizing data and functions, but it won’t divide the world any differently. Functions and
data structures are the basic elements of design.
Object-oriented programming doesn’tso much dispute this view of the world asrestructure it at a higher level.
It groups operations and data into modular units called objects and lets you combine objects into structured
networks to form a complete program. In an object-oriented programming language, objects and object
interactions are the basic elements of design.
Every object has both state (data) and behavior (operations on data). In that, they’re not much different from
ordinary physical objects. It’s easy to see how a mechanical device,such as a pocket watch or a piano, embodies
both state and behavior. But almost anything that’s designed to do a job does, too. Even simple things with
no moving parts such as an ordinary bottle combine state (how full the bottle is, whether or not it’s open, how
warm its contents are) with behavior (the ability to dispense its contents at various flow rates, to be opened
or closed, to withstand high or low temperatures).
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
8
Object-Oriented ProgrammingIt’sthisresemblance to real thingsthat gives objects much of their power and appeal. They can not only model
components of real systems, but equally as well fulfill assigned roles as components in software systems.
Interface and Implementation
To invent programs, you need to be able to capture abstractions and express them in the program design. It’s
the job of a programming language to help you do this. The language should facilitate the process of invention
and design by letting you encode abstractions that reveal the way things work. It should let you make your
ideas concrete in the code you write. Surface details shouldn’t obscure the architecture of your program.
All programming languages provide devices that help express abstractions. In essence, these devices are ways
of grouping implementation details, hiding them, and giving them, at least to some extent, a common
interface—much as a mechanical objectseparatesitsinterface from itsimplementation, asillustrated in Figure
2-1.
Figure 2-1 Interface and implementation
9
10
11
8
7
6
interface implementation
Looking at such a unit from the inside, as the implementer, you’d be concerned with what it’s composed of
and how it works. Looking at it from the outside, as the user, you’re concerned only with what it is and what
it does. You can look past the details and think solely in terms of the role that the unit plays at a higher level.
The principal units of abstraction in the C language are structures and functions. Both, in different ways, hide
elements of the implementation:
● On the data side of the world, C structures group data elements into larger units that can then be handled
as single entities. While some code must delve inside the structure and manipulate the fields separately,
much of the program can regard it as a single thing—not as a collection of elements, but as what those
elements taken together represent. One structure can include others, so a complex arrangement of
information can be built from simpler layers.
Object-Oriented Programming
Interface and Implementation
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
9In modern C, the fields of a structure live in their own namespace—that is, their names won’t conflict with
identically named data elements outside the structure. Partitioning the program namespace is essential
for keeping implementation details out of the interface. Imagine, for example, the enormous task of
assigning a different name to every piece of data in a large program and of making sure new names don’t
conflict with old ones.
● On the proceduralside of the world, functions encapsulate behaviorsthat can be used repeatedly without
being reimplemented. Data elements local to a function, like the fields within a structure, are protected
within their own namespace. Because functions can reference (call) other functions, complex behaviors
can be built from smaller pieces.
Functions are reusable. Once defined, they can be called any number of times without again considering
the implementation. The most generally useful functions can be collected in libraries and reused in many
different applications. All the user needs is the function interface, not the source code.
However, unlike data elements, functions aren’t partitioned into separate namespaces. Each function must
have a unique name. Although the function may be reusable, its name is not.
C structures and functions are able to expresssignificant abstractions, but they maintain the distinction between
data and operations on data. In a procedural programming language, the highest units of abstraction still live
on one side or the other of the data-versus-operations divide. The programs you design must always reflect,
at the highest level, the way the computer works.
Object-oriented programming languages don’t lose any of the virtues of structures and functions—they go a
step further and add a unit capable of abstraction at a higher level, a unit that hides the interaction between
a function and its data.
Suppose, for example, that you have a group of functions that act on a particular data structure. You want to
make those functions easier to use by, asfar as possible, taking the structure out of the interface. So you supply
a few additional functions to manage the data. All the work of manipulating the data structure—allocating
memory for it, initializing it, getting information from it, modifying values within it, keeping it up to date, and
freeing its memory—is done through the functions. All the user doesis call the functions and passthe structure
to them.
With these changes, the structure has become an opaque token that other programmers never need to look
inside. They can concentrate on what the functions do, not on how the data is organized. You’ve taken the
first step toward creating an object.
The nextstep isto give thisidea support in the programming language and completely hide the data structure
so that it doesn’t even have to be passed between the functions. The data becomes an internal implementation
detail; all that’s exported to users is a functional interface. Because objects completely encapsulate their data
(hide it), users can think of them solely in terms of their behavior.
Object-Oriented Programming
Interface and Implementation
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
10With thisstep, the interface to the functions has become much simpler. Callers don’t need to know how they’re
implemented (what data they use). It’s fair now to call this an object.
The hidden data structure unites all the functions that share access to it. So an object is more than a collection
of random functions; it’s a bundle of related behaviors that are supported by shared data. To use a function
that belongs to an object, you first create the object (thus giving it its internal data structure), and then tell
the object which function it should perform. You begin to think in terms of what the object does, rather than
in terms of the individual functions.
This progression from thinking about functions and data structures to thinking about object behaviors is the
essence of learning object-oriented programming. It may seem unfamiliar at first, but as you gain experience
with object-oriented programming, you find it’s a more natural way to think about things. Everyday programming
terminology isreplete with analogiesto real-world objects of various kinds—lists, containers, tables, controllers,
even managers. Implementing such things as programming objects merely extends the analogy in a natural
way.
A programming language can be judged by the kinds of abstractions that it enables you to encode. You
shouldn’t be distracted by extraneous matters or forced to express yourself using a vocabulary that doesn’t
match the reality you’re trying to capture.
If, for example, you must alwaystend to the business of keeping the right data matched with the right procedure,
you’re forced at all times to be aware of the entire program at a low level of implementation. While you might
still invent programs at a high level of abstraction, the path from imagination to implementation can become
quite tenuous—and more and more difficult as programs become bigger and more complicated.
By providing another, higher level of abstraction, object-oriented programming languages give you a larger
vocabulary and a richer model to program in.
Object-Oriented Programming
Interface and Implementation
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
11The insight of object-oriented programming isto combine state and behavior—data and operations on data—in
a high-level unit, an object, and to give it language support. An object is a group of related functions and a
data structure that serves those functions. The functions are known as the object’s methods, and the fields of
its data structure are its instance variables. The methods wrap around the instance variables and hide them
from the rest of the program, as Figure 3-1 illustrates.
Figure 3-1 An object
method
method
e m
doht
method
data
If you’ve ever tackled any kind of difficult programming problem, it’slikely that your design hasincluded groups
of functions that work on a particular kind of data—implicit “objects” without the language support.
Object-oriented programming makes these function groups explicit and permits you to think in terms of the
group, rather than its components. The only way to an object’s data, the only interface, is through its methods.
By combining both state and behavior in a single unit, an object becomes more than either alone; the whole
really is greater than the sum of its parts. An object is a kind of self-sufficient “subprogram” with jurisdiction
over a specific functional area. It can play a full-fledged modular role within a larger program design.
Terminology: Object-oriented terminology variesfrom language to language. For example, in C++,
methods are called member functions and instance variables are known as data members. This
document uses the terminology of Objective-C, which has its basis in Smalltalk.
For example, if you were to write a program that modeled home water usage, you might invent objects to
represent the various components of the water-delivery system. One might be a Faucet object that would
have methods to start and stop the flow of water, set the rate of flow, return the amount of water consumed
in a given period, and so on. To do this work, a Faucet object would need instance variables to keep track of
whether the tap is open or shut, how much water is being used, and where the water is coming from.
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
12
The Object ModelClearly, a programmatic Faucet object can be smarter than a real one (it’s analogous to a mechanical faucet
with lots of gauges and instruments attached). But even a real faucet, like any system component, exhibits
both state and behavior. To effectively model a system, you need programming units, like objects, that also
combine state and behavior.
A program consists of a network of interconnected objects that call upon each other to solve a part of the
puzzle (as illustrated in Figure 3-2 (page 13)). Each object has a specific role to play in the overall design of
the program and is able to communicate with other objects. Objects communicate through messages, which
are requests to perform methods.
Figure 3-2 Objects in a network
data
data
data
message
The objects in the network won’t all be the same. For example, in addition to Faucet objects, the program
that models water usage might also have Pipe objectsthat can deliver water to the Faucet and Valve objects
to regulate the flow among pipes. There could be a Building object to coordinate a set of pipes, valves, and
faucets, some Appliance objects—corresponding to dishwashers, toilets, and washing machines—that can
turn valves on and off, and maybe some User objects to work the appliances and faucets. When a Building
object is asked how much water is being used, it might call upon each Faucet and Valve object to report its
current state. When a user starts up an appliance, the appliance will need to turn on a valve to get the water
it requires.
The Messaging Metaphor
Every programming paradigm comes with its own terminology and metaphors. The jargon associated with
object-oriented programming invites you to think about what goes on in a program from a particular perspective.
The Object Model
The Messaging Metaphor
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
13There’s a tendency, for example, to think of objects as actors and to endow them with human-like intentions
and abilities. It’s tempting sometimes to talk about an object deciding what to do about a situation, asking
other objectsfor information, introspecting about itself to get requested information, delegating responsibility
to another object, or managing a process.
Rather than think in terms of functions or methods doing the work, as you would in a procedural programming
language, this metaphor asks you to think of objects as performing their methods. Objects are not passive
containers for state and behavior, but are said to be the agents of the program’s activity.
This metaphor is actually very useful. An object is like an actor in a couple of respects: It has a particular role
to play within the overall design of the program, and within that role it can act fairly independently of the
other parts of the program. It interacts with other objects as they play their own roles, but it is self-contained
and to a certain extent can act on its own. Like an actor onstage, it can’t stray from the script, but the role it
plays can be multifaceted and complex.
The idea of objects as actorsfits nicely with the principal metaphor of object-oriented programming—the idea
that objects communicate through messages. Instead of calling a method as you would a function, you send
a message to an object requesting it to perform one of its methods.
Although it can take some getting used to, this metaphor leads to a useful way of looking at methods and
objects. It abstracts methods away from the particular data they act on and concentrates on behavior instead.
For example, in an object-oriented programming interface, a start method might initiate an operation, an
archive method might archive information, and a draw method might produce an image. Exactly which
operation is initiated, which information is archived, and which image is drawn isn’t revealed by the method
name. Different objects might perform these methods in different ways.
Thus, methods are a vocabulary of abstract behaviors. To invoke one of those behaviors, you have to make it
concrete by associating the method with an object. This is done by naming the object as the receiver of a
message. The object you choose as receiver determines the exact operation that is initiated, the data that is
archived, or the image that is drawn.
Because methods belong to objects, they can be invoked only through a particular receiver (the owner of the
method and of the data structure the method will act on). Different receivers can have different implementations
of the same method. Consequently, different receivers can do different thingsin response to the same message.
The result of a message can’t be calculated from the message or method name alone; it also depends on the
object that receives the message.
By separating the message (the requested behavior) from the receiver (the owner of a method that can respond
to the request), the messaging metaphor perfectly captures the idea that behaviors can be abstracted away
from their particular implementations.
The Object Model
The Messaging Metaphor
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
14Classes
A program can have more than one object of the same kind. The program that models water usage, for example,
might have several faucets and pipes and perhaps a handful of appliances and users. Objects of the same kind
are said to be members of the same class. All members of a class are able to perform the same methods and
have matching sets of instance variables. They also share a common definition; each kind of object is defined
just once.
In this, objects are similar to C structures. Declaring a structure defines a type. For example, the declaration
struct key {
char *word;
int count;
};
defines the struct key type. Once defined, the structure name can be used to produce any number of
instances of the type:
struct key a, b, c, d;
struct key *p = malloc(sizeof(struct key) * MAXITEMS);
The declaration is a template for a kind of structure, but it doesn’t create a structure that the program can use.
It takes another step to allocate memory for an actual structure of that type, a step that can be repeated any
number of times.
Similarly, defining an object creates a template for a kind of object. It defines a class of objects. The template
can be used to produce any number of similar objects—instances of the class. For example, there would be
a single definition of the Faucet class. Using this definition, a program could allocate as many Faucet instances
as it needed.
A class definition is like a structure definition in that it lays out an arrangement of data elements (instance
variables) that become part of every instance. Each instance has memory allocated for its own set of instance
variables, which store values particular to the instance.
However, a class definition differs from a structure declaration in that it also defines methods that specify the
behavior of class members. Every instance is characterized by its access to the methods defined for the class.
Two objects with equivalent data structures but different methods would not belong to the same class.
The Object Model
Classes
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
15Modularity
To a C programmer, a module is nothing more than a file containing source code. Breaking a large (or even
not-so-large) program into different files is a convenient way of splitting it into manageable pieces. Each piece
can be worked on independently and compiled alone, and then integrated with other pieces when the program
is linked.
Using the static storage class designator to limit the scope of names to just the files where they’re declared
enhances the independence of source modules. This kind of module is a unit defined by the file system. It’s a
container for source code, not a logical unit of the language. What goes into the container is up to each
programmer. You can use them to group logically related parts of the code, but you don’t have to. Files are
like the drawers of a dresser; you can put your socks in one drawer, underwear in another, and so on, or you
can use another organizing scheme or simply choose to mix everything up.
Access to methods: It’s convenient to think of methods as being part of an object, just as instance
variables are. As in Figure 3-1 (page 12), methods can be diagrammed as surrounding the object’s
instance variables. But methods aren’t grouped with instance variables in memory. Memory is
allocated for the instance variables of each new object, but there’s no need to allocate memory for
methods. All an instance needs is access to its methods, and all instances of the same class share
access to the same set of methods. There’s only one copy of the methods in memory, no matter how
many instances of the class are created.
Object-oriented programming languages support the use of file containers for source code, but they also add
a logical module to the language—class definitions. As you’d expect, it’s often the case that each classis defined
in its own source file—logical modules are matched to container modules.
In Objective-C, for example, it would be possible to define the part of the Valve class that interacts with Pipe
objects in the same file that defines the Pipe class, thus creating a container module for Pipe-related code
and splitting the Valve class into more than one file. The Valve class definition would still act as a modular
unit within the construction of the program—it would still be a logical module—no matter how many files
the source code was located in.
The mechanisms that make class definitions logical units of the language are discussed in some detail under
“Mechanisms of Abstraction” (page 18).
Reusability
A principal goal of object-oriented programming is to make the code you write as reusable as possible—to
have it serve many different situations and applications—so that you can avoid reimplementing, even if only
slightly differently, something that’s already been done.
The Object Model
Classes
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
16Reusability is influenced by factors such as these:
● How reliable and bug-free the code is
● How clear the documentation is
● How simple and straightforward the programming interface is
● How efficiently the code performs its tasks
● How full the feature set is
These factors don’t apply just to the object model. They can be used to judge the reusability of any
code—standard C functions as well as class definitions. Efficient and well-documented functions, for example,
would be more reusable than undocumented and unreliable ones.
Nevertheless, a general comparison would show that class definitions lend themselves to reusable code in
waysthat functions do not. There are variousthings you can do to make functions more reusable—for example,
passing data as parameters rather than assuming specifically named global variables. Even so, it turns out that
only a small subset of functions can be generalized beyond the applications they were originally designed for.
Their reusability is inherently limited in at least three ways:
● Function names are global; each function must have a unique name (except for those declared static).
This naming requirement makesit difficult to rely heavily on library code when building a complex system.
The programming interface would be hard to learn and so extensive that it couldn’t easily capture significant
generalizations.
Classes, on the other hand, can share programming interfaces. When the same naming conventions are
used over and over, a great deal of functionality can be packaged with a relatively small and
easy-to-understand interface.
● Functions are selected from a library one at a time. It’s up to programmersto pick and choose the individual
functions they need.
In contrast, objects come as packages of functionality, not as individual methods and instance variables.
They provide integrated services, so users of an object-oriented library won’t get bogged down piecing
together their own solutions to a problem.
● Functions are typically tied to particular kinds of data structures devised for a specific program. The
interaction between data and function is an unavoidable part of the interface. A function is useful only to
those who agree to use the same kind of data structures it accepts as parameters.
Because it hides its data, an object doesn’t have this problem. This is one of the principal reasons that
classes can be reused more easily than functions.
The Object Model
Classes
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
17An object’s data is protected and won’t be touched by any other part of the program. Methods can therefore
trust its integrity. They can be sure that external access hasn’t put data into an illogical or untenable state. As
a result, an object data structure is more reliable than one passed to a function, and methods can depend on
it more. Reusable methods are consequently easier to write.
Moreover, because an object’s data is hidden, a class can be reimplemented to use a different data structure
without affecting its interface. All programs that use the class can pick up the new version without changing
any source code; no reprogramming is required.
Mechanisms of Abstraction
To this point, objects have been introduced as units that embody higher-level abstractions and as coherent
role-players within an application. However, they couldn’t be used this way without the support of various
language mechanisms. Two of the most important mechanisms are encapsulation and polymorphism.
Encapsulation keeps the implementation of an object out of its interface, and polymorphism results from
giving each class its own namespace. The following sections discuss each of these mechanisms in turn.
Encapsulation
To design effectively at any level of abstraction, you need to be able to leave details of implementation behind
and think in terms of units that group those details under a common interface. For a programming unit to be
truly effective, the barrier between interface and implementation must be absolute. The interface must
encapsulate the implementation—that is, hide it from other parts of the program. Encapsulation protects an
implementation from unintended actions and from inadvertent access.
In C, a function is clearly encapsulated; its implementation is inaccessible to other parts of the program and
protected from whatever actions might be taken outside the body of the function. In Objective-C, method
implementations are similarly encapsulated, but more importantly so are an object’sinstance variables. They’re
hidden inside the object and invisible outside it. The encapsulation of instance variables is sometimes also
called information hiding .
It might seem, at first, that hiding the information in instance variables might constrain your freedom as a
programmer. Actually, it gives you more room to act and frees you from constraints that might otherwise be
imposed. If any part of an object’s implementation could leak out and become accessible or a concern to other
parts of the program, it would tie the hands both of the object’s implementer and of those who would use
the object. Neither could make modifications without first checking with the other.
The Object Model
Mechanisms of Abstraction
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
18Suppose, for example, that you’re interested in the Faucet object being developed for the program that
models water use and you want to incorporate it in another program you’re writing. Once the interface to the
object is decided, you don’t have to be concerned when others work on it, fix bugs, and find better ways to
implement it. You get the benefit of these improvements, but none of them affects what you do in your
program. Because you depend solely on the interface, nothing they do can break your code. Your program is
insulated from the object’s implementation.
Moreover, although those implementing the Faucet object might be interested in how you use the class and
might try to make sure it meets your needs, they don’t have to be concerned with the way you write your
code. Nothing you do can touch the implementation of the object or limit their freedom to make implementation
changesin future releases. The implementation isinsulated from anything that you or other users of the object
might do.
Polymorphism
The ability of different objects to respond, each in its own way, to identical messages is called polymorphism.
Polymorphism results from the fact that every class lives in its own namespace. The names assigned within a
class definition don’t conflict with names assigned anywhere outside it. Thisistrue both of the instance variables
in an object’s data structure and of the object’s methods:
●
Just as the fields of a C structure are in a protected namespace, so are an object’s instance variables.
● Method names are also protected. Unlike the names of C functions, method names aren’t global symbols.
The name of a method in one class can’t conflict with method names in other classes; two very different
classes can implement identically named methods.
Method names are part of an object’sinterface. When a message issent requesting that an object do something,
the message names the method the object should perform. Because different objects can have methods with
the same name, the meaning of a message must be understood relative to the particular object that receives
the message. The same message sent to two different objects can invoke two distinct methods.
The main benefit of polymorphism is that it simplifies the programming interface. It permits conventions to
be established that can be reused in class after class. Instead of inventing a new name for each new function
you add to a program, the same names can be reused. The programming interface can be described as a set
of abstract behaviors, quite apart from the classes that implement them.
The Object Model
Mechanisms of Abstraction
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
19Overloading: The terms polymorphism and parameter overloading refer basically to the same
thing, but from slightly different points of view. Polymorphism takes a pluralistic point of view and
notesthatseveral classes can each have a method with the same name. Parameter overloading takes
the point of the view of the method name and notes that it can have different effects depending
on the parameters passed to it.Operator overloading issimilar. Itrefersto the ability to turn operators
of the language (such as == and + in C) into methods that can be assigned particular meanings for
particular kinds of objects. Objective-C implements polymorphism of method names, but not
parameter or operator overloading.
For example, suppose you want to report the amount of water used by an Appliance object over a given
period of time. Instead of defining an amountConsumed method for the Appliance class, an
amountDispensedAtFaucet method for a Faucet class, and a cumulativeUsage method for a Building
class, you can simply define a waterUsed method for each class. This consolidation reduces the number of
methods used for what is conceptually the same operation.
Polymorphism also permits code to be isolated in the methods of different objects rather than be gathered in
a single function that enumerates all the possible cases. This makes the code you write more extensible and
reusable. When a new case comes along, you don’t have to reimplement existing code; you need only to add
a new class with a new method, leaving the code that’s already written alone.
For example, suppose you have code that sends a draw message to an object. Depending on the receiver, the
message might produce one of two possible images. When you want to add a third case, you don’t have to
change the message or alter existing code; you merely allow another object to be assigned as the message
receiver.
Inheritance
The easiest way to explain something new is to start with something understood. If you want to describe what
a schooner is, it helpsif your listeners already know what a sailboat is. If you want to explain how a harpsichord
works, it’s best if you can assume your audience has already looked inside a piano, or has seen a guitar played,
or at least is familiar with the idea of a musical instrument.
The same is true if you want to define a new kind of object; the description is simpler if it can start from the
definition of an existing object.
With thisin mind, object-oriented programming languages permit you to base a new class definition on a class
already defined. The base class is called a superclass; the new class is its subclass. The subclass definition
specifies only how it differs from the superclass; everything else is taken to be the same.
The Object Model
Inheritance
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
20Nothing is copied from superclass to subclass. Instead, the two classes are connected so that the subclass
inherits all the methods and instance variables of itssuperclass, much as you want your listener’s understanding
ofschooner to inherit what they already know aboutsailboats. If the subclass definition were empty (if it didn’t
define any instance variables or methods of its own), the two classes would be identical (except for their names)
and would share the same definition. It would be like explaining what a fiddle is by saying that it’s exactly the
same as a violin. However, the reason for declaring a subclassisn’t to generate synonyms; it to create something
at least a little different from its superclass. For example, you might extend the behavior of the fiddle to allow
it to play bluegrass in addition to classical music.
Class Hierarchies
Any class can be used as a superclass for a new class definition. A class can simultaneously be a subclass of
another class, and a superclass for its own subclasses. Any number of classes can thus be linked in a hierarchy
of inheritance, such as the one depicted in Figure 3-3.
Figure 3-3 An inheritance hierarchy
root
C
D E F
A
B
Every inheritance hierarchy begins with a root class that has no superclass. From the root class, the hierarchy
branches downward. Each class inherits from its superclass and, through its superclass, from all the classes
above it in the hierarchy. Every class inherits from the root class.
Each class is the accumulation of all the class definitions in its inheritance chain. In the example above, class
D inherits both from C—itssuperclass—and the root class. Members of the D class have methods and instance
variables defined in all three classes—D, C, and root.
Typically, every class has just one superclass and can have an unlimited number of subclasses. However, in
some object-oriented programming languages (though not in Objective-C), a class can have more than one
superclass; it can inherit through multiple sources. Instead of having a single hierarchy that branches downward
as shown in Figure 3-3, multiple inheritance lets some branches of the hierarchy (or of different hierarchies)
merge.
Subclass Definitions
A subclass can make three kinds of changes to the definition it inherits through its superclass:
The Object Model
Inheritance
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
21●
It can expand the class definition it inherits by adding new methods and instance variables. This is the
most common reason for defining a subclass. Subclasses always add new methods and add new instance
variables if the methods require it.
●
It can modify the behavior it inherits by replacing an existing method with a new version. This is done by
simply implementing a new method with the same name as one that’sinherited. The new version overrides
the inherited version. (The inherited method doesn’t disappear; it’s still valid for the class that defined it
and other classes that inherit it.)
●
It can refine or extend the behavior it inherits by replacing an existing method with a new version, but it
still retains the old version by incorporating it in the new method. A subclass sends a message to perform
the old version in the body of the new method. Each class in an inheritance chain can contribute part of
a method’s behavior. In Figure 3-3 (page 21), for example, class D might override a method defined in
class C and incorporate C’s version, while C’s version incorporates a version defined in the root class.
Subclasses thus tend to fill out a superclass definition, making it more specific and specialized. They add, and
sometimesreplace, code rather than subtract it. Note that methods generally can’t be disinherited and instance
variables can’t be removed or overridden.
Uses of Inheritance
The classic examples of an inheritance hierarchy are borrowed from animal and plant taxonomies. For example,
there could be a class corresponding to the Pinaceae (pine) family of trees. Itssubclasses could be Fir, Spruce,
Pine, Hemlock, Tamarack, DouglasFir, and TrueCedar, corresponding to the various genera that make
up the family. The Pine class might have SoftPine and HardPine subclasses, with WhitePine, SugarPine,
and BristleconePine as subclasses of SoftPine, and PonderosaPine, JackPine, MontereyPine, and
RedPine as subclasses of HardPine.
There’s rarely a reason to program a taxonomy like this, but the analogy is a good one. Subclasses tend to
specialize a superclass or adapt it to a special purpose, much as a species specializes a genus.
Here are some typical uses of inheritance:
● Reusing code. If two or more classes have some things in common but also differ in some ways, the
common elements can be put in a single class definition that the other classes inherit. The common code
is shared and need only be implemented once.
For example, Faucet, Valve, and Pipe objects, defined for the program that models water use, all need
a connection to a water source and should be able to record the rate of flow. These commonalities can
be encoded once, in a class that the Faucet, Valve, and Pipe classes inherit from. A Faucet object can
be said to be a kind of Valve object, so perhaps the Faucet class would inherit most of what it is from
Valve, and add little of its own.
The Object Model
Inheritance
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
22● Setting up a protocol. A class can declare a number of methods that its subclasses are expected to
implement. The class might have empty versions of the methods, or it might implement partial versions
that are to be incorporated into the subclass methods. In either case, its declarations establish a protocol
that all its subclasses must follow.
When different classes implement similarly named methods, a program is better able to make use of
polymorphism in its design. Setting up a protocol that subclasses must implement helps enforce these
conventions.
● Delivering generic functionality. One implementer can define a class that contains a lot of basic, general
code to solve a problem but that doesn’t fill in all the details. Other implementers can then create subclasses
to adapt the generic class to their specific needs. For example, the Appliance class in the program that
models water use might define a generic water-using device thatsubclasses would turn into specific kinds
of appliances.
Inheritance is thus both a way to make someone else’s programming task easier and a way to separate
levels of implementation.
● Making slight modifications. When inheritance is used to deliver generic functionality, set up a protocol,
or reuse code, a class is devised that other classes are expected to inherit from. But you can also use
inheritance to modify classes that aren’t intended as superclasses. Suppose, for example, that there’s an
object that would work well in your program, but you’d like to change one or two things that it does. You
can make the changes in a subclass.
● Previewing possibilities. Subclasses can also be used to factor out alternatives for testing purposes. For
example, if a class is to be encoded with a particular user interface, alternative interfaces can be factored
into subclasses during the design phase of the project. Each alternative can then be demonstrated to
potential usersto see which they prefer. When the choice is made, the selected subclass can be reintegrated
into its superclass.
Dynamism
At one time in programming history, the question of how much memory a program might use was typically
settled when the source code was compiled and linked. All the memory the program would ever need was set
aside for it as it was launched. This memory was fixed; it could neither grow nor shrink.
In hindsight, it’s evident what a serious constraint this was. It limited not only how programs were constructed,
but what you could imagine a program doing. It constrained design, not just programming technique. The
introduction of functions that dynamically allocate memory as a program runs (such as malloc) opened
possibilities that didn’t exist before.
The Object Model
Dynamism
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
23Compile-time and link-time constraints are limiting because they force issues to be decided from information
found in the programmer’s source code, rather than from information obtained from the user as the program
runs.
Although dynamic allocation removes one such constraint, many others, equally as limiting as static memory
allocation, remain. For example, the elements that make up an application must be matched to data types at
compile time. And the boundaries of an application are typically set at link time. Every part of the application
must be united in a single executable file. New modules and new types can’t be introduced as the program
runs.
Objective-C seeks to overcome these limitations and to make programs as dynamic and fluid as possible. It
shifts much of the burden of decision making from compile time and link time to runtime. The goal is to let
program users decide what will happen, rather than constrain their actions artificially by the demands of the
language and the needs of the compiler and linker.
Three kinds of dynamism are especially important for object-oriented design:
● Dynamic typing, waiting until runtime to determine the class of an object
● Dynamic binding, determining at runtime what method to invoke
● Dynamic loading, adding new components to a program as it runs
Dynamic Typing
The compiler typically complains if the code you write assigns a value to a type that can’t accommodate it.
You might see warnings like these:
incompatible types in assignment
assignment of integer from pointer lacks a cast
Type checking is useful, but there are times when it can interfere with the benefits you get from polymorphism,
especially if the type of every object must be known to the compiler.
Suppose, for example, that you want to send an object a message to perform the start method. Like other
data elements, the object isrepresented by a variable. If the variable’stype (its class) must be known at compile
time, it would be impossible to let runtime factors influence the decision about what kind of object should be
assigned to the variable. If the class of the variable is fixed in source code, so is the version of start that the
message invokes.
The Object Model
Dynamism
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
24If, on the other hand, it’s possible to wait until runtime to discover the class of the variable, any kind of object
could be assigned to it. Depending on the class of the receiver, the start message might invoke different
versions of the method and produce very different results.
Dynamic typing thus givessubstance to dynamic binding (discussed next). But it does more than that. It permits
associations between objects to be determined at runtime, rather than forcing them to be encoded in a static
design. For example, a message could pass an object as a parameter without declaring exactly what kind of
object it is—that is, without declaring its class. The message receiver might then send its own messages to
the object, again without ever caring about what kind of object it is. Because the receiver uses the passed
object to do some of its work, it is in a sense customized by an object of indeterminate type (indeterminate
in source code, that is, not at runtime).
Dynamic Binding
In standard C, you can declare a set of alternative functions, such as the standard string-comparison functions,
int strcmp(const char *, const char *); /* case sensitive */
int strcasecmp(const char *, const char *); /*case insensitive*/
and declare a pointer to a function that has the same return and parameter types:
int (* compare)(const char *, const char *);
You can then wait until runtime to determine which function to assign to the pointer,
if ( **argv == 'i' )
compare = strcasecmp;
else
compare = strcmp;
and call the function through the pointer:
if ( compare(s1, s2) )
...
Thisis akin to what in object-oriented programming is called dynamic binding, delaying the decision of exactly
which method to perform until the program is running.
The Object Model
Dynamism
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
25Although not all object-oriented languages support it, dynamic binding can be routinely and transparently
accomplished through messaging. You don’t have to go through the indirection of declaring a pointer and
assigning values to it as shown in the example above. You also don’t have to assign each alternative procedure
a different name.
Messages invoke methods indirectly. Every message expression must find a method implementation to “call.”
To find that method, the messaging machinery must check the class of the receiver and locate itsimplementation
of the method named in the message. When this is done at runtime, the method is dynamically bound to the
message. When it’s done by the compiler, the method is statically bound.
Late binding: Some object-oriented programming languages (notably C++) require a message
receiver to be statically typed in source code, but don’t require the type to be exact. An object can
be typed to its own class or to any classthat it inheritsfrom. The compiler therefore can’t tell whether
the message receiver is an instance of the class specified in the type declaration, an instance of a
subclass, or an instance ofsome more distantly derived class. Because it doesn’t know the exact class
of the receiver, it can’t know which version of the method named in the message to invoke. In this
circumstance, the choice is between treating the receiver as if it were an instance of the specified
class and simply bind the method defined for that class to the message, or waiting until some later
time to resolve the situation. In C++, the decision is postponed to link time for methods (member
functions) that are declared virtual. Thisissometimesreferred to aslate binding rather than dynamic
binding . While dynamic in the sense that it happens at runtime, it carries with it strict compile-time
type constraints. As discussed here (and implemented in Objective-C), dynamic binding is
unconstrained.
Dynamic binding is possible even in the absence of dynamic typing, but it’s not very interesting. There’s little
benefit in waiting until runtime to match a method to a message when the class of the receiver is fixed and
known to the compiler. The compiler could just as well find the method itself; the runtime result won’t be any
different.
However, if the class of the receiver is dynamically typed, there’s no way for the compiler to determine which
method to invoke. The method can be found only after the class of the receiver isresolved at runtime. Dynamic
typing thus entails dynamic binding.
Dynamic typing also makes dynamic binding interesting, for it opensthe possibility that a message might have
very different results depending on the class of the receiver. Runtime factors can influence the choice of receiver
and the outcome of the message.
The Object Model
Dynamism
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
26Dynamic typing and binding also open the possibility that the code you write can send messages to objects
not yet invented. If object types don’t have to be decided until runtime, you can give others the freedom to
design their own classes and name their own data types and still have your code send messagesto their objects.
All you need to agree on are the messages, not the data types.
Note: Dynamic binding is routine in Objective-C. You don’t need to arrange for it specially, so your
design never needs to bother with what’s being done when.
Dynamic Loading
Historically, in many common environments, before a program can run, all its parts had to be linked together
in one file. When it was launched, the entire program was loaded into memory at once.
Some object-oriented programming environments overcome this constraint and allow different parts of an
executable program to be kept in different files. The program can be launched in bits and pieces as they’re
needed. Each piece is dynamically loaded and linked with the rest of program as it’s launched. User actions
can determine which parts of the program are in memory and which aren’t.
Only the core of a large program needs to be loaded at the start. Other modules can be added as the user
requests their services. Modules the user doesn’t request make no memory demands on the system.
Dynamic loading raisesinteresting possibilities. For example, an entire program does not have to be developed
at once. You can deliver your software in pieces and update one part of it at a time. You can devise a program
that groups several tools under a single interface, and load just the tools the user wants. The program can
even offer sets of alternative tools to do the same job—the user then selects one tool from the set and only
that tool would be loaded.
Perhaps the most important current benefit of dynamic loading is that it makes applications extensible. You
can allow others to add to and customize a program you’ve designed. All your program needs to do is provide
a framework that others can fill in, and, at runtime, find the pieces that they’ve implemented and load them
dynamically.
The main challenge that dynamic loading faces is getting a newly loaded part of a program to work with parts
already running, especially when the different parts were written by different people. However, much of this
problem disappears in an object-oriented environment because code is organized into logical modules with
a clear division between implementation and interface. When classes are dynamically loaded, nothing in the
newly loaded code can clash with the code already in place. Each class encapsulates its implementation and
has an independent namespace.
The Object Model
Dynamism
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
27In addition, dynamic typing and dynamic binding let classes designed by othersfit effortlessly into the program
you’ve designed. Once a class is dynamically loaded, it’s treated no differently than any other class. Your code
can send messages to their objects and theirs to yours. Neither of you has to know what classes the other has
implemented. You need only agree on a communications protocol.
Loading and linking: Although it’s the term commonly used, dynamic loading could just as well
be called dynamic linking . Programs are linked when their various parts are joined so that they can
work together; they’re loaded when they’re read into volatile memory at launch time. Linking usually
precedes loading. Dynamic loading refers to the process of separately loading new or additional
parts of a program and linking them dynamically to the parts already running.
The Object Model
Dynamism
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
28Object-oriented programs have two kinds of structure. One can be seen in the inheritance hierarchy of class
definitions. The other is evident in the pattern of message passing as the program runs. These messages reveal
a network of object connections.
● The inheritance hierarchy explains how objects are related by type. For example, in the program that
models water use, it might turn out that faucets and pipes are the same kind of object, except that faucets
can be turned on and off and pipes can have multiple connections to other pipes. This similarity would
be captured in the program design if the Faucet and Pipe classes inherit from a common superclass.
● The network of object connections explains how the program works. For example, Appliance objects
might send messages requesting water to valves, and valves to pipes. Pipes might communicate with the
Building object, and the Building object with all the valves, faucets, and pipes, but not directly with
appliances. To communicate with each other in this way, objects must know about each other. An
Appliance object would need a connection to a Valve object, and a Valve object to a Pipe object,
and so on. These connections define a program structure.
Object-oriented programs are designed by laying out the network of objects with their behaviors and patterns
of interaction and by arranging the hierarchy of classes. There’s structure both in the program’s activity and
in its definition.
Outlet Connections
Part of the task of designing an object-oriented program isto arrange the object network. The network doesn’t
have to be static; it can change dynamically as the program runs. Relationships between objects can be
improvised as needed, and the cast of objects that play assigned roles can change from time to time. But there
has to be a script.
Some connections can be entirely transitory. A message might contain a parameter identifying an object,
perhaps the sender of the message, that the receiver can communicate with. As it responds to the message,
the receiver can send messages to that object, perhaps identifying itself or still another object that the object
can in turn communicate with. Such connections are fleeting; they last only as long as the chain of messages.
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
29
Structuring ProgramsBut not all connections between objects can be handled on the fly. Some need to be recorded in program
data structures. There are various ways to do this. A table might be kept of object connections, or there might
be a service that identifies objects by name. However, the simplest way is for each object to have instance
variables that keep track of the other objects it must communicate with. These instance variables—termed
outlets because they record the outlets for messages—define the principal connections between objects in
the program network.
Although the names of outlet instance variables are arbitrary, they generally reflect the rolesthat outlet objects
play. Figure 4-1 illustrates an object with four outlets—an agent, a friend, a neighbor, and a boss. The objects
that play these parts may change every now and then, but the roles remain the same.
Figure 4-1 Outlets
agent
friend
neighbor
boss
Some outlets are set when the object isfirst initialized and may never change. Others might be set automatically
asthe consequence of other actions. Still others can be set freely, using methods provided just for that purpose.
However they’re set, outlet instance variables reveal the structure of the application. They link objects into a
communicating network, much as the components of a water system are linked by their physical connections
or as individuals are linked by their patterns of social relations.
Extrinsic and Intrinsic Connections
Outlet connections can capturemany different kinds of relationships between objects. Sometimesthe connection
is between objects that communicate more or less as equal partners in an application, each with its own role
to play and neither dominating the other. For example, an Appliance object might have an outlet instance
variable to keep track of the valve it’s connected to.
Structuring Programs
Outlet Connections
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
30Sometimes one object should be seen as being part of another. For example, a Faucet object might use a
Meter object to measure the amount of water being released. The Meter object would serve no other object
and would act only under orders from the Faucet object. It would be an intrinsic part of the Faucet object,
in contrast to an Appliance object’s extrinsic connection to a Valve object.
Similarly, an object that oversees other objects might keep a list of its charges. A Building object, for example,
might have a list of all the Pipe objects in the program. The Pipe objects would be considered an intrinsic
part of the Building object and belong to it. Pipe objects, on the other hand, would maintain extrinsic
connections to each other.
Intrinsic outlets behave differently from extrinsic ones. When an object is freed or archived in a file on disk,
the objects that its intrinsic outlets point to must be freed or archived with it. For example, when a faucet is
freed, its meter is rendered useless and therefore should be freed as well. A faucet archived without its meter
would be of little use when it’s unarchived (unless it could create a new Meter object for itself).
Extrinsic outlets, on the other hand, capture the organization of the program at a higher level. They record
connections between relatively independent program subcomponents. When an Appliance object is freed,
the Valve object it was connected to still is of use and remains in place. When an Appliance object is
unarchived, it can be connected to another valve and resume playing the same sort of role it played before.
Activating the Object Network
The object network is set into motion by an external stimulus. If you’re writing an interactive application with
a user interface, it will respond to user actions on the keyboard and mouse. A program that tries to factor very
large numbers might start when you pass it a target number on the command line. Other programs might
respond to data received over a phone line, information obtained from a database, or information about the
state of a mechanical process the program monitors.
Programs often are activated by a flow of events, which are reports of external activity ofsome sort. Applications
that display a user interface are driven by events from the keyboard and mouse. Every press of a key or click
of the mouse generates events that the application receives and responds to. An object-oriented program
structure (a network of objects that’s prepared to respond to an external stimulus) is ideally suited for this kind
of user-driven application.
Aggregation and Decomposition
Another part of the design task is deciding the arrangement of classes—when to add functionality to an existing
class by defining a subclass and when to define an independent class. The problem can be clarified by imagining
what would happen in the extreme case:
Structuring Programs
Aggregation and Decomposition
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
31●
It’s possible to conceive of a program consisting of just one object. Because it’s the only object, it can send
messages only to itself. It therefore can’t take advantage of polymorphism, or the modularity of a variety
of classes, or a program design conceived as a network of interconnected objects. The true structure of
the program would be hidden inside the class definition. Despite being written in an object-oriented
language, there would be very little that was object-oriented about it.
● On the other hand, it’s also possible to imagine a program that consists of hundreds of different kinds of
objects, each with very few methods and limited functionality. Here, too, the structure of the program
would be lost, this time in a maze of object connections.
Obviously, it’s best to avoid either of these extremes, to keep objects large enough to take on a substantial
role in the program but small enough to keep that role well-defined. The structure of the program should be
easy to grasp in the pattern of object connections.
Nevertheless, the question often arises of whether to add more functionality to a class or to factor out the
additional functionality and put it in a separate class definition. For example, a Faucet object needs to keep
track of how much water is being used over time. To do that, you could either implement the necessary methods
in the Faucet class, or you could devise a generic Meter object to do the job, as suggested earlier. Each
Faucet object would have an outlet connecting it to a Meter object, and the meter would not interact with
any object but the faucet.
The choice often depends on your design goals. If the Meter object could be used in more than one situation,
perhaps in another project entirely, it would increase the reusability of your code to factor the metering task
into a separate class. If you have reason to make Faucet objects as self-contained as possible, the metering
functionality could be added to the Faucet class.
It’s generally better to try for reusable code and avoid having large classes that do so many things that they
can’t be adapted to othersituations. When objects are designed as components, they become that much more
reusable. What works in one system or configuration might well work in another.
Dividing functionality between different classes doesn’t necessarily complicate the programming interface. If
the Faucet class keeps the Meter object private, the Meter interface wouldn’t have to be published for users
of the Faucet class; the object would be as hidden as any other Faucet instance variable.
Models and Frameworks
Objects combine state and behavior, and so resemble things in the real world. Because they resemble real
things, designing an object-oriented program is very much like thinking about real things—what they do, how
they work, and how one thing is connected to another.
Structuring Programs
Models and Frameworks
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
32When you design an object-oriented program, you are, in effect, putting together a computer simulation of
how something works. Object networks look and behave like models of real systems. An object-oriented
program can be thought of as a model, even if there’s no actual counterpart to it in the real world.
Each component of the model—each kind of object—is described in terms of its behavior, responsibilities,
and interactions with other components. Because an object’s interface lies in its methods, not its data, you can
begin the design process by thinking about what a system component must do, not how it’s represented in
data. Once the behavior of an object is decided, the appropriate data structure can be chosen, but this is a
matter of implementation, not the initial design.
For example, in the water-use program, you wouldn’t begin by deciding what the Faucet data structure looked
like, but what you wanted a Faucet object to do—make a connection to a pipe, be turned on and off, adjust
the rate of flow, and so on. The design is therefore not bound from the outset by data choices. You can decide
on the behavior first and implement the data afterwards. Your choice of data structures can change over time
without affecting the design.
Designing an object-oriented program doesn’t necessarily entail writing great amounts of code. The reusability
of class definitions means that the opportunity is great for building a program largely out of classes devised
by others. It might even be possible to construct interesting programs entirely out of classes someone else
defined. As the suite of class definitions grows, you have more and more reusable parts to choose from.
Reusable classes come from many sources. Development projects often yield reusable class definitions, and
some enterprising developers market them. Object-oriented programming environments typically come with
class libraries. There are well over two hundred classes in the Cocoa libraries. Some of these classes offer basic
services (hashing, data storage, remote messaging). Others are more specific (user interface devices, video
displays, sound).
Typically, a group of library classes work together to define a partial program structure. These classes constitute
a software framework (or kit) that can be used to build a variety of different kinds of applications. When you
use a framework, you accept the program model it provides and adapt your design to it. You use the framework
by:
●
Initializing and arranging instances of framework classes
● Defining subclasses of framework classes
● Defining new classes of your own to work with classes defined in the framework
In each of these ways, you not only adapt your program to the framework, but you also adapt the generic
framework structure to the specialized purposes of your application.
The framework, in essence, sets up part of an object network for your program and provides part of its class
hierarchy. Your own code completes the program model started by the framework.
Structuring Programs
Models and Frameworks
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
33Object-oriented programming not only structures programs in a better way, it also helps structure the
programming task.
As software tries to do more and more, and programs become bigger and more complicated, the problem of
managing the task also grows. There are more pieces to fit together and more people working together to
build them. The object-oriented approach offers ways of dealing with this complexity, not just in design, but
also in the organization of the work.
Collaboration
Complex software requires an extraordinary collaborative effort among people who must be individually
creative, yet still make what they do fit exactly with what others are doing.
The sheer size of the effort and the number of people working on the same project at the same time in the
same place can get in the way of the group’s ability to work cooperatively towards a common goal. In addition,
collaboration is often impeded by barriers of time, space, and organization:
● Code must be maintained, improved, and used long after it’s written. Programmers who collaborate on a
project may not be working on it at the same time, so they may not be in a position to talk things over
and keep each other informed about details of the implementation.
● Even if programmers work on the same project at the same time, they may not be located in the same
place. This also inhibits how closely they can work together.
● Programmers working in different groups with different priorities and different schedules often must
collaborate on projects. Communication across organizational barriers isn’t always easy to achieve.
The answer to these difficulties must grow out of the way programs are designed and written. It can’t be
imposed from the outside in the form of hierarchical management structures and strict levels of authority.
These often get in the way of people’s creativity, and become burdensin and of themselves. Rather, collaboration
must be built into the work itself.
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
34
Structuring the Programming TaskThat’s where object-oriented programming techniques can help. For example, the reusability of object-oriented
code means that programmers can collaborate effectively, even when they work on different projects at
different times or are in different organizations, just by sharing their code in libraries. This kind of collaboration
holds a great deal of promise, for it can conceivably lighten difficult tasks and make seemingly impossible
projects achievable.
Organizing Object-Oriented Projects
Object-oriented programming helps restructure the programming task in ways that benefit collaboration. It
helps eliminate the need to collaborate on low-level implementation details, while providing structures that
facilitate collaboration at a higher level. Almost every feature of the object model, from the possibility of
large-scale design to the increased reusability of code, has consequences for the way people work together.
Designing on a Large Scale
When programs are designed at a high level of abstraction, the division of labor is more easily conceived. It
can match the division of the program on logical lines; the way a project is organized can grow out of its design.
With an object-oriented design, it’s easier to keep common goals in sight, instead of losing them in the
implementation, and easier for everyone to see how the piece they’re working on fits into the whole. Their
collaborative efforts are therefore more likely to be on target.
Separating the Interface from the Implementation
The connections between the various components of an object-oriented program are worked out early in the
design process. They can be well-defined, at least for the initial phase of development, before implementation
begins.
During implementation, only this interface needs to be coordinated, and most of that falls naturally out of the
design. Because each class encapsulates its implementation and has its own namespace, there’s no need to
coordinate implementation details. Collaboration is simpler when there are fewer coordination requirements.
Dividing the Work into Modules
The modularity of object-oriented programming means that the logical components of a large program can
each be implemented separately. Different people can work on different classes. Each implementation task is
isolated from the others.
Structuring the Programming Task
Organizing Object-Oriented Projects
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
35Modularity has benefits, not just for organizing the implementation, but for fixing problems later. Because
implementations are contained within class boundaries, problems that come up are also likely to be isolated.
It’s easier to track down bugs when they’re located in a well-defined part of the program.
Separating responsibilities by class also means that each part can be worked on by specialists. Classes can be
updated periodically to optimize their performance and make the best use of new technologies. These updates
don’t have to be coordinated with other parts of the program. As long as the interface to an object doesn’t
change, improvements to its implementation can be scheduled at any time.
Keeping the Interface Simple
The polymorphism of object-oriented programs yields simpler programming interfaces, since the same names
and conventions can be reused in any number of classes. The result is less to learn, a greater shared
understanding of how the whole system works, and a simpler path to cooperation and collaboration.
Making Decisions Dynamically
Because object-oriented programs make decisions dynamically at runtime, lessinformation needsto be supplied
at compile time (in source code) to make two pieces of code work together. Consequently, there’s less to
coordinate and less to go wrong.
Inheriting Generic Code
Inheritance is a way of reusing code. If you can define your classes as specializations of more generic classes,
your programming task is simplified. The design is simplified as well, since the inheritance hierarchy lays out
the relationships between the different levels of implementation and makes them easier to understand.
Inheritance also increases the reusability and reliability of code. The code placed in a superclass is tested by
its subclasses. The generic class you find in a library will have been tested by other subclasses written by other
developers for other applications.
Reusing Tested Code
The more software you can borrow from others and incorporate in your own programs, the less you have to
do yourself. There’s more software to borrow in an object-oriented programming environment because the
code is more reusable. Collaboration between programmers working in different places for different
organizations is enhanced, while the burden of each project is eased.
Structuring the Programming Task
Organizing Object-Oriented Projects
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
36Classes and frameworks from an object-oriented library can make substantial contributions to your program.
When you program with the software frameworks provided by Apple, for example, you’re effectively collaborating
with the programmers at Apple; you’re contracting a part of your program, often a substantial part, to them.
You can concentrate on what you do best and leave other tasks to the library developer. Your projects can be
prototyped faster, completed faster, with less of a collaborative challenge at your own site.
The increased reusability of object-oriented code also increases its reliability. A class taken from a library is
likely to have found its way into a variety of applications and situations. The more the code has been used, the
more likely it is that problems will have been encountered and fixed. Bugs that would have seemed strange
and hard to find in your program might already have been tracked down and eliminated.
Structuring the Programming Task
Organizing Object-Oriented Projects
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
37This table describes the changes to Object-Oriented Programming with Objective-C.
Date Notes
2010-11-15 Edited for content and clarity.
2008-11-19 Corrected typographical errors.
2007-12-11 Corrected a typographical error.
2007-07-09 Originally published as part of "The Objective-C Programming Language".
2010-11-15 | © 2010 Apple Inc. All Rights Reserved.
38
Document Revision HistoryApple Inc.
© 2010 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrievalsystem, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer for personal use only and to print
copies of documentation for personal use
provided that the documentation contains
Apple’s copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-labeled computers.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, Cocoa, Mac, and
Objective-C are trademarks of Apple Inc.,
registered in the U.S. and other countries.
NeXT is a trademark of NeXT Software, Inc.,
registered in the U.S. and other countries.
Even though Apple has reviewed this document,
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE.ASARESULT, THISDOCUMENT IS PROVIDED
“AS IS,” AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL,OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
THE WARRANTY AND REMEDIES SET FORTH ABOVE
ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL
OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer,
agent, or employee is authorized to make any
modification, extension, or addition to this warranty.
Some states do not allow the exclusion or limitation
of implied warranties or liability for incidental or
consequential damages, so the above limitation or
exclusion may not apply to you. This warranty gives
you specific legal rights, and you may also have other
rights which vary from state to state.
Table View
Programming Guide for
iOSContents
About Table Views in iOS Apps 8
At a Glance 9
Table Views Draw Their Rows Using Cells 9
Responding to Selections of Rows 9
In Editing Mode You Can Add, Delete, and Reorder Rows 10
To Create a Table View, Use a Storyboard 10
Prerequisites 11
See Also 11
Table View Styles and Accessory Views 12
Table View Styles 12
Plain Table Views 13
Grouped Table Views 15
Standard Styles for Table View Cells 17
Accessory Views 21
Overview of the Table View API 23
Table View 23
Table View Controller 23
Data Source and Delegate 23
Extension to the NSIndexPath Class 24
Table View Cells 24
Navigating a Data Hierarchy with Table Views 25
Hierarchical Data Models and Table Views 25
The Data Model as a Hierarchy of Model Objects 25
Table Views and the Data Model 26
View Controllers and Navigation-Based Apps 28
Navigation Controllers 28
Navigation Bars 29
Table View Controllers 31
Managing Table Views in a Navigation-Based App 32
Design Pattern for Navigation-Based Apps 35
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
2Creating and Configuring a Table View 36
Basics of Table View Creation 36
Recommendations for Creating and Configuring Table Views 38
Creating a Table View Using a Storyboard 38
Choose the Table View’s Display Style 39
Choose the Table View’s Content Type 40
Design the Table View’s Rows 42
Create Additional Table Views 42
Learn More by Creating a Sample App 43
Creating a Table View Programmatically 43
Adopt the Data Source and Delegate Protocols 43
Create and Configure a Table View 44
Populating a Dynamic Table View with Data 44
Populating a Static Table View With Data 46
Populating an Indexed List 47
Optional Table View Configurations 52
Add a Custom Title 52
Provide a Section Title 53
Indent a Row 53
Vary a Row’s Height 54
Customize Cells 54
A Closer Look at Table View Cells 55
Characteristics of Cell Objects 55
Using Cell Objects in Predefined Styles 57
Customizing Cells 60
Loading Table View Cells from a Storyboard 61
Programmatically Adding Subviews to a Cell’s Content View 69
Cells and Table View Performance 73
Managing Selections 74
Selections in Table Views 74
Responding to Selections 74
Programmatically Selecting and Scrolling 78
Inserting and Deleting Rows and Sections 80
Inserting and Deleting Rows in Editing Mode 81
When a Table View is Edited 81
An Example of Deleting a Table-View Row 84
An Example of Adding a Table-View Row 85
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
3
ContentsBatch Insertion, Deletion, and Reloading of Rows and Sections 87
An Example of Batched Insertion and Deletion Operations 88
Ordering of Operations and Index Paths 89
Managing the Reordering of Rows 91
What Happens When a Row is Relocated 92
Examples of Moving a Row 93
Document Revision History 95
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
4
ContentsFigures and Listings
About Table Views in iOS Apps 8
Figure I-1 Table views of various kinds 8
Table View Styles and Accessory Views 12
Figure 1-1 A table view in the plain style 13
Figure 1-2 A table view configured as an indexed list 14
Figure 1-3 A table view configured as a selection list 15
Figure 1-4 A table view in the grouped style 16
Figure 1-5 Header and footer of a section 17
Figure 1-6 Default table row style 18
Figure 1-7 Table row style with a subtitle under the title 19
Figure 1-8 Table row style with a right-aligned subtitle 20
Figure 1-9 Table row style in Contacts format 21
Navigating a Data Hierarchy with Table Views 25
Figure 3-1 Mapping levels of the data model to table views 27
Figure 3-2 Navigation controller and view controllers in a navigation-based app 29
Figure 3-3 Navigation bars and common control items 30
Figure 3-4 A storyboard with two table view controllers 33
Listing 3-1 Passing data to a destination view controller 34
Listing 3-2 Passing data to a source view controller 34
Creating and Configuring a Table View 36
Figure 4-1 Calling sequence for creating and configuring a table view 37
Figure 4-2 The master view controller in the Master-Detail Application storyboard 39
Figure 4-3 A dynamic table view 40
Figure 4-4 A static table view 41
Listing 4-1 Adopting the data source and delegate protocols 43
Listing 4-2 Creating a table view 44
Listing 4-3 Populating a dynamic table view with data 45
Listing 4-4 Populating a static table view with data 46
Listing 4-5 Defining the model-object interface 48
Listing 4-6 Loading the table-view data and initializing the model objects 48
Listing 4-7 Preparing the data for the indexed list 49
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
5Listing 4-8 Providing section-index data to the table view 50
Listing 4-9 Populating the rows of an indexed list 51
Listing 4-10 Adding a title to the table view 52
Listing 4-11 Returning a title for a section 53
Listing 4-12 Custom indentation of a row 53
Listing 4-13 Varying row height 54
A Closer Look at Table View Cells 55
Figure 5-1 Parts of a table view cell 55
Figure 5-2 Parts of a table-view cell in editing mode 56
Figure 5-3 Default cell content in a UITableViewCell object 57
Figure 5-4 A table view with rows showing both images and text 58
Figure 5-5 Table view cells in a storyboard 61
Figure 5-6 Table view rows drawn with a custom prototype cell 62
Figure 5-7 Table view rows drawn with multiple cells 66
Figure 5-8 Making connections to your static cell content 68
Figure 5-9 Cells with custom content as subviews 70
Listing 5-1 Configuring a UITableViewCell object with both image and text 58
Listing 5-2 Alternating the background color of cells 60
Listing 5-3 Adding data to a cell using tags 63
Listing 5-4 Adding data to a cell using outlets 65
Listing 5-5 Defining outlet properties for static cell objects 66
Listing 5-6 Setting the data in the user interface 68
Listing 5-7 Adding subviews to a cell’s content view 70
Managing Selections 74
Listing 6-1 Responding to a row selection 75
Listing 6-2 Setting a switch object as an accessory view and responding to its action message 75
Listing 6-3 Managing a selection list—exclusive list 77
Listing 6-4 Managing a selection list—inclusive list 77
Listing 6-5 Programmatically selecting a row 78
Inserting and Deleting Rows and Sections 80
Figure 7-1 Calling sequence for inserting or deleting rows in a table view 82
Figure 7-2 Deletion of section and row and insertion of row 90
Listing 7-1 View controller responding to setEditing:animated: 84
Listing 7-2 Customizing the editing style of rows 84
Listing 7-3 Updating the data-model array and deleting the row 85
Listing 7-4 Adding an Add button to the navigation bar 85
Listing 7-5 Responding to a tap on the Add button 86
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
6
Figures and ListingsListing 7-6 Adding the new item to the data-model array 86
Listing 7-7 Batch insertion and deletion methods 87
Listing 7-8 Inserting and deleting a block of rows in a table view 88
Managing the Reordering of Rows 91
Figure 8-1 Reordering a row 91
Figure 8-2 Calling sequence for reordering a row in a table view 92
Listing 8-1 Excluding a row from relocation 93
Listing 8-2 Updating the data-model array for the relocated row 94
Listing 8-3 Retargeting the destination row of a move operation 94
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
7
Figures and ListingsTable views are versatile user interface objects frequently found in iOS apps. A table view presents data in a
scrollable list of multiple rows that may be divided into sections.
Table views have many purposes:
● To let users navigate through hierarchically structured data
● To present an indexed list of items
● To display detail information and controls in visually distinct groupings
● To present a selectable list of options
Figure I-1 Table views of various kinds
A table view has only one column and allows vertical scrolling only. It consists of rows in sections. Each section
can have a header and a footer that displaystext or an image. However, many table views have only one section
with no visible header or footer. Programmatically, the UIKit framework identifies rows and sections through
their index number: Sections are numbered 0 through n – 1 from the top of a table view to the bottom; rows
are numbered 0 through n – 1 within a section. A table view can have its own header and footer, distinct from
any section; the table header appears before the first row of the first section, and the table footer appears after
the last row of the last section.
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
8
About Table Views in iOS AppsAt a Glance
A table view is an instance of the UITableView class in one of two basic styles, plain or grouped. A plain table
view is an unbroken list; a grouped table view has visually distinct sections. A table view has a data source and
might have a delegate. The data source object provides the data for populating the sections and rows of the
table view. The delegate object customizes its appearance and behavior.
Related chapters: “Table View Styles and Accessory Views” (page 12)
Table Views Draw Their Rows Using Cells
A table view draws its visible rows using cells—that is, UITableViewCell objects. Cells are views that can
display text, images, or other kinds of content. They can have background views for both normal and selected
states. Cells can also have accessory views, which function as controls for selecting or setting an option.
The UIKit framework defines four standard cell styles, each with its own layout of the three default content
elements: main label, detail label, and image. You may also create your own custom cellsto acquire a distinctive
style for your app’s table views.
When you configure the attributes of a table view in the storyboard editor, you choose between two types of
cell content: static cells or dynamic prototypes.
● Static cells. Use static cells to design a table with a fixed number of rows, each with its own layout. Use
static cells when you know what the table looks like at design time, regardless of the specific information
it displays.
● Dynamic prototypes. Use dynamic prototypes to design one cell and then use it as the template for other
cells in the table. Use a dynamic prototype when multiple cells in a table should use the same layout to
display information. Dynamic prototype content is managed by the data source at runtime, with an arbitrary
number of cells.
Related Chapters: “Table View Styles and Accessory Views” (page 12), “A Closer Look at Table View
Cells” (page 55)
Responding to Selections of Rows
When users select a row (by tapping it), the delegate of the table view is informed via a message. The delegate
is passed the indexes of the row and the section that the row is in. It uses this information to locate the
corresponding item in the app’s data model. This item might be at an intermediate level in the hierarchy of
About Table Views in iOS Apps
At a Glance
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
9data or it might be a “leaf node" in the hierarchy. If the item is at an intermediate level, the app displays a new
table view. If the item is a leaf node, the app displays details about the selected item in a grouped-style table
view or some other kind of view.
In table views that list a series of options, tapping a row simply selects its associated option. No subsequent
view of data is displayed.
Related Chapters: “Navigating a Data Hierarchy with Table Views” (page 25), “Managing
Selections” (page 74)
In Editing Mode You Can Add, Delete, and Reorder Rows
Table views can enter an editing mode in which users can insert or delete rows, or relocate them within the
table. In editing mode, rows that are marked for insertion or deletion display a green plus sign (insertion) or a
red minus sign (deletion) near the left edge of the row. If users touch a deletion control or, in some table views,
swipe across a row, a red Delete button appears, prompting usersto delete that row. Rowsthat can be relocated
display (near their right edge) an image consisting of several horizontal lines. When the table view leaves
editing mode, the insertion, deletion, and reordering controls disappear.
When users attempt to insert, delete, or reorder rows, the table view sends a sequence of messages to its data
source and delegate so that they can manage these operations.
Related Chapters: “Inserting and Deleting Rows and Sections” (page 80), “Managing the Reordering
of Rows” (page 91)
To Create a Table View, Use a Storyboard
The easiest and recommended way to create and manage a table view is to use a custom
UITableViewController object in a storyboard. If your app is based largely on table views, create your
Xcode project using the Master-Detail Application template. This template includes an initial custom
UITableViewController class and a storyboard for the scenes in the user interface, including the custom
view controller and its table view. In the storyboard editor, choose one of the two styles for this table view and
design its content.
At runtime, UITableViewController creates the table view and assigns itself as delegate and data source.
Immediately after it’s created, the table view asks its data source for the number of sections, the number of
rows in each section, and the table view cell to use to draw each row. The data source manages the application
data used for populating the sections and rows of the table view.
About Table Views in iOS Apps
At a Glance
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
10Related Chapters: “Navigating a Data Hierarchy with Table Views” (page 25), “Creating and
Configuring a Table View” (page 36)
Prerequisites
Before reading this document, you should read Start Developing iOS Apps Today to understand the basic
process for developing iOS apps. Then read View Controller Programming Guide for iOS for a comprehensive
look at view controllers and storyboards. Finally, to gain valuable hands-on experience using table views in a
storyboard, read the tutorial Your Second iOS App: Storyboards.
The information presented in this introduction and in “Table View Styles and Accessory Views” (page 12)
summarizes prescriptive information on table views presented in iOS Human Interface Guidelines. You can find
a complete description of the styles and characteristics of table views, as well as their recommended uses, in
the chapter “iOS UI Element Usage Guidelines”.
See Also
You will find the following sample code projects to be instructive models for your own table view
implementations:
● SimpleDrillDown project
● Table View Animations and Gestures project
For guidance on how to use the standard container view controllers provided by UIKit, see View Controller
Catalog for iOS . This document describes split view controllers and navigation controllers, which can both
contain table view controllers as children.
About Table Views in iOS Apps
Prerequisites
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
11Table views come in distinctive styles that are suitable for specific purposes. In addition, the UIKit framework
provides standard styles for the cells used to draw the rows of table views. It also gives you standard accessory
views (that is, controls) that you can include in cells.
Table View Styles
There are two major styles of table views: plain and grouped. The two styles are distinguished mainly by
appearance.
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
12
Table View Styles and Accessory ViewsPlain Table Views
A table view in the plain (or regular) style displays rows that stretch across the screen and have a creamy white
background (see Figure 1-1). A plain table view can have one or more sections, sections can have one or more
rows, and each section can have its own header or footer title. (A header or footer may also have a custom
view, for instance one containing an image). When the user scrolls through a section with many rows, the
header of the section floats to the top of the table view and the footer of the section floats to the bottom.
Figure 1-1 A table view in the plain style
A variation of plain table views associates an index with sections for quick navigation; Figure 1-2 shows an
example of this kind of table view, which is called an indexed list. The index runs down the right edge of the
table view. Entries in the index correspond to section header titles. Touching an item in the index scrolls the
table view to the associated section. For example, the section headings could be two-letterstate abbreviations,
Table View Styles and Accessory Views
Table View Styles
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
13and the rows for a section could be the cities in that state; touching at a certain spot in the index displays the
cities for the selected state. The rows in indexed lists should not have disclosure indicators or detail disclosure
buttons, because these interfere with the index.
Figure 1-2 A table view configured as an indexed list
Table View Styles and Accessory Views
Table View Styles
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
14The simplest kind of table view is a selection list (see Figure 1-3). A selection list is a plain table view that
presents a menu of optionsthat users can select. It can limit the selection to one row or allow multiple selections.
A selection list marks a selected row with a checkmark (see Figure 1-3).
Figure 1-3 A table view configured as a selection list
Grouped Table Views
A grouped table view also displays a list of information, but it groups related rows in visually distinct sections.
As shown in Figure 1-4, each section has rounded corners and by default appears against a bluish-gray
background. Each section may have text or an image for its header or footer to provide some context or
Table View Styles and Accessory Views
Table View Styles
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
15summary for the section. A grouped table works especially well for displaying the most detailed information
in a data hierarchy. It allows you to separate detailsinto conceptual groups and provide contextual information
to help users understand it quickly.
Figure 1-4 A table view in the grouped style
Table View Styles and Accessory Views
Table View Styles
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
16The headers and footers of sections in a grouped table view have relative locations and sizes as indicated in
Figure 1-5.
Figure 1-5 Header and footer of a section
Padding
Padding
Header
Table cell
Footer
On iPad devices, a grouped table view automatically gets wider margins when the table view itself is wide.
Standard Styles for Table View Cells
In addition to defining two styles of table views, the UIKit framework defines four styles for the cells that a
table view uses to draw its rows. You may create custom table view cells with different appearances if you
want, but these four predefined cell styles are suitable for most purposes. The techniques for creating table
view cells in a predefined style and for creating custom cells are described in “A Closer Look at Table View
Cells” (page 55).
Table View Styles and Accessory Views
Standard Styles for Table View Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
17The defaultstyle for table view rows uses a simple cellstyle that has a single title and an optional image (Figure
1-6). This style is associated with the UITableViewCellStyleDefault constant.
Figure 1-6 Default table row style
Table View Styles and Accessory Views
Standard Styles for Table View Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
18The cell style for the rows in Figure 1-7 left-aligns the main title and puts a gray subtitle under it. It also permits
an image in the default image location. This style is associated with the UITableViewCellStyleSubtitle
constant.
Figure 1-7 Table row style with a subtitle under the title
Table View Styles and Accessory Views
Standard Styles for Table View Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
19The cell style for the rows in Figure 1-8 left-aligns the main title. It puts the subtitle in blue text and right-aligns
it on the right side of the row. Images are not permitted. This style is used in the Settings app, where the
subtitle indicatesthe currentsetting for a preference. It is associated with the UITableViewCellStyleValue1
constant.
Figure 1-8 Table row style with a right-aligned subtitle
Table View Styles and Accessory Views
Standard Styles for Table View Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
20The cell style for the rows in Figure 1-9 puts the main title in blue and right-aligns it at a point that’s indented
from the left side of the row. The subtitle is left aligned at a short distance to the right of this point. This style
does not allow images. It is used in the Contacts part of the Phone app and is associated with the
UITableViewCellStyleValue2 constant.
Figure 1-9 Table row style in Contacts format
Accessory Views
There are three standard kinds of accessory views (shown with their accessory-type constants):
Standard accessory views Description
Disclosure indicator—UITableViewCellAccessoryDisclosureIndicator. You use the disclosure indicator when selecting a cell results
in the display of another table view reflecting the next level in the data
model hierarchy.
Detail disclosure button—UITableViewCellAccessoryDetailDisclosureButton. You use the detail disclosure button when selecting
a cell resultsin a detail view of that item (which may or may not be a table
view).
Table View Styles and Accessory Views
Accessory Views
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
21Standard accessory views Description
Checkmark—UITableViewCellAccessoryCheckmark. You use a
checkmark when a touch on a row results in the selection of that item.
This kind of table view is known as a selection list, and it is analogous to
a pop-up list. Selection lists can limit selections to one row, or they can
allow multiple rows with checkmarks.
Instead of the standard accessory views, you may specify a control (for example, a switch) or a custom view as
the accessory view.
Table View Styles and Accessory Views
Accessory Views
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
22The table view programming interface includesseveral UIKit classes, two formal protocols, and a category added
to a Foundation framework class.
Table View
A table view itself is an instance of the UITableView class. You use its methods to configure the appearance
of the table view—for example,specifying the default height of rows or providing a subview used asthe header
for the table. Other methods give you access to the currently selected row as well as specific rows or cells. You
can call other methods of UITableView to manage selections, scroll the table view, and insert or delete rows
and sections.
UITableView inherits from the UIScrollView class, which defines scrolling behavior for views with content
larger than the size of the window. UITableView redefines the scrolling behavior to allow vertical scrolling
only.
Table View Controller
The UITableViewController class manages a table view and addssupport for many standard table-related
behaviors such as selection management, row editing, table configuration, and others. This additional support
is there to minimize the amount of code you have to write to create and initialize your table-based interface.
You don’t use this class directly—instead you subclass UITableViewController to add custom behaviors.
Data Source and Delegate
A UITableView object must have a delegate and a data source. Following the Model-View-Controller design
pattern, the data source mediates between the app’s data model (that is, its model objects) and the table view.
The delegate, on the other hand, manages the appearance and behavior of the table view. The data source
and the delegate are often (but not necessarily) the same object, and that object is usually a custom subclass
of UITableViewController. (See “Navigating a Data Hierarchy with Table Views” (page 25) for further
information.)
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
23
Overview of the Table View APIThe data source adoptsthe UITableViewDataSource protocol. UITableViewDataSource hastwo required
methods. The tableView:numberOfRowsInSection: method tellsthe table view how many rowsto display
in each section, and the tableView:cellForRowAtIndexPath: method provides the cell to display for
each row in the table. Optional methods allow the data source to configure multiple sections, provide headers
and/or footers, and support adding, removing, and reordering rows in the table.
The delegate adoptsthe UITableViewDelegate protocol. This protocol has no required methods. It declares
methods that allow the delegate to modify visible aspects of the table view, manage selections, support an
accessory view, and support editing of individual rows in a table.
An app can make use of the convenience class UILocalizedIndexedCollation to help the data source
organize the data for indexed lists and display the proper section when users tap an item in the index. The
UILocalizedIndexedCollation class also localizes section titles.
Extension to the NSIndexPath Class
Many table view methods use index paths as parameters or return values. An index path identifies a path to
a specific node in a tree of nested arrays, and in the Foundation framework it isrepresented by an NSIndexPath
object. UIKit declares a category on NSIndexPath with methods that return key paths, locate rows in sections,
and construct NSIndexPath objects from row and section indexes. For more information, see NSIndexPath
UIKit Additions.
Table View Cells
As noted in “Data Source and Delegate” (page 23), the data source must return a cell object for each visible
row that a table view displays. These cell objects must inherit from the UITableViewCell class. This class
includes methodsfor managing cellselection and editing, managing accessory views, and configuring the cell.
You can instantiate cells directly in the standard styles defined by the UITableViewCell class and give these
cells content consisting of one or two strings of text and, in some styles, both image and text. Instead of using
a cell in a standard style, you can put your own custom subviews in the content view of an “off-the-shelf” cell
object. You may also subclass UITableViewCell to customize the appearance and behavior of table view
cells. These approaches are all discussed in “A Closer Look at Table View Cells” (page 55).
Overview of the Table View API
Extension to the NSIndexPath Class
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
24A common use of table views—and one to which they’re ideally suited—is to navigate hierarchies of data. A
table view at a top level of the hierarchy lists categories of data at the most general level. Users select a row
to “drill down” to the next level in the hierarchy. At the bottom of the hierarchy is a view (often a table view)
that presents details about a specific item (for example, an address book record) and may allow users to edit
the item. This section explains how you can map the levels of the data model hierarchy to a succession of table
views and describes how you can use the facilities of the UIKit framework to help you implement such
navigation-based apps.
Hierarchical Data Models and Table Views
For a navigation-based app, you typically design your app data as a graph of model objects that is sometimes
referred to as the app’s data model. You can then implement the model layer of your app using various
mechanisms or technologies, including Core Data, property lists, or archives of custom objects. Regardless of
the approach, the traversal of your app’s data model follows patterns that are common to all navigation-based
apps. The data model has hierarchical depth, and objects at variouslevels of this hierarchy should be the source
for populating the rows of a table view.
Note: To learn about the Core Data technology and framework, see Core Data Starting Point.
The Data Model as a Hierarchy of Model Objects
A well-designed app factors its classes and objects in a way that conforms to the Model-View-Controller (MVC)
design pattern. The app’s data model consists of the model objects in this pattern. You can describe model
objects (using the terminology provided by the object modeling pattern) in terms of their properties. These
properties are of two general kinds: attributes and relationships.
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
25
Navigating a Data Hierarchy with Table ViewsNote: The notion of “property” here is abstractly related to, but not identical with, the declared
property feature of Objective-C. A class definition typically represents properties programmatically
through instance variables and declared properties. For more on declared properties, see The
Objective-C Programming Language . To find out more about MVC and object modeling, read “Cocoa
Design Patterns” in Cocoa Fundamentals Guide .
Attributes represent elements of model-object data. Attributes can range from an instance of a primitive class
(for example, an NSString, NSDate, or UIColor object) to a C structure or a simple scalar value. Attributes
are generally what you use to populate a table view that represents a “leaf node” of the data hierarchy and
that presents a detail view of that item.
A model object may also have relationships with other model objects. It is through these relationships that a
data model acquires hierarchical depth by composing an object graph. Relationships are of two general kinds
in terms of cardinality: to-one and to-many. To-one relationships define an object’s relationship with another
object (for example, a parent relationship). A to-many relationship, on the other hand, defines an object’s
relationship with multiple objects of the same kind. The to-many relationship is characterized by containment
and can be programmatically represented by collections such as NSArray objects (or, simply, arrays). An array
might contain other arrays, or it could contain multiple dictionaries, which are collections that identify their
contained valuesthrough keys. Dictionaries, in turn, can contain one or more other collections, including arrays,
sets, and even other dictionaries. As collections nest in other collections, your data model can acquire hierarchical
depth.
Table Views and the Data Model
The rows of a plain table view are typically backed by collection objects of the app’s data model; these objects
are usually arrays. Arrays contain strings or other elements that a table view can use when displaying row
content. When you create a table view (described in “Creating and Configuring a Table View” (page 36)), it
immediately queries its data source for its dimensions—that is, it requests the number of sections and the
number of rows per section—and then asks for the content of each row. The data source fetches this content
from an array in the appropriate level of the data-model hierarchy.
In many of the methods defined for a table view’s data source and delegate, the table view passes in an index
path to identify the section and row that is the focus of the current operation—for example, fetching content
for a row or indicating the row the user tapped. An index path is an instance of the Foundation framework’s
NSIndexPath classthat you can use to identify an item in a tree of nested arrays. The UIKit framework extends
NSIndexPath to add a section and a row property to the class. The data source should use these properties
to map a section and row of the table view to a value at the corresponding index of the array being used as
the table view’s source of data.
Navigating a Data Hierarchy with Table Views
Hierarchical Data Models and Table Views
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
26Note: The UIKit framework extension of the NSIndexPath class is described in NSIndexPath UIKit
Additions.
In the sequence of table views in Figure 3-1, the top level of the data hierarchy is an array of four arrays, with
each inner array containing objects representing the trails for a particular region. When the user selects one
of these regions, the next table view lists names identifying the trails within the selected array. When the user
selects a particular trail, the next table view describes that trail using a grouped table view.
Figure 3-1 Mapping levels of the data model to table views
"Name" =
"Sylvan Trail Loop"
"Location" =
"Edgewood City Park
(Redwood City)"
"Distance" = 2
"Difficulty" = "Moderate"
"Restrictions" =
"No bicycles, pets, or
horses"
"Map" = pen_map6.png
// other key/value pairs
Alambique-Skyline
Sweeny Ridge
Sawyer Camp Trail
Purisima Creek
Dean-Crystal Springs
Sylvan Trail Loop
regions array trails array trail dictionary
East Bay
North Bay
Peninsula
South Bay
Navigating a Data Hierarchy with Table Views
Hierarchical Data Models and Table Views
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
27Note: You could easily redesign the app in Figure 3-1 (page 27) to have only two table views. The
first table view would be an indexed list of trails by region. The second table view would display the
detail for a selected trail.
View Controllers and Navigation-Based Apps
The UIKit framework provides a number of view controller classesfor managing common user interface patterns
in iOS. View controllers are controller objects that inherit from the UIViewController class. They are an
essential tool for view management, especially when an app uses those views to present successive levels of
its data hierarchy. This section describes how two subclasses of UIViewController, navigation controllers
and table view controllers, present and manage a succession of table views.
Note: Thissection gives an overview of view controllersto provide some background for the coding
tasks discussed later in this document. To learn about view controllers in depth, see View Controller
Programming Guide for iOS .
Navigation Controllers
The UINavigationController classinheritsfromUIViewController, a base classthat definesthe common
programmatic interface and behavior for controller objects that manage views in iOS. Through inheritance from
this base class, a view controller acquires an interface for general view management. After it implements parts
of thisinterface, a view controller can autorotate its view, respond to low-memory notifications, overlay “modal”
views, respond to taps on the Edit button, and otherwise manage the view.
A navigation controller maintains a stack of view controllers, one for each of the table views displayed (see
Figure 3-2). It begins with what’s known as the root view controller. When the user taps a row of the table
view (often on a detail disclosure button), the root view controller pushes the next view controller onto the
stack. The new view controller’s table view visually slides into place from the right, and the navigation bar
Navigating a Data Hierarchy with Table Views
View Controllers and Navigation-Based Apps
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
28items are updated appropriately. When users tap the back button in the navigation bar, the current view
controller is popped off the stack. As a consequence, the navigation controller displaysthe table view managed
by the view controller that is now at the top of the stack.
Figure 3-2 Navigation controller and view controllers in a navigation-based app
UITableView
UIViewController
UINavigationController
UINavigationBar
Navigation Bars
Navigation bars are a user-interface device that enables users to navigate a hierarchy of data. Users start with
general, top-level items and “drill down” the hierarchy to detailed viewsshowing specific properties of leaf-node
items. The view below the navigation bar presents the current level of data. A navigation bar includes a title
for the current view and, if that view is lower in the hierarchy than the top level, a back button on the left side
of the bar; the back button is a navigation control that the user taps to return to the previous level. (The back
Navigating a Data Hierarchy with Table Views
View Controllers and Navigation-Based Apps
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
29button by default displaysthe title for the previous view.) A navigation bar may also have an Edit button—used
to enter editing mode for the current view—or custom buttons for functions that manage content (see Figure
3-3).
Figure 3-3 Navigation bars and common control items
Navigational control Controls to manage content
A UINavigationController manages the navigation bar, including the items that are displayed in the bar
for the view below it. A UIViewController object manages a view displayed below the navigation bar. For
this view controller, you create a subclass of UIViewController or a subclass of a view controller class that
the UIKit framework provides for managing a particular type of view. For table views, this view controller class
is UITableViewController. For a navigation controller that displays a sequence of table views reflecting
levels within a data hierarchy, you need to create a separate custom table view controller for each table view.
The UIViewController class includes methods that let view controllers access and set the navigation items
displayed in the navigation bar for the currently displayed table view. This class also declares a title property
through which you can set the title of the navigation bar for the current table view.
Navigating a Data Hierarchy with Table Views
View Controllers and Navigation-Based Apps
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
30Table View Controllers
Although you could manage a table view using a direct subclass of UIViewController, you save yourself a
lot of work if instead you subclass UITableViewController. The UITableViewController class takes
care of many of the details you would have to implement if you created a directsubclass of UIViewController
to manage a table view.
The recommended way to create a table view controller is to specify it in a storyboard. The associated table
view isloaded from the storyboard, along with the table view’s attributes,size, and autoresizing characteristics.
The table view controller sets itself as the data source and the delegate of the table view.
Note: You can create a table view controller programmatically by allocating memory for it and
initializing it with the initWithStyle: method, passing in either UITableViewStylePlain or
UITableViewStyleGrouped for the required table view style.
When the table view is about to appear for the first time, the table view controller sends reloadData to the
table view, which prompts it to request data from its data source. The data source tells the table view how
many sections and rows per section it wants, and then gives the table view the data to display in each row.
This process is described in “Creating and Configuring a Table View” (page 36).
The UITableViewController class also performs other common tasks. It clears selections when the table
view is about to be displayed and flashes the scroll indicators when the table finishes displaying. In addition,
it responds properly when users tap the Edit button by putting the table view into editing mode (or taking it
out of editing mode if users tap Done). The class exposes one property, tableView, which gives you access
to the managed table view.
Note: A table view controller supports inline editing of table view rows; if, for example, rows have
embedded text fields in editing mode, it scrolls the row being edited above the virtual keyboard
that is displayed. It also supports the NSFetchedResultsController class for managing the
results returned from a Core Data fetch request.
The UITableViewController class implements the foregoing behavior by overriding loadView,
viewWillAppear:, and other methods inherited from UIViewController. In your subclass of
UITableViewController, you may also override these methods to acquire specialized behavior. If you do
override these methods, be sure to invoke the superclass implementation of the method, usually as the first
method call, to get the default behavior.
Navigating a Data Hierarchy with Table Views
View Controllers and Navigation-Based Apps
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
31Note: You should use a UIViewController subclass rather than a subclass of
UITableViewController to manage a table view if the view to be managed is composed of
multiple subviews, only one of which is a table view. The default behavior of the
UITableViewController class is to make the table view fill the screen between the navigation
bar and the tab bar (if either are present).
If you decide to use a UIViewController subclass rather than a subclass of
UITableViewController to manage a table view, you should perform a couple of the tasks
mentioned above to conform to the human interface guidelines. To clear any selection in the table
view before it’s displayed, implement the viewWillAppear: method to clear the selected row (if
any) by calling deselectRowAtIndexPath:animated:. After the table view has been displayed,
you should flash the scroll view’sscroll indicators by sending a flashScrollIndicators message
to the table view; you can do this in an override of the viewDidAppear: method of
UIViewController.
Managing Table Views in a Navigation-Based App
A UITableViewController object—or any other object that assumes the roles of data source and delegate
for a table view—must respond to messages sent by the table view in order to populate its rows, configure it,
respond to selections, and manage editing sessions. In the rest of this document, you learn how to do these
things. However, there are certain other things you need to do to ensure the proper display of a sequence of
table views in a navigation-based app.
Note: Thissection summarizes view-controller and navigation-controller tasks, with a focus on table
views. For a thorough discussion of view controllers and navigation controllers, including the complete
details of their implementation, see View Controller Programming Guide for iOS and View Controller
Catalog for iOS .
At this point, let’s assume that a table view managed by a table view controller presents a list to the user. How
does the app display the next table view in the sequence?
When a user taps a row of the table view, the table view callsthe tableView:didSelectRowAtIndexPath:
or tableView:accessoryButtonTappedForRowWithIndexPath:method implemented by the delegate.
(That latter method is invoked if the user taps a row’s detail disclosure button.) The delegate creates the table
view controller managing the next table view in the sequence, sets the data it needs to populate its table view,
and pushes this new view controller onto the navigation controller’s stack of view controllers. A storyboard
provides the specification that allows UIKit to perform most of this work for you.
Navigating a Data Hierarchy with Table Views
View Controllers and Navigation-Based Apps
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
32Storyboards represent the screens in an app and the transitions between them. The storyboard in a basic app
may contain just a few screens, but a more complex app might have multiple storyboards, each of which
represents a different subset of its screens. The storyboard example in Figure 3-4 presents a graphical
representation of each scene, its contents, and its connections.
Figure 3-4 A storyboard with two table view controllers
A scene represents an onscreen content area that is managed by a view controller. (In the context of a
storyboard, scene and view controller are synonymous terms.) The leftmost scene in the default storyboard
represents a navigation controller. A navigation controller is a container view controller because, in addition
to its views, it also manages a set of other view controllers. For example, the navigation controller in Figure
3-4 (page 33) manages the master and detail view controllers, in addition to the navigation bar and the back
button that you see when you run the app.
A relationship is a type of connection between scenes. In Figure 3-4, there is a relationship between the
navigation controller and the master scene. In this case, the relationship represents the containment of the
master and detailscenes by the navigation controller. When the app runs, the navigation controller automatically
loads the master scene and displays the navigation bar at the top of the screen.
A segue represents a transition from one scene (called the source) to the next scene (called the destination).
For example, in Figure 3-4, the master scene is the source and the detail scene is the destination. When you
select the Detail item in the master list, you trigger a segue from the source to the destination. In this case, the
segue is a push segue, which means that the destination scene slides over the source scene from right to left.
As the detail screen is revealed, a back button appears at the left end of the navigation bar, titled with the
previous screen’s title (in this case, “Master”). The back button is provided automatically by the navigation
controller that manages the master-detail hierarchy.
Navigating a Data Hierarchy with Table Views
View Controllers and Navigation-Based Apps
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
33Storyboards make it easy to pass data from one scene to another via the prepareForSegue:sender: method
of the UIViewController class. This method is called when the first scene (the source) is about to transition
to the next scene (the destination). The source view controller can implement prepareForSegue:sender:
to perform setup tasks, such as passing information to the destination view controller about what it should
display in its table view. Listing 3-1 shows one implementation of this method.
Listing 3-1 Passing data to a destination view controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"ShowDetails"]) {
MyDetailViewController *detailViewController = [segue destinationViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
detailViewController.data = [self.dataController
objectInListAtIndex:indexPath.row];
}
}
A segue represents a one-way transition from a source scene to a destination scene. One of the consequences
of this design is that you can use a segue to pass data to a destination, but you can’t use a segue to send data
from a destination to its source. To solve this problem, you create a delegate protocol that declares methods
that the destination view controller calls when it needs to pass back some data.
Listing 3-2 shows one implementation of a protocol for passing data back to a source view controller.
Listing 3-2 Passing data to a source view controller
@protocol MyAddViewControllerDelegate
- (void)addViewControllerDidCancel:(MyAddViewController *)controller;
- (void)addViewControllerDidFinish:(MyAddViewController *)controller data:(NSString
*)item;
@end
- (void)addViewControllerDidCancel:(MyAddViewController *)controller {
[self dismissViewControllerAnimated:YES completion:NULL];
}
Navigating a Data Hierarchy with Table Views
View Controllers and Navigation-Based Apps
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
34- (void)addViewControllerDidFinish:(MyAddViewController *)controller data:(NSString
*)item {
if ([item length]) {
[ self.dataController addData:item];
[[self tableView] reloadData];
}
[self dismissViewControllerAnimated:YES completion:NULL];
}
Note: The full details of creating storyboards are described in Xcode 4 User Guide . To learn more
about using view controllers in storyboards, see View Controller Programming Guide for iOS .
Design Pattern for Navigation-Based Apps
A navigation-based app with table views should follow these design best practices:
● A view controller (typically a subclass of UITableViewController), acting in the role of data source,
populates its table view with data from an object representing a level of the data hierarchy.
When the table view displays a list of items, the object is typically an array. When the table view displays
item detail (that is, a leaf node of the data hierarchy), the object can be a custom model object, a Core
Data managed object, a dictionary, or something similar.
● The view controller stores the data it needs for populating its table view.
The view controller can use this data directly for populating the table view, or it can use it to fetch or
otherwise obtain the necessary data. When you design your view controller subclass, you should define
a property to hold this data.
View controllers should not obtain the data for their table view through a global variable or a singleton
objectsuch asthe app delegate. Such direct dependencies make your code lessreusable and more difficult
to test and debug.
● The current view controller on top of the navigation-controller stack creates the next view controller in
the sequence and, before it pushes it onto the stack, sets the data that this view controller, acting as data
source, needs to populate its table view.
Navigating a Data Hierarchy with Table Views
Design Pattern for Navigation-Based Apps
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
35Your app must present a table view to users before it can manage it in response to taps on rows and other
actions. This chapter shows what you must do to create a table view, configure it, and populate it with data.
Most of the code examples shown in this chapter come from the sample projects TableViewSuite and
TheElements.
Basics of Table View Creation
To create a table view, several entities in an app must interact: the view controller, the table view itself, and
the table view’s data source and delegate. The view controller, data source, and delegate are usually the same
object. The view controller starts the calling sequence, diagrammed in Figure 4-1 (page 37).
1. The view controller creates a UITableView instance in a certain frame and style. It can do this either
programmatically or in a storyboard. The frame is usually set to the screen frame, minus the height of the
status bar or, in a navigation-based app, to the screen frame minus the heights of the status bar and the
navigation bar. The view controller may also set global properties of the table view at this point, such as
its autoresizing behavior or a global row height.
To learn how to create table views in a storyboard and programmatically, see “Creating a Table View Using
a Storyboard” (page 38) and “Creating a Table View Programmatically” (page 43).
2. The view controllersetsthe data source and delegate of the table view and sends a reloadData message
to it. The data source must adopt the UITableViewDataSource protocol, and the delegate must adopt
the UITableViewDelegate protocol.
3. The data source receives a numberOfSectionsInTableView: message from the UITableView object
and returns the number of sections in the table view. Although this is an optional protocol method, the
data source must implement it if the table view has more than one section.
4. For each section, the data source receives a tableView:numberOfRowsInSection: message and
responds by returning the number of rows for the section.
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
36
Creating and Configuring a Table View5. The data source receives a tableView:cellForRowAtIndexPath: message for each visible row in the
table view. It responds by configuring and returning a UITableViewCell object for each row. The
UITableView object uses this cell to draw the row.
Figure 4-1 Calling sequence for creating and configuring a table view
tableView:
cellForRowAtIndexPath:
Set data source and delegate
Client
Data Source
Table View
initWithFrame:style:
numberOfSectionsInTableView:
tableView:numberOfRowsInSection:
The diagram in Figure 4-1 shows the required protocol methods as well as the
numberOfSectionsInTableView: method. Populating the table view with data occurs in steps 3 through
5. To learn how to implement the methods in these steps, see “Populating a Dynamic Table View with
Data” (page 44).
The data source and the delegate may implement other optional methods of their protocolsto further configure
the table view. For example, the data source might want to provide titles for each of the sections in the table
view by implementing the tableView:titleForHeaderInSection: method. For more on some of these
optional table view customizations, see “Optional Table View Configurations” (page 52).
You create a table view in either the plain style (UITableViewStylePlain) or the grouped style
(UITableViewStyleGrouped). (You specify the style in a storyboard.) Although the procedure for creating
a table view in eitherstylesisidentical, you may want to perform different kinds of configurations. For example,
because a grouped table view generally presents item detail, you may also want to add custom accessory
views (for example, switches and sliders) or custom content (for example, text fields). For an example, see “A
Closer Look at Table View Cells” (page 55).
Creating and Configuring a Table View
Basics of Table View Creation
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
37Recommendations for Creating and Configuring Table Views
There are many ways to put together a table view app. For example, you can use an instance of a custom
NSObject subclass to create, configure, and manage a table view. However, you will find the task much easier
if you adopt the classes, techniques, and design patterns that the UIKit framework offers for this purpose. The
following approaches are recommended:
● Use an instance of a subclass of UITableViewController to create and manage a table view.
Most apps use a custom UITableViewController object to manage a table view. As described in
“Navigating a Data Hierarchy with Table Views” (page 25), UITableViewController automatically
creates a table view, assignsitself as both delegate and data source (and adoptsthe corresponding protocols),
and initiates the procedure for populating the table view with data. It also takes care of several other
“housekeeping” details of behavior. The behavior of UITableViewController (a subclass of
UIViewController) within the navigation controller architecture is described in “Table View
Controllers” (page 31).
●
If your app is largely based on table views, select the Master-Detail Application template provided by
Xcode when you create your project.
As described in “Creating a Table View Using a Storyboard” (page 38), the template includes stub code
and a storyboard defining an app delegate, the navigation controller, and the master view controller (which
is an instance of a custom subclass of UITableViewController).
● For successive table views, you should implement custom UITableViewController objects. You can
either load them from a storyboard or create the associated table views programmatically.
Although either option is possible, the storyboard route is generally easier.
If the view to be managed is a composite view in which a table view is one of multiple subviews, you must
use a custom subclass of UIViewController to manage the table view (and other views). Do not use
UITableViewController, because this controller class sizes the table view to fill the screen between the
navigation bar and the tab bar (if either is present).
Creating a Table View Using a Storyboard
Create an app with a table view using Xcode. When you create your project, select a template that contains
stub code and a storyboard that, by default, supply the structure for setting up and managing table views.
To create an app structured around table views
1. In Xcode, choose File > New > Project.
Creating and Configuring a Table View
Recommendations for Creating and Configuring Table Views
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
382. In the iOS section at the left side of the dialog, select Application.
3. In the main area of the dialog, select Master-Detail Application and then click Next.
4. Choose your project options (make sure Use Storyboard is selected), and then click Next.
5. Choose a save location for your project and then click Create.
Depending on which device family you chose in step 4, the project has one or two storyboards. To display the
storyboard canvas, double-click a storyboard file in the project navigator. If the device family is iPhone, for
example, your storyboard should contain a table view controller that looks similar to the one in Figure 4-2.
Figure 4-2 The master view controller in the Master-Detail Application storyboard
To make sure that the scene on the canvas represents the master view controller class in
your code
1. On the canvas, click the scene’s title bar to select the table view controller.
2. Click the Identity button at the top of the utility area to open the Identity inspector.
3. Verify that the Class field contains the project’s custom subclass of UITableViewController.
Choose the Table View’s Display Style
As described in “Table View Styles” (page 12), every table view has a display style: plain or grouped.
To choose the display style of a table view in a storyboard
1. Click the center of the scene to select the table view.
Creating and Configuring a Table View
Creating a Table View Using a Storyboard
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
392. In the utility area, display the Attributes inspector.
3. In the Table View section of the Attributes inspector, use the Style pop-up menu to choose Plain or
Grouped.
Choose the Table View’s Content Type
Storyboards introduce two convenient ways to design a table view’s content:
● Dynamic prototypes. Design a prototype cell and then use it as the template for other cells in the table.
Use a dynamic prototype when multiple cells in a table should use the same layout to display information.
Dynamic content is managed by the table view data source (the table view controller) at runtime, with an
arbitrary number of cells. Figure 4-3 shows a plain table view with a one prototype cell.
Figure 4-3 A dynamic table view
Note: If a table view in a storyboard is dynamic, the custom subclass of
UITableViewController that contains the table view needs to implement the data source
protocol. For more information, see “Populating a Dynamic Table View with Data” (page 44).
Creating and Configuring a Table View
Creating a Table View Using a Storyboard
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
40● Static cells. Use static content to design the overall layout of the table, including the total number of cells.
A table view with static content has a fixed set of cells that you can configure at design time. You can also
configure otherstatic data elementssuch assection headers. Use static cells when a table does not change
its layout, regardless of the specific information it displays. Figure 4-4 shows a grouped table view with
three static cells.
Figure 4-4 A static table view
Note: If a table view in a storyboard isstatic, the custom subclass of UITableViewController
that contains the table view should not implement the data source protocol. Instead, the table
view controllershould use its viewDidLoad method to populate the table view’s data. For more
information, see “Populating a Static Table View With Data” (page 46).
By default, when you add a table view controller to a storyboard, the controller contains a table view that uses
prototype-based cells. If you want to use static cells:
1. Select the table view.
2. Display the Attributes inspector.
3. In the the Content pop-up menu, choose Static Cells.
Creating and Configuring a Table View
Creating a Table View Using a Storyboard
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
41If you’re designing a prototype cell, the table view needs a way to identify the prototype when the data source
dequeues reusable cells for the table at runtime. Therefore you must assign a reuse identifier to the cell. In the
Table View Cell section of the Attributes inspector, enter an ID in the Identifier text field. By convention, a cell’s
reuse identifier should describe what the cell contains, such as BirdSightingCell.
Design the Table View’s Rows
As described in “Standard Styles for Table View Cells” (page 17), UIKit defines four styles for the cells that a
table view uses to draw its rows. You can use one of the four standard styles, design a custom style, or subclass
UITableViewCell to define additional behavior or properties for the cell. This topic is covered in detail in “A
Closer Look at Table View Cells” (page 55).
A table view cell can also have an accessory, as described in “Accessory Views” (page 21). An accessory is a
standard user interface element that UIKit draws at the right end of a table cell. For example, the disclosure
indicator, which looks similar to a right angle bracket (>), tells users that tapping an item reveals related
information in a new screen. In the Attributes inspector, use the Accessory pop-up menu to select a cell’s
accessory.
Create Additional Table Views
If your app displays and manages more than one table view, add those table views to your storyboard. You
add a table view by adding a custom UITableViewController object, which contains the table view it
manages.
To add custom class files to your project
1. In Xcode, choose File > New > File.
2. In the iOS section at the left side of the dialog, select Cocoa Touch.
3. In the main area of the dialog, select Objective-C class, and then click Next.
4. Enter a name for your new class, choose subclass of UITableViewController, and then click Next.
5. Choose a save location for your class files, and then click Create.
To add a table view controller to a storyboard
1. Display the storyboard to which you want to add the table view controller.
2. Drag a table view controller out of the object library and drop it on the storyboard.
3. With the new scene still selected on the canvas, click the Identity button in the utility area to open the
Identity inspector.
Creating and Configuring a Table View
Creating a Table View Using a Storyboard
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
424. In the Custom Class section, choose the new custom class in the Class pop-up menu.
5. Set the new table view’s style and cell content (dynamic or static).
6. Create a segue to the new scene.
The details of step 7 vary depending on the project. To learn more about adding segues, see Xcode 4 User
Guide .
Note: Populating a table view with data and configuring a table view are discussed in “Populating
a Dynamic Table View with Data” (page 44) and “Optional Table View Configurations” (page 52).
Learn More by Creating a Sample App
The tutorial Your Second iOS App: Storyboards shows how to create a sample app that is structured around
table views. After you complete the steps in this tutorial, you’ll have a working knowledge of how to create
both dynamic and static table views using a storyboard. The tutorial creates a basic navigation-based app
called BirdWatching that uses table view controllers connected by both push and modal segues.
Creating a Table View Programmatically
If you choose not to use UITableViewController for table view management, you must replicate what this
class gives you “for free.”
Adopt the Data Source and Delegate Protocols
The class creating the table view typically makes itself the data source and delegate by adopting the
UITableViewDataSource and UITableViewDelegate protocols. The adoption syntax appears just after
the superclass in the @interface directive, as shown in Listing 4-1.
Listing 4-1 Adopting the data source and delegate protocols
@interface RootViewController : UIViewController
@property (nonatomic, strong) NSArray *timeZoneNames;
@end
Creating and Configuring a Table View
Creating a Table View Programmatically
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
43Create and Configure a Table View
The next step is for the client to allocate and initialize an instance of the UITableView class. Listing 4-2 gives
an example of a client that creates a UITableView object in the plain style, specifies its autoresizing
characteristics, and then sets itself to be both data source and delegate. Again, keep in mind that the
UITableViewController does all of this for you automatically.
Listing 4-2 Creating a table view
- (void)loadView
{
UITableView *tableView = [[UITableView alloc] initWithFrame:[[UIScreen
mainScreen] applicationFrame] style:UITableViewStylePlain];
tableView.autoresizingMask =
UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
tableView.delegate = self;
tableView.dataSource = self;
[tableView reloadData];
self.view = tableView;
}
Because in this example the class creating the table view is a subclass of UIViewController, it assigns the
created table view to its view property, which it inherits from that class. It also sends a reloadData message
to the table view, causing the table view to initiate the procedure for populating its sections and rows with
data.
Populating a Dynamic Table View with Data
Just after a table view object is created, it receives a reloadData message, which tells it to start querying the
data source and delegate for the information it needs for the sections and rows it displays. The table view
immediately asks the data source for its logical dimensions—that is, the number of sections and the number
of rows in each section. It then repeatedly invokes the tableView:cellForRowAtIndexPath: method to
get a cell object for each visible row; it uses this UITableViewCell object to draw the content of the row.
(Scrolling a table view also causes an invocation of tableView:cellForRowAtIndexPath: for each newly
visible row.)
Creating and Configuring a Table View
Populating a Dynamic Table View with Data
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
44As noted in “Choose the Table View’s Content Type” (page 40), if the table view is dynamic then you need to
implement the required data source methods. Listing 4-3 shows an example of how the data source and the
delegate could configure a dynamic table view.
Listing 4-3 Populating a dynamic table view with data
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [regions count];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
// Number of rows is the number of time zones in the region for the specified
section.
Region *region = [regions objectAtIndex:section];
return [region.timeZoneWrappers count];
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
// The header for the section is the region name -- get this from the region
at the section index.
Region *region = [regions objectAtIndex:section];
return [region name];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = @"MyReuseIdentifier";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier]];
}
Region *region = [regions objectAtIndex:indexPath.section];
Creating and Configuring a Table View
Populating a Dynamic Table View with Data
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
45TimeZoneWrapper *timeZoneWrapper = [region.timeZoneWrappers
objectAtIndex:indexPath.row];
cell.textLabel.text = timeZoneWrapper.localeName;
return cell;
}
The data source, in its implementation of the tableView:cellForRowAtIndexPath: method, returns a
configured cell object that the table view can use to draw a row. For performance reasons, the data source
tries to reuse cells as much as possible. It first asks the table view for a specific reusable cell object by sending
it a dequeueReusableCellWithIdentifier: message. If no such object exists, the data source creates it,
assigning it a reuse identifier. The data source sets the cell’s content (in this example, its text) and returns it.
“A Closer Look at Table View Cells” (page 55) discusses this data source method and UITableViewCell
objects in more detail.
If the dequeueReusableCellWithIdentifier: method asks for a cell that’s defined in a storyboard, the
method always returns a valid cell. If there is not a recycled cell waiting to be reused, the method creates a
new one using the information in the storyboard itself. This eliminates the need to check the return value for
nil and create a cell manually.
The implementation of the tableView:cellForRowAtIndexPath: method in Listing 4-3 includes an
NSIndexPath argument that identifies the table view section and row. UIKit declares a category of the
NSIndexPath class, which is defined in the Foundation framework. This category extends the class to enable
the identification of table view rows by section and row number. For more information on this category, see
NSIndexPath UIKit Additions.
Populating a Static Table View With Data
As noted in “Choose the Table View’s Content Type” (page 40), if a table view is static then you should not
implement any data source methods. The configuration of the table view is known at compile time, so UIKit
can get this information from the storyboard at runtime. However, you still need to populate a static table
view with data from your data model. “Populating a Static Table View With Data” shows an example of how a
table view controller could load user data in a static table view. This example is adapted from Your Second iOS
App: Storyboards.
Listing 4-4 Populating a static table view with data
- (void)viewDidLoad
{
Creating and Configuring a Table View
Populating a Static Table View With Data
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
46[super viewDidLoad];
BirdSighting *theSighting = self.sighting;
static NSDateFormatter *formatter = nil;
if (formatter == nil) {
formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterMediumStyle];
}
if (theSighting) {
self.birdNameLabel.text = theSighting.name;
self.locationLabel.text = theSighting.location;
self.dateLabel.text = [formatter stringFromDate:(NSDate*)theSighting.date];
}
}
The table view is populated with data in the UIViewController method viewDidLoad, which is called after
the view is loaded into memory. The data is passed to the table view controller in the sighting object, which
isset in the previous view controller’s prepareForSegue:sender: method. The properties birdNameLabel,
locationLabel, and dateLabel are outlets connected to labelsin the static table view (see Figure 4-4 (page
41)).
Populating an Indexed List
An indexed list (see Figure 1-2 (page 14)) is ideally suited for navigating large amounts of data organized by
a conventional ordering scheme such as an alphabet. An indexed list is a table view in the plain style that is
specially configured through three UITableViewDataSource methods:
● sectionIndexTitlesForTableView:
Returns an array of the strings to use as the index entries (in order).
● tableView:titleForHeaderInSection:
Maps these index strings to the titles of the table view’s sections (they don’t have to be the same).
● tableView:sectionForSectionIndexTitle:atIndex:
Returns the section index related to the entry the user tapped in the index.
Creating and Configuring a Table View
Populating an Indexed List
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
47The data you use to populate an indexed list should be organized to reflect this indexing model. Specifically,
you need to build an array of arrays. Each inner array corresponds to a section in the table. Section arrays are
sorted (or collated) within the outer array according to the prevailing ordering scheme, which is often an
alphabetical scheme (for example, A through Z). Additionally, the items in each section array are sorted. You
can build and sort this array of arrays yourself, but fortunately the UILocalizedIndexedCollation class
greatly simplifies the tasks of building and sorting these data structures and providing data to the table view.
The class also collates items in the arrays according to the current localization.
However you internally manage this array-of-arrays structure is up to you. The objects to be collated should
have a property or method that returns a string value that the UILocalizedIndexedCollation class uses
in collation; if it is a method, it should have no parameters. You might find it convenient to define a custom
model class whose instances represent the rows in the table view. These model objects not only return a string
value but also define a property that holdsthe index of the section array to which the object is assigned. Listing
4-5 illustrates the definition of a class that declares a name property and a sectionNumber property.
Listing 4-5 Defining the model-object interface
@interface State : NSObject
@property(nonatomic,copy) NSString *name;
@property(nonatomic,copy) NSString *capitol;
@property(nonatomic,copy) NSString *population;
@property NSInteger sectionNumber;
@end
Before your table view controller is asked to populate the table view, you load the data to be used (from whatever
source) and create instances of your model class from this data. The example in Listing 4-6 loads data defined
in a property list and creates the model objects from that. It also obtains the shared instance of
UILocalizedIndexedCollation and initializes the mutable array (states) that will contain the section
arrays.
Listing 4-6 Loading the table-view data and initializing the model objects
- (void)viewDidLoad {
[super viewDidLoad];
UILocalizedIndexedCollation *theCollation = [UILocalizedIndexedCollation
currentCollation];
self.states = [NSMutableArray arrayWithCapacity:1];
Creating and Configuring a Table View
Populating an Indexed List
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
48NSString *thePath = [[NSBundle mainBundle] pathForResource:@"States"
ofType:@"plist"];
NSArray *tempArray;
NSMutableArray *statesTemp;
if (thePath && (tempArray = [NSArray arrayWithContentsOfFile:thePath]) ) {
statesTemp = [NSMutableArray arrayWithCapacity:1];
for (NSDictionary *stateDict in tempArray) {
State *aState = [[State alloc] init];
aState.name = [stateDict objectForKey:@"Name"];
aState.population = [stateDict objectForKey:@"Population"];
aState.capitol = [stateDict objectForKey:@"Capitol"];
[statesTemp addObject:aState];
}
} else {
return;
}
After the data source has this “raw” array of model objects, it can process it with the facilities of the
UILocalizedIndexedCollation class. In Listing 4-7, the code is annotated with numbers.
Listing 4-7 Preparing the data for the indexed list
// viewDidLoad continued...
// (1)
for (State *theState in statesTemp) {
NSInteger sect = [theCollation sectionForObject:theState
collationStringSelector:@selector(name)];
theState.sectionNumber = sect;
}
// (2)
NSInteger highSection = [[theCollation sectionTitles] count];
NSMutableArray *sectionArrays = [NSMutableArray arrayWithCapacity:highSection];
for (int i = 0; i < highSection; i++) {
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:1];
[sectionArrays addObject:sectionArray];
}
Creating and Configuring a Table View
Populating an Indexed List
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
49// (3)
for (State *theState in statesTemp) {
[(NSMutableArray *)[sectionArrays objectAtIndex:theState.sectionNumber]
addObject:theState];
}
// (4)
for (NSMutableArray *sectionArray in sectionArrays) {
NSArray *sortedSection = [theCollation sortedArrayFromArray:sectionArray
collationStringSelector:@selector(name)];
[self.states addObject:sortedSection];
}
} // end of viewDidLoad
Here's what the code in Listing 4-7 does:
1. The data source enumerates the array of model objects and sends
sectionForObject:collationStringSelector: to the collation manager on each iteration. This
method takes as arguments a model object and a property or method of the object that it usesin collation.
Each call returns the index of the section array to which the model object belongs, and that value is
assigned to the sectionNumber property.
2. The data source source then creates a (temporary) outer mutable array and mutable arraysfor each section;
it adds each created section array to the outer array.
3. It then enumerates the array of model objects and adds each object to its assigned section array.
4. The data source enumerates the array of section arrays and calls
sortedArrayFromArray:collationStringSelector: on the collation manager to sort the items in
each array. It passes in a section array and a property or method that is to be used in sorting the items in
the array. Each sorted section array is added to the final outer array.
Now the data source is ready to populate its table view with data. It implements the methods specific to
indexed lists as shown in Listing 4-8. In doing this it calls two UILocalizedIndexedCollation methods:
sectionIndexTitles and sectionForSectionIndexTitleAtIndex:. Also note that in
tableView:titleForHeaderInSection: itsuppresses any headersfrom appearing in the table view when
the associated section does not have any items.
Listing 4-8 Providing section-index data to the table view
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
Creating and Configuring a Table View
Populating an Indexed List
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
50return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles];
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
if ([[self.states objectAtIndex:section] count] > 0) {
return [[[UILocalizedIndexedCollation currentCollation] sectionTitles]
objectAtIndex:section];
}
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString
*)title atIndex:(NSInteger)index
{
return [[UILocalizedIndexedCollation currentCollation]
sectionForSectionIndexTitleAtIndex:index];
}
Finally, the data source should implement the UITableViewDataSource methods that are common to all
table views. Listing 4-9 gives examples of these implementations, and illustrates how to use the section and
row properties of the table view–specific category of the NSIndexPath class described in NSIndexPath UIKit
Additions.
Listing 4-9 Populating the rows of an indexed list
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.states count];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [[self.states objectAtIndex:section] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"StateCell";
Creating and Configuring a Table View
Populating an Indexed List
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
51UITableViewCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier]];
}
State *stateObj = [[self.states objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row];
cell.textLabel.text = stateObj.name;
return cell;
}
For table views that are indexed lists, when the data source assigns cells for rows in
tableView:cellForRowAtIndexPath:, it should ensure that the accessoryType property of the cell is
set to UITableViewCellAccessoryNone.
After initially populating the table view following the procedure outlined above, you can reload the contents
of the index by calling the reloadSectionIndexTitles method.
Optional Table View Configurations
The table view API allows you to configure various visual and behavioral aspects of a table view, including
specific rows and sections. The following examples serve to give you some idea of the options available to
you.
Add a Custom Title
In the same block of code that createsthe table view, you can apply global configurations using certain methods
of the UITableView class. The code example in Listing 4-10 adds a custom title for the table view (using a
UILabel object).
Listing 4-10 Adding a title to the table view
- (void)loadView
{
CGRect titleRect = CGRectMake(0, 0, 300, 40);
UILabel *tableTitle = [[UILabel alloc] initWithFrame:titleRect];
Creating and Configuring a Table View
Optional Table View Configurations
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
52tableTitle.textColor = [UIColor blueColor];
tableTitle.backgroundColor = [self.tableView backgroundColor];
tableTitle.opaque = YES;
tableTitle.font = [UIFont boldSystemFontOfSize:18];
tableTitle.text = [curTrail objectForKey:@"Name"];
self.tableView.tableHeaderView = tableTitle;
[self.tableView reloadData];
}
Provide a Section Title
The example in Listing 4-11 returns a title string for a section.
Listing 4-11 Returning a title for a section
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
// Returns section title based on physical state: [solid, liquid, gas,
artificial]
return [[[PeriodicElements sharedPeriodicElements] elementPhysicalStatesArray]
objectAtIndex:section];
}
Indent a Row
The code in Listing 4-12 moves a specific row to the next level of indentation.
Listing 4-12 Custom indentation of a row
- (NSInteger)tableView:(UITableView *)tableView
indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath {
if ( indexPath.section==TRAIL_MAP_SECTION && indexPath.row==0 ) {
return 2;
}
return 1;
}
Creating and Configuring a Table View
Optional Table View Configurations
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
53Vary a Row’s Height
The example in Listing 4-13 varies the height of a specific row based on its index value.
Listing 4-13 Varying row height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath
{
CGFloat result;
switch ([indexPath row])
{
case 0:
{
result = kUIRowHeight;
break;
}
case 1:
{
result = kUIRowLabelHeight;
break;
}
}
return result;
}
Customize Cells
You can also affect the appearance of rows by returning custom UITableViewCell objects with specially
formatted subviews for content in tableView:cellForRowAtIndexPath:. Cell customization is discussed
in “A Closer Look at Table View Cells” (page 55).
Creating and Configuring a Table View
Optional Table View Configurations
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
54A table view uses cell objects to draw its visible rows and then caches those objects as long as the rows are
visible. Cells inherit from the UITableViewCell class. The table view’s data source provides the cell objects
to the table view by implementing the tableView:cellForRowAtIndexPath: method, a required method
of the UITableViewDataSource protocol.
In this chapter, you’ll learn about:
● The characteristics of cells
● How to use the default capabilities of UITableViewCell for setting cell content
● How to create custom UITableViewCell objects
Characteristics of Cell Objects
A cell object has various parts, which can change depending on the mode of the table view. Normally, most
of a cell object is reserved for its content: text, image, or any other kind of distinctive identifier. Figure 5-1
shows the major parts of a cell.
Figure 5-1 Parts of a table view cell
Cell content Accessory view
The smaller area on the rightside of the cell isreserved for accessory views: disclosure indicators, detail disclosure
controls, control objects such as sliders or switches, and custom views.
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
55
A Closer Look at Table View CellsWhen the table view goes into editing mode, the editing control for each cell object (if it’s configured to have
such a control) appears on its left side, in the area shown in Figure 5-2.
Figure 5-2 Parts of a table-view cell in editing mode
Editing control Cell content Reordering control
The editing control can be either a deletion control (a red minus sign inside a circle) or an insertion control (a
green plus sign inside a circle). The cell’s content is pushed toward the right to make room for the editing
control. If the cell object is configured for reordering (that is, relocation within the table view), the reordering
control appearsin the rightside of the cell, next to any accessory view specified for editing mode. The reordering
control is a stack of horizontal lines; to relocate a row within itstable view, users press on the reordering control
and drag the cell.
If a cell object isreusable—the typical case—you assign it a reuse identifier (an arbitrary string) in the storyboard.
At runtime, the table view stores cell objects in an internal queue. When the table view asks the data source to
configure a cell object for display, the data source can access the queued object by sending a
dequeueReusableCellWithIdentifier: message to the table view, passing in a reuse identifier. The data
source sets the content of the cell and any special properties before returning it. This reuse of cell objects is a
performance enhancement because it eliminates the overhead of cell creation.
With multiple cell objects in a queue, each with its own identifier, you can have table views constructed from
cell objects of different types. For example, some rows of a table view can have content based on the image
and text properties of a UITableViewCell in a predefined style, while other rows can be based on a customized
UITableViewCell that defines a special format for its content.
When providing cells for the table view, there are three general approaches you can take. You can use
ready-made cell objects in a range of styles, you can add your own subviews to the cell object’s content view
(which can be done in Interface Builder), or you can use cell objects created from a custom subclass of
UITableViewCell. Note that the content view is a container of other views and so displays no content itself.
A Closer Look at Table View Cells
Characteristics of Cell Objects
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
56Using Cell Objects in Predefined Styles
Using the UITableViewCell class directly, you can create “off-the-shelf” cell objects in a range of predefined
styles. “Standard Styles for Table View Cells” (page 17) describes these standard cells and provides examples
of how they look in a table view. These cells are associated with the following enum constants, declared in
UITableViewCell.h:
typedef enum {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
} UITableViewCellStyle;
These cell objects have two kinds of content: one or more text strings and, in some cases, an image. Figure
5-3 shows the approximate areas for image and text. As an image expands to the right, it pushes the text in
the same direction.
Figure 5-3 Default cell content in a UITableViewCell object
Image Text
Cell content Accessory view
The UITableViewCell class defines three properties for this cell content:
● textLabel—A label for the title (a UILabel object)
● detailTextLabel—A label for the subtitle if there is additional detail (a UILabel object)
● imageView—An image view for an image (a UIImageView object)
Because the first two of these properties are labels, you can set the font, alignment, line-break mode, and color
of the associated text through the properties defined by the UILabel class (including the color of text when
the row is highlighted). For the image view property, you can also set an alternative image for when the cell
is highlighted using the highlightedImage property of the UIImageView class.
A Closer Look at Table View Cells
Using Cell Objects in Predefined Styles
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
57Figure 5-4 gives an example of a table view whose rows are drawn using a UITableViewCell object in the
UITableViewCellStyleSubtitle style; it includes both an image and, for textual content, a title and a
subtitle.
Figure 5-4 A table view with rows showing both images and text
Listing 5-1 showsthe implementation of tableView:cellForRowAtIndexPath: that createsthe table view
rows in Figure 5-4 (page 58). The first thing the data source should do is send
dequeueReusableCellWithIdentifier: to the table view, passing in a reuse identifier. If a prototype for
the cell existsin a storyboard, the table view returns a reusable cell object. Then itsetsthe cell object’s content,
both text and image.
Listing 5-1 Configuring a UITableViewCell object with both image and text
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"MyIdentifier"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:@"MyIdentifier"]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
A Closer Look at Table View Cells
Using Cell Objects in Predefined Styles
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
58NSDictionary *item = (NSDictionary *)[self.content objectAtIndex:indexPath.row];
cell.textLabel.text = [item objectForKey:@"mainTitleKey"];
cell.detailTextLabel.text = [item objectForKey:@"secondaryTitleKey"];
NSString *path = [[NSBundle mainBundle] pathForResource:[item
objectForKey:@"imageKey"] ofType:@"png"];
UIImage *theImage = [UIImage imageWithContentsOfFile:path];
cell.imageView.image = theImage;
return cell;
}
The table view’s data source implementation of tableView:cellForRowAtIndexPath: should always
reset all content when reusing a cell.
When you configure a UITableViewCell object, you can also set various other properties, including (but not
limited to) the following:
● selectionStyle—Controls the appearance of the cell when selected.
● accessoryType and accessoryView—Allow you to set one of the standard accessory views(disclosure
indicator or detail disclosure control) or a custom accessory view for a cell in normal (nonediting) mode.
For a custom view, you may provide any UIView object, such as a slider, a switch, or a custom view.
● editingAccessoryType and editingAccessoryView—Allow you to set one of the standard accessory
views (disclosure indicator or detail disclosure control) or a custom accessory view for a cell in editing
mode. For a custom view, you may provide any UIView object, such as a slider, a switch, or a custom view.
● showsReorderControl—Specifies whether it shows a reordering control when in editing mode. The
related but read-only editingStyle property specifies the type of editing control the cell has (if any).
The delegate returns the value of the editingStyle property in its implementation of the
tableView:editingStyleForRowAtIndexPath: method.
● backgroundView and selectedBackgroundView—Provide a background view (when a cell is unselected
and selected) to display behind all other views of the cell.
● indentationLevel and indentationWidth—Specify the indentation level for cell content and the
width of each indentation level.
Because a table view cell inherits from UIView, you can also affect its appearance and behavior by setting the
properties defined by that superclass. For example, to affect a cell’s background color, you could set its
backgroundColor property. Listing 5-2 shows how you might use the delegate method
tableView:willDisplayCell:forRowAtIndexPath: to alternate the background color of rows(via their
backing cells) in a table view.
A Closer Look at Table View Cells
Using Cell Objects in Predefined Styles
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
59Listing 5-2 Alternating the background color of cells
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row%2 == 0) {
UIColor *altCellColor = [UIColor colorWithWhite:0.7 alpha:0.1];
cell.backgroundColor = altCellColor;
}
}
Listing 5-2 also illustrates an important aspect of the table view API. A table view sends a
tableView:willDisplayCell:forRowAtIndexPath: message to its delegate just before it draws a row.
If the delegate chooses to implement this method, it can make last-minute changes to the cell object before
it is displayed. With this method, the delegate should change only state-based properties that were set earlier
by the table view, such as selection and background color, and not content.
Customizing Cells
The four predefined styles of UITableViewCell objects suffice for most of the rows that table views display.
With these ready-made cell objects, rows can include one or two styles of text, often an image, and an accessory
view of some sort. The application can modify the text in its font, color, and other characteristics, and it can
supply an image for the row in its selected state as well as its normal state.
Asflexible and useful asthis cell content is, it might notsatisfy the requirements of all applications. For example,
the labels permitted by a native UITableViewCell object are pinned to specific locations within a row, and
the image must appear on the left side of the row. If you want the cell to have different content components
and to have these laid out in different locations, or if you want different behavioral characteristics for the cell,
you have two alternatives:
● Add subviews to a cell’s content view.
● Create a custom subclass of UITableViewCell.
The following sections discuss both approaches.
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
60Loading Table View Cells from a Storyboard
In a storyboard, the cells in a table view are dynamic or static. With dynamic content, the table view is a list with
a large (and potentially unbounded) number of rows. With static content, the number of rowsis a finite quantity
that’s known at compile time. A table view that presents a detail view of an item is a good candidate for static
content.
You can design dynamic or static cell content directly inside a table view object. Figure 5-5 shows the master
and detail table viewsin a simple storyboard. In this example, the master table view contains dynamic prototype
cells, and the detail table view contains static cells.
Figure 5-5 Table view cells in a storyboard
The following sections demonstrate how to load data into table views that contain custom-configured cells.
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
61The Technique for Dynamic Row Content
In this section, you compose a custom prototype cell in a storyboard. At runtime, the data source dequeues
cells, prepares them, and gives them to its table view for drawing the rows depicted in Figure 5-6.
Figure 5-6 Table view rows drawn with a custom prototype cell
The data source can use two different ways to access the subviews of the cells. One approach uses the tag
property defined by UIView and the other approach uses outlets. Using tags is convenient, although it makes
the code more fragile because it introduces a coupling between the tag numbers in the storyboard and the
code. Using outlets requires a little more work because you need to define a custom subclass of
UITableViewCell. Both approaches are described here.
To create a project that uses a storyboard to load custom table view cells
1. Create a project using the Master-Detail Application template and select the Use Storyboards option.
2. On the storyboard canvas, select the master view controller.
3. In the Identity inspector, verify that Class is set to the custom MasterViewController class.
4. Select the table view inside the master view controller.
5. In the Attributes inspector, verify that the Content pop-up menu is set to Dynamic Prototypes.
6. Select the prototype cell.
7. In the Attributes inspector, choose Custom in the Style pop-up menu.
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
628. Enter a reuse identifier in the Identifier text field.
This is the same reuse identifier you send to the table view in the
dequeueReusableCellWithIdentifier: message. For an example, see Listing 5-3.
9. Choose Disclosure Indicator in the Accessory pop-up menu.
10. Drag objects from the Library onto the cell.
For this example, drag two label objects and position them near the ends of the cell (leaving room for
the accessory view).
11. Select the objects and set their attributes, sizes, and autoresizing characteristics.
An important attribute to set for the programmatic portion of this procedure is each object’s tag
property. Find this property in the View section of the Attributes inspector and assign each object a
unique integer.
Now write the code you would normally write to obtain the table view’s data. (For this example, the only data
you need is the row number of each cell.) Implement the data source method
tableView:cellForRowAtIndexPath: to create a new cell from the prototype and populate it with data,
in a manner similar to Listing 5-3.
Listing 5-3 Adding data to a cell using tags
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"MyIdentifier"];
UILabel *label;
label = (UILabel *)[cell viewWithTag:1];
label.text = [NSString stringWithFormat:@"%d", indexPath.row];
label = (UILabel *)[cell viewWithTag:2];
label.text = [NSString stringWithFormat:@"%d", NUMBER_OF_ROWS - indexPath.row];
return cell;
}
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
63There are a few aspects of this code to note:
● The string identifier you assign to the prototype cell is the same string you pass to the table view in
dequeueReusableCellWithIdentifier:.
● Because the prototype cell is defined in a storyboard, the dequeueReusableCellWithIdentifier:
method always returns a valid cell. You don’t need to check the return value against nil and create a cell
manually.
● The code gets the labels in the cell by calling viewWithTag:, passing in their tag integers. It can then set
the textual content of the labels.
If you prefer not to use tags, you can use an alternative method for setting the content in the cell. Define a
custom UITableViewCell subclass with outlet properties for the objects you want to set. In the storyboard,
associate the new class with the prototype cell and connect the outlets to the corresponding objects in the
cell.
To use outlets for the custom cell content
1. Add an Objective-C class named MyTableViewCell to your project.
2. Add the following code to the interface in MyTableViewCell.h:
@interface MyTableViewCell : UITableViewCell
@property (nonatomic, weak) IBOutlet UILabel *firstLabel;
@property (nonatomic, weak) IBOutlet UILabel *secondLabel;
@end
3. Add the following code to the implementation in MyTableViewCell.m:
@synthesize firstLabel, secondLabel;
4. Add the following line of code to the source file that implements the data source:
#import "MyTableViewCell.h"
5. Use the Identity inspector to set the Class of the prototype cell to MyTableViewCell.
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
646. Use the Connections inspector to connect the two outlets in the prototype cell to their corresponding
labels.
7. Implement the data source method tableView:cellForRowAtIndexPath: in a manner similar to
Listing 5-4.
Listing 5-4 Adding data to a cell using outlets
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"MyIdentifier"];
cell.firstLabel.text = [NSString stringWithFormat:@"%d", indexPath.row];
cell.secondLabel.text = [NSString stringWithFormat:@"%d", NUMBER_OF_ROWS -
indexPath.row];
return cell;
}
The code gains access to the labels in the cell using accessor methods (dot notation is used here). The code
can then set the textual content of the labels.
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
65The Technique for Static Row Content
In this section, you compose several cells in a table view with static content. At runtime, when the table view
is loaded from the storyboard, the table view controller has immediate access to these cells and composes the
sections and rows of the table view with them, as depicted in Figure 5-7.
Figure 5-7 Table view rows drawn with multiple cells
As with the procedure for dynamic content, start by adding a subclass of UITableViewController to your
project. Define outlet properties for the master row label in the first cell and the slider value label in the last
cell, as shown in Listing 5-5.
Listing 5-5 Defining outlet properties for static cell objects
@interface DetailViewController : UITableViewController
@property (strong, nonatomic) id detailItem;
@property (weak, nonatomic) IBOutlet UILabel *masterRowLabel;
@property (weak, nonatomic) IBOutlet UILabel *sliderValueLabel;
@property (weak, nonatomic) IBOutlet UISlider *slider;
- (IBAction)logHello;
- (IBAction)sliderValueChanged:(UISlider *)slider;
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
66@end
In the storyboard, drag a Table View Controller object from the Library onto the canvas. Select the table view
and set the following attributes in the Attributes inspector:
1. Set the Content pop-up menu to Static Cells.
2. Set the number of sections to 2.
3. Set the Style pop-up menu to Grouped.
For each section in the table view, use the Attributes inspector to enter a string in the Header field. Then for
the cells, complete the following steps:
1. Delete two of the three cells in the first table-view section and one cell in the second section.
2. Increase the height of each remaining cell as needed.
It isn’t necessary to assign reuse identifiers tof these cells, because you’re not going to implement the data
source method tableView:cellForRowAtIndexPath:.
3. Drag objects from the Library to compose the subviews of each cell as depicted in Figure 5-7 (page 66).
4. Set any desired attributes of these objects.
The slider in this example has a range of values from 0 to 10 with an initial value of 7.5.
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
67Select the table view controller and display the Connections inspector. Make connections between the three
outlets in your table view controller and the corresponding objects, as shown in Figure 5-8. While you’re at it,
implement the two action methods declared in Listing 5-5 (page 66) and make target-action connections to
the button and the slider.
Figure 5-8 Making connections to your static cell content
To populate the data in the static cells, implement a method called configureView in the detail view controller.
In this example, detailItem is an NSString object passed in by the master view controller in its
prepareForSegue:sender: method. The string contains the master row number.
Listing 5-6 Setting the data in the user interface
- (void)configureView
{
if (self.detailItem) {
self.masterRowLabel.text = [self.detailItem description];
}
self.sliderValueLabel.text = [NSString stringWithFormat:@"%1.1f",
self.slider.value];
}
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
68The detail view controller calls the configureView method in viewDidLoad and setDetailItem:, as
illustrated in the Xcode template Master-Detail Application.
Programmatically Adding Subviews to a Cell’s Content View
A cell that a table view uses for displaying a row is a view (UITableViewCell inherits from UIView). As a
view, a cell has a content view—a superview for cell content—that it exposes as a property. To customize the
appearance of rows in a table view, add subviews to the cell’s content view, which is accessible through its
contentView property, and lay them out in the desired locations in their superview’s coordinates. You can
configure and lay them out programmatically or in Interface Builder. (The approach using Interface Builder is
discussed in “Loading Table View Cells from a Storyboard” (page 61).)
One advantage of this approach is its relative simplicity; it doesn’t require you to create a custom subclass of
UITableViewCell and handle all of the implementation details required for custom views. However, if you
do take this approach, avoid making the views transparent, if you can. Transparent subviews affect scrolling
performance because of the increased compositing cost. Subviews should be opaque, and typically should
have the same background color as the cell. And if the cell is selectable, make sure that the cell content is
highlighted appropriately when selected. The content is selected automatically if the subview implements (if
appropriate) the accessor methods for the highlighted property.
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
69Suppose you want a cell with text and image content in custom locations. For example, you want the image
on the right side of the cell and the title and subtitle of the cell right-aligned against the left side of the image.
Figure 5-9 show how a table view with rows drawn with such a cell might look. (This example is for illustration
only, and is not intended as a human-interface model.)
Figure 5-9 Cells with custom content as subviews
The code example in Listing 5-7 illustrates how the data source programmatically composes the cell with which
thistable view drawsitsrows. In tableView:cellForRowAtIndexPath:, it first checksto see the table view
already has a cell object with the given reuse identifier. If there is no such object, the data source creates two
label objects and one image view with specific frames within the coordinate system of their superview (the
content view). It also sets attributes of these objects. Having acquired an appropriate cell to use, the data
source sets the cell’s content before returning the cell.
Listing 5-7 Adding subviews to a cell’s content view
#define MAINLABEL_TAG 1
#define SECONDLABEL_TAG 2
#define PHOTO_TAG 3
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
70static NSString *CellIdentifier = @"ImageOnRightCell";
UILabel *mainLabel, *secondLabel;
UIImageView *photo;
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier]];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
mainLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 220.0,
15.0)]];
mainLabel.tag = MAINLABEL_TAG;
mainLabel.font = [UIFont systemFontOfSize:14.0];
mainLabel.textAlignment = UITextAlignmentRight;
mainLabel.textColor = [UIColor blackColor];
mainLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:mainLabel];
secondLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0, 20.0, 220.0,
25.0)]];
secondLabel.tag = SECONDLABEL_TAG;
secondLabel.font = [UIFont systemFontOfSize:12.0];
secondLabel.textAlignment = UITextAlignmentRight;
secondLabel.textColor = [UIColor darkGrayColor];
secondLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:secondLabel];
photo = [[[UIImageView alloc] initWithFrame:CGRectMake(225.0, 0.0, 80.0,
45.0)]];
photo.tag = PHOTO_TAG;
photo.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:photo];
} else {
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
71mainLabel = (UILabel *)[cell.contentView viewWithTag:MAINLABEL_TAG];
secondLabel = (UILabel *)[cell.contentView viewWithTag:SECONDLABEL_TAG];
photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
}
NSDictionary *aDict = [self.list objectAtIndex:indexPath.row];
mainLabel.text = [aDict objectForKey:@"mainTitleKey"];
secondLabel.text = [aDict objectForKey:@"secondaryTitleKey"];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:[aDict
objectForKey:@"imageKey"] ofType:@"png"];
UIImage *theImage = [UIImage imageWithContentsOfFile:imagePath];
photo.image = theImage;
return cell;
}
When the data source creates the cells, it assigns each subview an identifier called a tag. With tags, you can
locate a view in its view hierarchy by calling the viewWithTag: method. If the delegate later getsthe designated
cell from the table view’s queue, it uses the tags to obtain references to the three subviews prior to assigning
them content.
This code creates a UITableViewCell object in the predefined default style
(UITableViewCellStyleDefault). Because the content properties of the standard cells—textLabel,
detailTextLabel, and imageView—are nil until assigned content, you may use any predefined cell as
the template for customization.
Note: Another approach is to subclass UITableViewCell and create instances in the
UITableViewCellStyleSubtitle style. Then override the layoutSubviewsmethod to reposition
the textLabel, detailTextLabel, and imageView subviews (after calling super).
One way to achieve “attributed string” effects with textual content is to lay out UILabel subviews of the
UITableViewCell content view. The text of each label can have its own font, color,size, alignment, and other
characteristics. If you want that kind of variation within a label object, create multiple labels and lay them out
relative to each other.
A Closer Look at Table View Cells
Customizing Cells
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
72Cells and Table View Performance
The proper use of table view cells, whether off-the-shelf or custom cell objects, is a major factor in the
performance of table views. Ensure that your application does the following three things:
● Reuse cells. Object allocation has a performance cost, especially if the allocation hasto happen repeatedly
over a short period—say, when the user scrolls a table view. If you reuse cells instead of allocating new
ones, you greatly enhance table view performance.
● Avoid relayout of content. When reusing cells with custom subviews, refrain from laying out those
subviews each time the table view requests a cell. Lay out the subviews once, when the cell is created.
● Use opaque subviews. When customizing table view cells, make the subviews of the cell opaque, not
transparent.
A Closer Look at Table View Cells
Cells and Table View Performance
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
73When users tap a row of a table view, usually something happens as a result. Another table view could slide
into place, the row could display a checkmark, orsome other action could be performed. The following sections
describe how to respond to selections and how to make selections programmatically.
Selections in Table Views
There are a few human-interface guidelines to keep in mind when dealing with cell selection in table views:
● You should never use selection to indicate state. Instead, use check marks and accessory viewsforshowing
state.
● When the user selects a cell, you should respond by deselecting the previously selected cell (by calling
the deselectRowAtIndexPath:animated: method) as well as by performing any appropriate action,
such as displaying a detail view.
●
If you respond to the the selection of a cell by pushing a new view controller onto the navigation controller’s
stack, you should deselect the cell (with animation) when the view controller is popped off the stack.
You can control whether rows are selectable when the table view is in editing mode by setting the
allowsSelectionDuringEditing property of UITableView. In addition, beginning with iOS 3.0, you can
control whether cells are selectable when editing mode is not in effect by setting the allowsSelection
property.
Responding to Selections
Users tap a row in a table view either to signal to the application that they want to know more about what
that row signifies or to select what the row represents. In response to the user tapping a row, an application
could do any of the following:
● Show the next level in a data-model hierarchy.
● Show a detail view of an item (that is, a leaf node of the data-model hierarchy).
● Show a checkmark in the row to indicate that the represented item is selected.
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
74
Managing Selections●
If the touch occurred in a control embedded in the row, it could respond to the action message sent by
the control.
To handle most selections of rows, the table view’s delegate must implement the
tableView:didSelectRowAtIndexPath: method. In sample method implementation shown in Listing
6-1, the delegate first deselectsthe selected row. Then it allocates and initializes an instance of the next table-view
controller in the sequence. Itsetsthe data this view controller needsto populate itstable view and then pushes
this object onto the stack maintained by the application’s UINavigationController object.
Listing 6-1 Responding to a row selection
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
BATTrailsViewController *trailsController = [[BATTrailsViewController alloc]
initWithStyle:UITableViewStylePlain];
trailsController.selectedRegion = [regions objectAtIndex:indexPath.row];
[[self navigationController] pushViewController:trailsController animated:YES];
[trailsController release];
}
If a row has a disclosure control—the white chevron over a blue circle—for an accessory view, clicking the
control resultsin the delegate receiving a tableView:accessoryButtonTappedForRowWithIndexPath:
message (instead of tableView:didSelectRowAtIndexPath:). The delegate responds to this message in
the same general way as it does for other kinds of selections.
A row can also have a control object as its accessory view, such as a switch or a slider. This control object
functions as it would in any other context: Manipulating the object in the proper way results in an action
message being sent to a target object. Listing 6-2 illustrates a data source object that adds a UISwitch object
as a cell’s accessory view and then responds to the action messages sent when the switch is “flipped.”
Listing 6-2 Setting a switch object as an accessory view and responding to its action message
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath
*)indexPath {
UITableViewCell *cell = [tv
dequeueReusableCellWithIdentifier:@"CellWithSwitch"];
if (cell == nil) {
Managing Selections
Responding to Selections
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
75cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CellWithSwitch"]
autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.font = [UIFont systemFontOfSize:14];
}
UISwitch *switchObj = [[UISwitch alloc] initWithFrame:CGRectMake(1.0, 1.0,
20.0, 20.0)];
switchObj.on = YES;
[switchObj addTarget:self action:@selector(toggleSoundEffects:)
forControlEvents:(UIControlEventValueChanged | UIControlEventTouchDragInside)];
cell.accessoryView = switchObj;
[switchObj release];
cell.textLabel.text = @"Sound Effects";
return cell;
}
- (void)toggleSoundEffects:(id)sender {
[self.soundEffectsOn = [(UISwitch *)sender isOn];
[self reset];
}
You may also define controls as accessory views of table-view cells created in Interface Builder. Drag a control
object (switch, slider, and so on) into a nib document window containing a table-view cell. Then, using the
connection window, make the control the accessory view of the cell. “Loading Table View Cells from a
Storyboard” (page 61) describes the procedure for creating and configuring table-view cell objects in nib files.
Selection management is also important with selection lists. There are two kinds of selection lists:
● Exclusive lists where only one row is permitted the checkmark
●
Inclusive lists where more than one row can have a checkmark
Listing 6-3 illustrates one approach to managing an exclusive selection list. It first deselects the currently
selected row and returns if the same row is selected; otherwise it sets the checkmark accessory type on the
newly selected row and removes the checkmark on the previously selected row
Managing Selections
Responding to Selections
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
76Listing 6-3 Managing a selection list—exclusive list
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSInteger catIndex = [taskCategories indexOfObject:self.currentCategory];
if (catIndex == indexPath.row) {
return;
}
NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:catIndex inSection:0];
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
if (newCell.accessoryType == UITableViewCellAccessoryNone) {
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
self.currentCategory = [taskCategories objectAtIndex:indexPath.row];
}
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:oldIndexPath];
if (oldCell.accessoryType == UITableViewCellAccessoryCheckmark) {
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
}
Listing 6-4 illustrates how to manage a inclusive selection list. As the comments in this example indicate, when
the delegate adds a checkmark to a row or removes one, it typically also sets or unsets any associated
model-object attribute.
Listing 6-4 Managing a selection list—inclusive list
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
[theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow]
animated:NO];
UITableViewCell *cell = [theTableView cellForRowAtIndexPath:newIndexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
Managing Selections
Responding to Selections
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
77// Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
// Reflect deselection in data model
}
}
In tableView:didSelectRowAtIndexPath: you should always deselect the currently selected row.
Programmatically Selecting and Scrolling
Occasionally the selection of a row originates within the application itself rather than from a tap in a table
view. There could be an externally induced change in the data model. For example, the user adds a new person
to an address book and then returnsto the list of contacts; the application wantsto scroll thislist to the recently
added person. For situations like these, you can use the UITableView methods
selectRowAtIndexPath:animated:scrollPosition: and (if the row is already selected)
scrollToNearestSelectedRowAtScrollPosition:animated:. You may also call
scrollToRowAtIndexPath:atScrollPosition:animated: if you want to scroll to a specific row without
selecting it.
The code in Listing 6-5 (somewhat whimsically) programmatically selects and scrolls to a row 20 rows away
from the just-selected row using the selectRowAtIndexPath:animated:scrollPosition: method.
Listing 6-5 Programmatically selecting a row
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)newIndexPath {
NSIndexPath *scrollIndexPath;
if (newIndexPath.row + 20 < [timeZoneNames count]) {
scrollIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row+20
inSection:newIndexPath.section];
} else {
scrollIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row-20
inSection:newIndexPath.section];
}
[theTableView selectRowAtIndexPath:scrollIndexPath animated:YES
scrollPosition:UITableViewScrollPositionMiddle];
Managing Selections
Programmatically Selecting and Scrolling
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
78}
Managing Selections
Programmatically Selecting and Scrolling
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
79A table view has an editing mode as well as its normal (selection) mode. When a table view goes into editing
mode, it displays the editing and reordering controls associated with its rows. The editing controls, which are
in the left side of the row, allow the user to insert and delete rows in the table view. The editing controls have
distinctive appearances:
Deletion control
Insertion control
When a table view enters editing mode and when users click an editing control, the table view sends a series
of messages to its data source and delegate, but only if they implement these methods. These methods allow
the data source and delegate to refine the appearance and behavior of rows in the table view; the messages
also enable them to carry out the deletion or insertion operation.
Even if a table view is not in editing mode, you can insert or delete a number of rows or sections as a group
and have those operations animated.
The first section below shows you how, when a table is in editing mode, to insert new rows and delete existing
rows in a table view in response to user actions. The second section, “Batch Insertion, Deletion, and Reloading
of Rows and Sections” (page 87), discusses how you can insert and delete multiple sections and rows animated
as a group.
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
80
Inserting and Deleting Rows and SectionsNote: The procedure for reordering rows when in editing mode is described in “Managing the
Reordering of Rows” (page 91).
Inserting and Deleting Rows in Editing Mode
When a Table View is Edited
A table view goes into editing mode when it receives a setEditing:animated: message. Typically (but not
necessarily) the message originates as an action message sent when the user taps an Edit button in the
navigation bar. In editing mode, a table view displays any editing (and reordering) controls that its delegate
has assigned to each row. The delegate assigns the controls as a result of returning the editing style for a row
in the tableView:editingStyleForRowAtIndexPath: method.
Inserting and Deleting Rows and Sections
Inserting and Deleting Rows in Editing Mode
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
81Note: If a UIViewController object is managing the table view, it automatically receives a
setEditing:animated: message when the Edit button is tapped. In its implementation of this
message, it can update button state or do any other kind of task before invoking the table view’s
version of the method.
When the table view receives setEditing:animated:, itsendsthe same message to the UITableViewCell
object for each visible row. Then it sends a succession of messages to its data source and its delegate (if they
implement the methods) as depicted in the diagram in Figure 7-1.
Figure 7-1 Calling sequence for inserting or deleting rows in a table view
tableView:
editingStyleForRowAtIndexPath:
tableView:
canEditRowAtIndexPath:
Client Delegate
Data Source
Table View
User presses Edit button
User presses editing control
User presses Delete button
setEditing:YES animated:YES
tableView:commitEditingStyle:
forRowAtIndexPath:
deleteRowsAtIndexPath:
withRowAnimation
or
insertRowsAtIndexPath:
withRowAnimation:
After resending setEditing:animated: to the cells corresponding to the visible rows, the sequence of
messages is as follows:
1. The table view invokesthe tableView:canEditRowAtIndexPath: method if its data source implements
it. This method allows the application to exclude rows in the table view from being edited even when
their cell’s editingStyle property indicates otherwise. Most applications do not need to implement this
method.
2. The table view invokes the tableView:editingStyleForRowAtIndexPath: method if its delegate
implements it. This method allows the application to specify a row’s editing style and thus the editing
control that the row displays.
Inserting and Deleting Rows and Sections
Inserting and Deleting Rows in Editing Mode
2012-09-19 | © 2012 Apple Inc. All Rights Reserved.
82At this point, the table view is fully in editing mode. It displays the insertion or deletion control for each
eligible row.
3. The user taps an editing control (either the deletion control or the insertion control). If he or she taps a
deletion control, a Delete button is displayed on the row. The user then taps that button to confirm the
deletion.
4. The table view sends the tableView:commitEditingStyle:forRowAtIndexPath: message to the
data source. Although this protocol method is marked as optional, the data source must implement it if it
wants to insert or delete a row. It must do two things:
● Send deleteRowsAtIndexPaths:withRowAnimation: or
insertRowsAtIndexPaths:withRowAnimation: to the table view to direct it to adjust its
presentation.
● Update the corresponding data-model array by either deleting the referenced item from the array or
adding an item to the array.
When the user swipes across a row to display the Delete button for that row, there is a variation in the calling
sequence diagrammed in Figure 7-1 (page 82). When the user swipes a row to delete it, the table view first
checks to see if its data source has implemented the
tableView:commitEditingStyle:forRowAtIndexPath: method; if that is so, it sends
setEditing:animated: to itself and enters editing mode. In this “swipe to delete” mode, the table view
does not display the editing and reordering controls. Because this is a user-driven event, it also brackets the
messagesto the delegate inside of two other messages: tableView:willBeginEditingRowAtIndexPath:
and tableView:didEndEditingRowAtIndexPath:. By implementing these methods,