0

Айфон 5 обновление

Из этой главы, да и из всей этой книги понятно, что самые лакомые куски программирования под iOS включены в публичные фреймворки, но не в SDK. Неофициальная политика Apple насчет этого проста: вы можете всё это использовать, но только на свой страх и риск. Ваш код может сломаться при следующем обновлении прошивки. Вам самим придётся искать компромисс между риском и прибылью.
Erica Sadun, The iPhone Developer’s CookBook
ОригиналAs you’ve seen in this chapter, and throughout this book, some of the nicest bits of iPhone programming are included in the public iPhone frameworks but not in the SDK. Apple’s unofficial policy on this is clear:You can use these items in your programs, but you do so at your own risk.Your code may break at each firmware release. Striking the balance between risk and reward is up to you.

Дисклеймеры

  • Приведенные здесь куски кода работают на обычных iPhone (включая 4S) и iPad (включая new iPad) и не требуют jailbreak.
  • Все решения написаны и протестированы на iOS 5. Все решения также протестированы на совместимость с iOS 4.3, т.е. работают с iOS 4, если не сказано обратное. Основная часть статьи была написана до выхода iOS 6, так что приведенные решения не тестировались на совместимость с iOS 6.
  • Использование недокументированных API может привести к тому, что ваше приложение не допустят в AppStore. А может и не привести 🙂
    Для тех, кому интересно, как Apple опрделяет использование приваетных API:»Как Apple узнаёт, что ты используешь приватные API?»
  • Apple может изменить реализацию вместе со следующим релизом iOS, и в вашем коде что-то сломается. Впрочем, это решаемо, и ничем принципиально не отличается от реализации обратной совместимости для документированных API. Ниже я рассмотрел эту проблему чуть подробнее.
  • Я не могу гарантировать, что у найденных мной API нет побочных эффектов. Используйте на свой страх и риск.
  • Лицензионное соглашение Apple Developer Program запрещает реверс-инжинеринг iOS.
  • Статья в процессе доработки. Конструктивная критика приветствуется!

Краткая инструкция по поиску в SDK

Допустим, вам нужно сделать что-то, выходящее за рамки официальной документации. Например, изменить уровень подсветки экрана (до iOS 5 этого не было в документированной части SDK). Известно, что программисты Apple как правило дают функциям и переменным осмысленные и выразительные названия, этим мы и воспользуемся для поиска в SDK. Для этого выберем несколько слов, относящихся к теме, например, brightness, level, screen. Запустим скрипт LookSDKForSymbol.sh (это моя обертка над nm; об этом скрипте и других используемых инструментах написано далее, в разделе «Инструменты») с ключевыми словами в качестве параметров. Скрипт выдаёт найденные в объектном файле символы (т.е. названия классов, функций, переменных). Пример выдачи:
$ LookSDKForSymbol.sh light level
U _UIBacklightLevelChangedNotification
Found in ./System/Library/CoreServices/SpringBoard.app/SpringBoard
001b43c4 t —
001b4360 t —
0025ce54 t —
0025ce40 t —
… и ещё несколько десятков символов
Большую часть результатов можно сразу отбросить, например — возвращает значение подсветки, а не устанавливает его.
Оставшиеся, если их не более нескольких десятков, можно попытаться скормить гуглу. Случается, что кто-то уже занимался исследованием API, связанных с найденными символами, и в этом случае задача, считай, решена. В более сложных случаях приходится заниматься реверс-инжинерингом, то есть выяснять, как работают найденные функции, как использовать найденных оповещения и тому подобное.
Символьные строки, выдаваемые утилитой, делятся на следующие категории:
Дальнейшие действия зависят от категории символа.

  1. Генерируем заголовочный файл для данного фреймворка:
    class-dump-z Foundation > $/iOS_private_headers/Foundation.h
    В большинстве случаев, сгенерированного заголовчного файла достаточно: в нем должны быть довольно хорошо описаны иерархии наследования классов, структуры, методы и т.д, чтобы потратив немного времени можно разобраться с API и использовать его в своём предложении.
    К сожалению, иногда информации содержащейся в заголовочном файле недостаточно, чтобы заставить код работать, и тогда приходится анализировать ассемблерный код, сгенерированный otool.
    Hint по дизассемблированию Objective-C кода: почти наверняка вы столкнетесь с вызовами функций типа objc_msgSend (отправка сообщения объекту). В качестве первого параметра всегда идет указатель на объект, а вторым — указатель на селектор (selector), т.е. указатель на строку, являющуюся названием метода (остальные «обычные» аргументы идут третьим, четвертым и т.д. аргументами). Определить, что за сообщение отправляется в данном случае, поможет hexdump.
  2. Про это можно сразу забыть. Блоки (обычно) локальны, их нельзя вызвать из своего кода.
  3. Самый сложный вариант. В самых простых случаях можно подобрать сигнатуру для функции, в остальных — только дизассемблирование. Больше об этом можно узнать в разделе «Как узнать сигнатуру неизвестной функции?».
  4. Начнем с того, что попытаемся отловить оповещения в одном из трёх основных центров оповещений (это Local, Darwin и CoreTelephony). Если оповещения такого типа не приходят, дело может быть в одной из двух вещей:
    — Оповещения такого типа приходитя в отдельный, специальный центр оповещений. Cледует поискать следы такого центра оповещений в том же фреймворке, к каторому принадлежит найденное оповещение.
    — Доставка оповещений отключена. Попытаться найти механизм включения доставки оповещений такого типа.
  5. В этом случае, скорее всего существует либо функция, которая принимает данную константу в виде параметра, либо словарь, в котором данная константа является ключом. В любом случае, следует искать функцию или метод, название которых начинаются с того же слова (например: константа kLockdownDeviceColorKey -> функция lockdown_copy_value(…);

Как узнать сигнатуру неизвестной функции?

1. Найти в интернете, как это не банально. Мне довольно часто попадались китайский сайты, были корейский и японский сайты с очень полезной информацией. Обычно самого кода уже достаточно, чтобы понять что происходит и как используется данная функция, данный класс и т.д. Спасибо многословности и выразительности Objective-C!
2. Для многих простых функций, можно попытаться угадать сигнатуру. Внимание, это может быть довольно опасно.
Использование некоторые простые функции, таких как GSEventSetBackLightLevel, самоочевидно.
void GSEventSetBackLightLevel(float level);
Для многих других я использовал следующий трюк (на примере функции SBGetRingerSwitchState):
SInt32 ret = 5, out1 = 1, out2 = 2, out3 = 3, out4 = 4; void *libHandle = dlopen(SPRINGBOARD_SERVICES_PATH, RTLD_LAZY); SInt32 (*SBGetRingerSwitchState)(SInt32*,SInt32*,SInt32*,SInt32*) = dlsym(libHandle, «SBGetRingerSwitchState»); ret = SBGetRingerSwitchState(&out1, &out2, &out3, &out4); NSLog(@»%x %x %x %x %x», ret, out1, out2, out3, out4);
В результате работы этого кода выяснилось, что
1) функция возвращала значение 0x10000003, не зависящее от реального положения переключателя.
2) Переменная out2 изменила свое значение на self. Возвращаемое значение также не зависит от переключателя.
3) Остальные переменные не изменили свое значение.
Из 1) я сделал вывод функция возвращает значене типа kern_return_t, так как 0x10000003 соответствует системной ошибке MACH_SEND_INVALID_DEST. По видимому, ошибка указывала на неправильный порт . Как правило, если в вызове функции используется номер порта, то он идет первым аргументом. Из 2) следует, что через второй аргумент функция возвращает некое значение по ссылке.
В результате этих нехитрых действий получается следующая сигнатура:
kern_return_t SBGetRingerSwitchState(mach_port_t port, SInt32 *state);
Кстати, если в названии функции присутствует слово get, то согласно naming conventions Objective-C эта функция должна возвращать значение по ссылке. Это также видно из приведенного примера.
3. Дизассемблирование. На примере все той же SBGetRingerSwitchState. Используем otool:
$ otool -p _SBGetRingerSwitchState -tV -arch armv6 SpringBoardServices | less
000038cc b5f0 push {r4, r5, r6, r7, lr}
000038ce af03 add r7, sp, #12
000038d0 b092 sub sp, #72
000038d2 aa06 add r2, sp, #24 // значение регистра r2 затирается
000038d4 9205 str r2,
000038d6 ac08 add r4, sp, #32 // … как и регистра r4
000038d8 ab0f add r3, sp, #60 // … и r3
000038da 9304 str r3,
000038dc 9103 str r1, // значение r1 сохраняется в стеке
000038de 4925 ldr r1, (0x3974)
000038e0 6011 str r1,
000038e2 6020 str r0, // значение r0 также сохраняется в стеке

Из этого кода, используя даже поверхностные знания arm-ассемблера, можно предположить, что функция принимает два аргумента типа «слово» (word)
Выходит, что у функции два аргумента. Идем дальше, в самый конец.

00003964 9e04 ldr r6,
00003966 6836 ldr r6,
00003968 9903 ldr r1,
0000396a 600e str r6,
// примерно соответствует (в терминах языка си): *r1 = r6; т.е. по адресу, хранящемуся в r1 записывается значение из r6;

// Это значит, что функция возвращает значение по ссылке
0000396c 462e mov r6, r5
0000396e 4630 mov r0, r6
// результат выполнения функции помещается в r0
00003970 b012 add sp, #72
00003972 bdf0 pop {r4, r5, r6, r7, pc}

В сухом остатке получаем:
int SBGetRingerSwitchState(int arg1, int* arg2);
Продолжая анализировать этот асcемблерный код, уточняем типы и приходим к окончательному варианту:
kern_return_t SBGetRingerSwitchState(mach_port_t port, SInt32 *state);

Разные прошивки и разные устройства: что может сломаться и как это исправить?

Понятно, что недокументированные API вовсе не обязательно работают на всех устройствах одинаково. По моему опыту, чаще всего ничего не меняется, и API работает одинаково на всех устройствах и всех прошивках. Так, например, все функции расширения UIDevice-IOKitExtensions (кроме определения IMEI) работают одинаково хорошо на всех устройствах и всех прошивках. Какие изменения могут произойти при обновлении iOS?
Вот несколько практических вариантов.

  • Может появиться официально документированный программный интерфейс, при этом недокументированный интерфейс, как правило, продолжает работать. Пример:
    void GSEventSetBacklightLevel(float level); // работает во всех версиях iOS -; // появилось в iOS 5.0
  • Программные интерфейсы переносятся в другой фреймворк. Apple может объединить несколько фреймворков в один, переименовать или удалить фреймворк. Например, функции для работы с WiFi (Apple80211Open, Apple80211Close и так далее) были перенесены из Aeropuerto.dylib в IPConfiguration.dylib.
  • API могут просто удалить, особенно если она связана с уязвимостью.

Для того чтобы избежать проблем совместимости, соблюдайте простые правила: проверяйте наличие функций (например, с помощью -), классов (NSClassFromString(@»SomeClass») вернет nil в случае отсутствия класса SomeClass) и т.д., а также заранее подумайте, что должна делать программа в случае, если API отсутствует. При использовании динамической линковки библотек следует также всегда проверять возвращаемые значения dlsym(…) и dlopen(…) на равенство NULL.

Примеры
Пример 1:

Определение положения бокового переключателя вибро (a.k.a. Ring/Silent switch, Mute switch)
Одной из задач, которые стояли передо мной, было определение положения бокового переключателя, который в оригинале называется ring/silent switch. Этот переключатель используется для переключения между «тихим» и обычном/»громким» режимами в айфоне и айпаде. Поиск по StackOverflow дал решение:
#import <AudioToolbox/AudioToolbox.h> … /* Возвращаемое значение: 0: тихий режим 1: обычный режим */ int switchState() { // … // Инициализируем и активируем аудиосессию, устанавливаем // категорию в значение kAudioSessionCategoryAmbient // … UInt32 size = sizeof(CFStringRef)); CFStringRef route; AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &route); // Получаем описание текущего аудиовыхода CFIndex len = CFStringGetLength(route); return (len > 0); // Если результат — пустая строка, значит телефон находится в «тихом» режиме }

Которое, впрочем, не работает в iOS 5. Не сработало и использование более нового API (kAudioSessionProperty_AudioRouteDescription) которое дает расширенную информацию об аудиовходах и -выходах. (AUDIOROUTE)
Мои дальнейшие поиски по StackOverflow вывели меня на этот пост. В нем описывается библиотечная функция AudioServicesAddSystemSoundCompletion(), чьё нестандартное поведение рассматривалось разработчиками как баг.
#import <AudioToolbox/AudioToolbox.h> … void playSound() { AudioServicesAddSystemSoundCompletion(MySoundId, NULL, NULL, MyAudioServicesSystemSoundCompletionProc, AudioServicesPlaySystemSound(MySoundId); } void MyAudioServicesSystemSoundCompletionProc (SystemSoundID ssID, void *clientData) { // Проигрывание звука завершено NSLog(@»Playback has been finished»); }
Нестандартное поведение заключается в том, что вызов колбэка MyAudioServicesSystemSoundCompletionProc состоится в конце проигрывания звука в обычном режиме, но сразу после вызова AudioServicesPlaySystemSound в «тихом» режиме. Это создает лазейку для определения текущего состояния переключателя. Если, например, длина аудиофайла что мы проигрываем равна 1 с, то разница во времени вызова MyAudioServicesSystemSoundCompletionProc() в «тихом» и громком режиме составляет 1 c. На этом я построил свое второе, асинхронное решение для определения положения бокового переключателя. Вот оно:
#import <AudioToolbox/AudioToolbox.h> #import «MuteSwitchTet.h» … enum MuteSwitchStates { kMuteSwitchUndefined = -1, kSoundless = 0, kSound = 1 }; @implementation MuteSwitchTest … void MyAudioServicesSystemSoundCompletionProc (SystemSoundID ssID, void *clientData) { // «тихий» режим MuteSwitchTest *self = (MuteSwitchTest*)clientData; ; self.muteSwitchState = kSoundless; } — (void) cancelSystemSoundCompletion { // «громкий» режим AudioServicesRemoveSystemSoundCompletion(SoundID); self.muteSwitchState = kSound; } — (void) startPlayback { AudioServicesAddSystemSoundCompletion(SoundID, NULL, NULL, MyAudioServicesSystemSoundCompletionProc, self); AudioServicesPlaySystemSound(SoundID); ; } … @end
Хотя это новое решение и было рабочим, оно не устраивало меня по нескольким причинам. Во-первых, оно было асинхронным и работало с ощутимой задержкой (около 1/10 секунды). Снижение задержки вело к ложным срабатываниям. Во-вторых, был побочный эффект — сам проигрываемый звук, который звучал достаточно громко чтобы смутить пользователя. Позже я искусственно выкрутил громкость в ноль в аудиоредакторе. В-третьих, это был уже слишком похоже на грязный хак, хотя это, например, не помешало создателям VSSilentSwitch продавать свое решение, по всей видимости основанное на том же эффекте.
Примерно через месяц я вернулся к этой проблеме. Я начал использовать команду nm для поиска символов в объектных файлах, на её основе я написал простейший shell-скрипт, листинг которого можно найти ниже (В разделе «Инструменты»). Скрипт запускается с одним, двумя или тремя параметрами, каждый из которых представляет ключевое слово.
$ sh ~/Documents/LookSDKForSymbol.sh RingerSwitch

# часть результатов опущена
0000d738 S _kGSRingerSwitchCapability
Found in ./System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices
000038cc T _SBGetRingerSwitchState
0000370c T _SBGetRingerSwitchState
Found in ./System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices
Функция с названием SBGetRingerSwitchState выглядела многообещающе.
Для получения нужного порта использовалась функция:
mach_port_t SBSSpringBoardServerPort();
из того же фреймворка.
Вот что получилось в итоге:
@implementation MuteSwitchTest … — (int) switchState { // Функции SBSSpringBoardServerPort и SBGetRingerSwitchState // загружается при инициализации объекта MuteSwitchTest mach_port_t port = SBSSpringBoardServerPort(); SInt32 state; SBGetRingerSwitchState(port, &state); return (int)state; }

Пример 2:

Определение IMEI
IMEI (International Mobile Equipment Identity) — уникальный идентификационный
код, присваиваемый каждому телефону, своего рода MAC-адрес телефона (хотя MAC-адрес у телефона также есть)
Я уже и не помню, как я вышел проект Эрики Садун uidevice-extension, но по мере того, как я с ним разбирался он всё больше казался мне этакой программистской «золотой жилой».
Одна из категорий, UIDeviсe(IOKit_Extensions) содержит функции для определения IMEI. Я протестировал эти функции на iPhone 4 c iOS 5.1 и iPad c iOS 4.3, всё работало и я перешел к другим задачам. Но в ходе бета-тестирования выяснилось, что функция для определения IMEI не работает на новых устройствах: iPad 2, the new iPad и iPhone 4S. Для выяснения причин я отправился на StackOverflow, где мои опасения подтвердились. Поиски привели меня тогда к фреймворку под названием CoreTelephony.
$ nm -g ./CoreTelephony | grep -i imei
U _kCFAbsoluteTimeIntervalSince1970
00053b28 S _kCTMobileEquipmentInfoIMEI
00053ad4 S _kCTPostponementInfoIMEI
00053ac4 S _kCTPostponementStatusErrorDefaultIMEI
$ nm -g ./CoreTelephony | grep MobileEquipment
000260e4 T __CTServerConnectionCopyMobileEquipmentInfo
00053b34 S _kCTMobileEquipmentInfo1xIMSI
00053b20 S _kCTMobileEquipmentInfoCurrentMobileId
00053b24 S _kCTMobileEquipmentInfoCurrentSubscriberId
00053b40 S _kCTMobileEquipmentInfoERIVersion
00053b2c S _kCTMobileEquipmentInfoICCID
00053b28 S _kCTMobileEquipmentInfoIMEI
00053b30 S _kCTMobileEquipmentInfoIMSI
00053b38 S _kCTMobileEquipmentInfoMEID
00053b44 S _kCTMobileEquipmentInfoMIN
00053b3c S _kCTMobileEquipmentInfoPRLVersion
Можно предположить что функция (_CTServerConnectionCopyMobileEquipmentInfo(…)) возвращает словарь(CFDictionaryRef) c ключами вида kCTMobileEquipmentInfo* и соответствующими им значениями. К счастью, на этот раз мне не пришлось восстанавливать сигнатуру. Поиск в гугле по запросу _CTServerConnectionCopyMobileEquipmentInfo привел меня на эту страничку, и вскоре функция для определения IMEI была готова.
// Заголовочный файл с декларациями недокументированных функций и констант #include «CoreTelephony.h» … NSString* CTGetIMEI { struct CTResult it; NSMutableDictionary *dict; CTServerConnectionRef conn; conn = _CTServerConnectionCreate(kCFAllocatorDefault, ConnectionCallback, NULL); _CTServerConnectionCopyMobileEquipmentInfo(&it, conn, &(CFMutableDictionaryRef)dict); CFRelease(conn); ; return ; }
Этот метод определения IMEI работает на всех устройствах.
Позже я нашел еще один метод определения IMEI (через lockdownd).

Пример 3:

Использование недокументированных оповещений: нажатия кнопок громкости.
Изначально я наивно полагал, что любая символьная константа, заканчивающаяся на «Notification» является названием системного оповещения и её можно использовать, просто зарегистрировав наблюдателя (observer) с помощью .
$ sh ~/Documents/LookSDKForSymbol.sh notification$ volume change
001dbe60 S _MPAVControllerVolumeDidChangeNotification
001dbe64 S _MPAVControllerVolumeMutedDidChangeNotification
001dc4f8 S _MPMusicPlayerControllerVolumeDidChangeNotification
001dc314 S _MPVolumeViewRouteButtonChangedNotification
001dc310 S _MPVolumeViewVisibilityChangedNotification
Found in ./System/Library/Frameworks/MediaPlayer.framework/MediaPlayer
000d6d24 D _AVController_EffectiveVolumeDidChangeNotification
000d6d60 D _AVController_VolumeDidChangeNotification
000d6fec D _AVSystemController_CurrentRouteHasVolumeControlDidChangeNotification
000d6ffc D _AVSystemController_EffectiveVolumeDidChangeNotification
000d6fdc D _AVSystemController_SystemVolumeDidChangeNotification
Found in ./System/Library/PrivateFrameworks/Celestial.framework/Celestial
… и еще около десятка из других фреймворков
Написав тестовую программу, я принялся проверять, какие оповещения приходили в ответ на нажатия клавиш громкости.
Из составленного мной довольно большого списка опопвещений приходили только вот эти 2:
AVController_EffectiveVolumeDidChangeNotification
AVController_VolumeDidChangeNotification
Недостаток этих оповещений в том, что
1) Нельзя напрямую определить, какая из двух кнопок была нажата
2) Нельзя отследить, когда нажата и когда отпущена каждая из кнопок
Ищу по другим ключевым словам:
$ sh ~/Documents/LookSDKForSymbol.sh volume button
001b221c t —
003cce5c t _SBSetWantsVolumeButtonEvents$shim
0054478c S __UIApplicationVolumeDownButtonDownNotification
00544790 S __UIApplicationVolumeDownButtonUpNotification
00544784 S __UIApplicationVolumeUpButtonDownNotification
00544788 S __UIApplicationVolumeUpButtonUpNotification
Found in ./System/Library/Frameworks/UIKit.framework/UIKit
… и еще несколько десятков из разных фреймворкрв
Четыре оповещения из UIKit сработали не сразу: необходимо было подать связанную с ними команду.
setWantsVolumeButtonEvents: YES];
После этого стали приходить оповещения о нажатиях соответствующих кнопок.
Побочный эффект: вызов данной функции приводит к тому, что кнопки громкости больше не регулируют громкость, так что по завершении работы с кнопками следует вызвать
setWantsVolumeButtonEvents: NO];

Пример 4:

Использование недокументированных оповещений: отслеживание статуса SIM-карты
Работаем по проверенной схеме:
$ sh ~/Documents/LookSDKForSymbol.sh notification$ SIM

00052560 S _kCTSIMSupportSIMInsertionNotification
00052564 S _kCTSIMSupportSIMStatusChangeNotification

000525bc S _kCTSIMSupportSIMTrayStatusNotification

Found in ./System/Library/Frameworks/CoreTelephony.framework/CoreTelephony

Found in ./System/Library/PrivateFrameworks/FTServices.framework/FTServices

$
Наиболее подходящими мне показались оповещения под названиями:
1) kCTSIMSupportSIMInsertionNotification
2) kCTSIMSupportSIMStatusChangeNotification
3) kCTSIMSupportSIMTrayStatusNotification
Простейшая тестовая программа показала, что оповещения под названием (1) приходили только в момент вставки сим-карты (я мог бы догадаться и раньше по названию), (2) приходили именно тогда когда мне нужно (при вставке и вынимании), оповещения (3) не приходили вообще. Позже я узнал, что оповещения (3) относятся к специальному центру оповещений под названием CTTelephonyCenter. Об использовании CTTelephonyCenter можно прочитать .
Оповещения о статусе SIM:
#include «CoreTelephony.h» — (void) notificationCallback:(NSNotification)notification { … } — (void) startUpdateSIMStatus { addObserver:self selector:@selector(notificationCallback:) name:kCTSIMSupportSIMStatusChangeNotification object:nil ]; } — (void) stopUpdateSIMStatus { removeObserver:self name:kCTSIMSupportSIMStatusChangeNotification object:nil]; }

Дополнения

1. Используем системные звуки в своем приложении

www.iphonedevwiki.net/index.php/AudioServices — здесь описаны недокументированные константы типа SystemSoundID для проигрывания стандартных коротких (< 30сек) звуков, таких как нажатие кнопок. Можно и самому их все найти, просто перебирая значения от 1000 в цикле.
Еще можно проигрывать стандартные рингтоны:
— (void) playDefaultRingTone { NSURL *defaultRingTone = ; AVAudioPlayer *player = initWithContentsOfURL:defaultRingTone error:nil]; ; }

2. Рекурсивный поиск по иерархии UIView

Как известно, у объектов класса UIView обычно есть родительский вид (superview) и могут быть дочерние виды (subviews). Верхушкой (как мы дальше увидим, верхушками) этой иерархии являются объект(ы) UIWindow. Что если пройти по всей иерархии? Тут есть тольк одна тонкость: как это не странно, в программе может быть больше одного объекта типа UIWindow.
Для получения __всех__ окон я использовал недокументированную функцию
+
При помощи неё мне удалось обнаружить что в самом обычном приложении может быть до четырех окон! (UIWindow)
1) обычное окно программы
2) окно для статус-бара (его также можно получить при помощи — )
3) окно для UIAlertView (Родительский вид для объектов типа UIAlertView).
4) окно для экранной клавиатуры.
Какую пользу мы можем из этого извлечь?
Очевидно, что с объектами последних трех типов мы можем обращаться так же, как и с первым.
В частности, мы можем:
— изменять внешний вид UIAlertView, добавлять текстовые поля, переключатели и т.д.
— изменять внешний вид статус-бара, добавлять свои индикаторы и удалять стандартные.
— изменять внешний вид экранной клавиатуры: например, изменять вид кнопок, добавить свои кнопки, переключатели и т.д
Но это еще не все, есть также неожиданные побочные последствия. Часть графических элементов может быть напрямую связана с тем или иным более глубоким функционалом.
@interface UIStatusBarSignalStrengthItemView : UIStatusBarItemView { @private int _signalStrengthRaw; int _signalStrengthBars; BOOL _enableRSSI; BOOL _showRSSI; } -(id)_stringForRSSI; -(float)extraRightPadding; -(void)touchesEnded:(id)ended withEvent:(id)event; -(id)contentsImageForStyle:(int)style; -(BOOL)updateForNewData:(id)newData actions:(int)actions; @end

Так, например, получив доступ к UIStatusBarSignalStrengthItemView мы имеем возможность вполне легально получать значения RSSI (мощность принимаемого сигнала) сотовой сети, и отображать в любой удобной форме.
Полный листинг кода, рекурсивно описывающего все видимые объекты в программе. Вызывается через :
@interface UIView (RecursiveDescription) — (void) recursiveDescription; + (void) completeDescription @end @implementation UIView (RecursiveDescription) — (void) recursiveDescription { NSLog(@»______________________________________»); NSLog(@»%@», self); NSArray *subviews_ = ; if ( > 0) { for (UIView *subview_ in subviews_) { ; } } } — (void)completeDescription { NSArray *windows = ; for (UIView *view in windows) { ; } } @end

3. Некоторые важные низкоуровневые подсистемы iOS

Подсистема MIG (MIG-subsystem, Mach Interface Generator) — интерфейс взаимодействия с ядром операционной системы (так называемый «Mach kernel»). Реализация MIG-subsystem для Mac OS X лежит где-то здесь: www.opensource.apple.com/source/xnu/xnu-1228.0.2/libsyscall/mach. Смотри также список известных низкоуровневых сообщений: www.iphonedevwiki.net/index.php/MIG_subsystem.
IORegistry, I/O registry — реестр ввода-вывода; древовидная структура, описывающая аппаратное обеспечение iPhone и взаимодействие с аппаратными компонентами. Примеры использования IORegistry можно найти в проектах Эрики Садун (см. UIDevice-IOKitExtensions).

Инструменты

nm — UNIX-утилита, выводящая таблицу символов объектнового файла.
На основе nm я написал простой (и довольно тупой) bash-скрипт, который ищет по всем библиотекам и объектным файлам внутри iOS SDK.
LookSDKForSymbol.sh:
#!/bin/bash SDK=/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk PrivateFrameworks=$SDK/System/Library/PrivateFrameworks Frameworks=$SDK/System/Library/Frameworks Lib=$SDK/usr/lib cd $SDK for i in $(find -f .); do test -f $i && nm $i | c++filt | grep -i «$1» | grep -i «$2» | grep -i «$3» && echo «Found in $i «; done
c++filt — восстановление (demangling) имён. Только для С++; имена objective-C и просто С идут сразу в человекочитаемом виде.
otool — стандартная утилита для анализа и дизассемблирования объектных файлов.
hexdump — дамп он дамп и есть 🙂
class-dump-z — суперполезная утилита. Позволяет генерировать заголовочный файл из объектного файла. Описание всех структур, протоколов, классов, категорий, их методов, свойств и так далее.
Репозиторий на Google Code — здесь можно ознакомиться с более подробным описанием и скачать исходный код этого проекта.
Hex-Rays ARM Decompiler — плагин для HEX-Rays IDA, декомпилятор ARM кода. Официальный сайт.
Утилиты Эрики Садун — на сайте Эрики есть несколько полезных утилит, например, утилита для анализа оповещений.

Полезные ресурсы, источники

Wiki

Единственная известная мне wiki, относящаяся к jailbreak разработке. Хотя информация и устарела
(iOS 3.x, iOS 4.x), но все равно это крайне полезный ресурс.

Персоны

1. Джей Фримен (Jay Freeman, saurik)

2. Эрика Садун (Erica Sadun) — автор книг «The iPhone Developer’s CookBook» и «The iOS 5 Developer’s Cookbook: Core Concepts and Essential Recipes for iOS Programmers», а также многих полезных утилит. В её книгах описываются некоторые недокументированные возможности публичных фреймворков.

ericasadun.com
Некоторые проекты Эрики Садун:
github.com/erica/iOS-5-Cookbook
github.com/erica/iphone-3.0-cookbook-
github.com/erica/uidevice-extension
3. KennyTM / networkpx — создатель class-dump-z, активный участник StackOverflow.com в вопросах, связанных с недокументированными API
networkpx.blogspot.com
code.google.com/p/networkpx
github.com/kennytm
stackoverflow.com/users/224671/kennytm
github.com/kennytm/iphone-private-frameworks
Информация сильно устарела (iOS 3).

Книги

Для того, чтобы Ваш новенький айфон всегда корректно работал, необходимо регулярно выполнять обновления фирменной операционной системы — iOS. Новые версии прошивки устраняют все недостатки и проблемы предыдущих, выявленные в процессе эксплуатации — по отзывам и замечаниям пользователей. Также они делают работу устройств более стабильной, быстрой и четкой. При этом значительно расширяются возможности, как системных приложений, так и сполна раскрывается потенциал «железа» в айфоне. Давайте разберемся, как обновить iPhone правильно, чтобы не возникла какая-нибудь непредвиденная ошибка.

Перед процедурой

Если Вы являетесь владельцем версии «шестерки» с объёмом памяти на 16 гигабайт, заранее позаботьтесь о наличии достаточного свободного объёма памяти. Имейте в виду, что даже если для скачивания архивированной папки с обновлениями места и хватает, то при её распаковке и установке на устройство — его потребуется немного больше. Поэтому старайтесь всегда оставлять небольшой задел, сверх указанного объёма пакета обновлений. Если этого не сделать – в любой момент может возникнуть ошибка. Загрузка может прерваться, айфон — зависнуть или застрять в петле восстановления, и потребуется в лучшем случае всё выполнять заново, в худшем – принудительно восстанавливать гаджет, с полной потерей данных.

Поэтому запомните три основных момента, которые обязательно нужно сделать перед обновлением iOS: убедитесь в наличии достаточного количества свободного места, обновите до последней версии iTunes, и обязательно сделайте резервные копии на компьютер или в iCloud.

Это сведёт к минимуму риск неудачного обновления. И в случае системного сбоя – Вы практически без потерь сможете восстановить все свои данные.

Чтобы наверняка исключить вероятность возникновения ошибок, не помешает предварительно обновить и операционную систему вашего компьютера, встроенные программы безопасности, а также установленный антивирус. После этого перезагрузите ПК, чтобы применить все обновления, и можно приступать к загрузке новой прошивки на iPhone.

Обновление

Существует два основных способа загрузки и установки обновлений. Первый — с самого айфона, при условии устойчивого подключения к сети через Wi-Fi, второй – с помощью авторизованного компьютера, в меню фирменной программы – iTunes.

Главные отличия между ними заключаются в скорости и удобстве.

Первый вариант удобен тем, что не требуется ничего кроме смартфона и интернета. Устройство выполнит само всю процедуру (если Вы выбираете этот способ – обязательно ставьте айфон на зарядку – неизвестно, сколько времени займёт скачивание и установка). Загрузка iOS проходит дольше, так как чисто физически – маршрутизатор, раздающий Wi-Fi, делает скорость передачи данных — медленной.

Второй – происходит значительно быстрее, но не обойтись без ПК и подключения по кабелю.

Для того чтобы сделать обновление с самого устройства – зайдите из меню настроек в – Основные, и в них выберите пункт – Обновление ПО.
После поиска в сети последних обновлений iOS — высветится предложение на их загрузку и установку – для этого нажмите на соответствующее сообщение снизу.
Затем подтвердите согласие на предлагаемые условия использования. После этого Вам больше ничего не нужно делать, айфон всё выполнит сам. Просто дождитесь окончания процесса. Лучше всего проводить эту процедуру вечером, тогда и скорость интернета повыше, и желающих скачивать обновления меньше. Да и к утру смартфон будет готов к работе.

Как только появляется свежее обновление – iTunes при подключении обязательно уведомит Вас об этом.
Если Вы сами инициируете этот процесс – подсоедините телефон к ПК кабелем (желательно — оригинальным), запустите iTunes. В ее меню найдите свой айфон среди устройств. Во вкладке настроек синхронизации и параметров – инициируйте обновление.

Всё, дальнейшие процессы будут протекать автоматически. Главное, чтобы соединение с сетью было устойчивым. После загрузки пакета обновлений iOS запустится установка, во время которой iPhone может неоднократно перезагружаться – это нормально.

Но случается, что с вашей стороны все условия соблюдены, но обновить iOS не удаётся. Упорно высвечивается ошибка, и пользоваться устройством нет никакой возможности.

Виды и причины ошибок

Бывает что, запустив процесс обновления прошивки, на экране высвечивается системное сообщение типа: «Не удалось установить связь с сервером обновления ПО iPhone». Первым делом — проверьте подключение устройства к интернету, и исключите проблемы с сетью. Может выдаваться ошибка и при неисправности кабеля или его непригодности (всегда пользуйтесь фирменным для прошивки). Попробуйте также исключить неполадки маршрутизатора, подсоединив компьютер непосредственно к сети.

Кроме того, вполне возможно, что именно в этот момент на серверах активации iOS у Apple есть временные проблемы. Повторите всё немного позже. Если Вам всё равно не удалось установить связь с сервером обновления ПО iPhone, попробуйте подключиться к другому ПК, с сетью от другого оператора.

Иногда ошибка возникает из-за ограничений, которые накладываются установленным антивирусом или брандмауэром компьютера, или несоответствием версий их обновлений. Поэтому установите все актуальные базы (в том числе и для операционной системы компьютера), перезагрузите ПК и повторите установку прошивки телефона.

В некоторых случаях программная ошибка решается т.н. «жесткой» перезагрузкой. Поможет она в случае, если смартфон перестал реагировать на любые действия пользователя. Для её запуска одновременно нажмите, и десять секунд подержите в этом состоянии кнопки — Home с Power. Экран гаджета должен погаснуть. Отпустив кнопки, дождитесь высвечивания логотипа по центру дисплея. После этого снова нажмите и сразу же отпустите Power – айфон должен загрузиться в штатном режиме.

Когда телефон неожиданно потух, и нет никакого отклика на нажатие кнопок — убедитесь, что батарея устройства не разряжена. Поставьте его на зарядку на всякий случай, и подождите 10-15 минут, чтобы он набрал достаточно энергии для запуска всех систем. После этого включите его обычным способом, и повторите процедуру обновления.

Если выше перечисленные способы не помогли, то, скорее всего, придётся восстанавливать устройство при помощи iTunes или iCloud (надеемся, что, прислушавшись к нашим советам, Вы создали копии всех важных данных). Процедура схожа с процессом обновления, только при соответствующем запросе выбрать нужно — Восстановить. При восстановлении — iPhone должен автоматически установить самую свежую версию прошивки.

Как видим, ничего сложного в установке обновлений прошивки на «шестёрки» нет. В подавляющем большинстве случаев, при условии соблюдения указанных выше требований, всё проходит гладко. А при наличии резервных копий, все данные сохранятся, даже, если непредвиденная ошибка и возникнет.

admin

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *