суббота, 25 апреля 2015 г.

Выжимка из книги Git Pro 2ed

После уже более чем 2-ух летней работы с git всеткаи заставил себя прочитать Git Pro 2ed. Не пожалел, узнал много нового. По мере продвижения вперед записывал все самое интересное в одну заметку в Evernote. В итоге получился достаточно ценный документ. Как и оригинальная книга все тут распространяется под  Creative Commons Attribution Non Commercial Share Alike 3.0 license.

Три состояния
Теперь слушайте внимательно. Это самая важная вещь, которую нужно запомнить о Git, если вы хотите, чтобы остаток процесса обучения прошёл гладко. Git имеет три основных состояния, в которых могут находиться ваши файлы: зафиксированном (committed), изменённом (modified) и подготовленном (staged). “Зафиксированный” значит, что файл уже сохранён в вашей локальной базе. К изменённым относятся файлы, которые поменялись, но ещё не были зафиксированы. Подготовленные файлы — это изменённые файлы, отмеченные для включения в следующий коммит.
Мы подошли к трём основным секциям проекта Git: Git директория (Git directory), рабочая директория (working directory) область подготовленных файлов (staging area).

Рабочая директория, stage область и директория Git.

The lifecycle of the status of your files.

Коммит и его дерево

Коммит и его родители

HEAD - указатель на коммит где мы находимся
ветка в Git — это всего лишь файл, содержащий 40 символов контрольной суммы SHA-1 того коммита, на который он указывает

Первоначальная настройка Git 
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com 
git config --global core.editor vim
git config user.name #текущее значение

Glob patterns
  • Blank lines or lines starting with # are ignored.
  • Standard glob patterns work.
  • You can end patterns with a forward slash (/) to specify a directory.
  • You can negate a pattern by starting it with an exclamation point (!).
Glob patterns are like simplified regular expressions that shells use. An aster- isk (*) matches zero or more characters; [abc] matches any character inside the brackets (in this case a, b, or c); a question mark (?) matches a single char- acter; and brackets enclosing characters separated by a hyphen([0-9]) match- es any character between them (in this case 0 through 9). You can also use two asterisks to match nested directories; a/**/z would match a/z, a/b/z, a/b/c/z, and so on.

# no .a files  
*.a
# but do track lib.a, even though you're ignoring .a files above  
!lib.a
# only ignore the root TODO file, not subdir/TODO  
/TODO
# ignore all files in the build/ directory  
build/
# ignore doc/notes.txt, but not doc/server/arch.txt  
doc/*.txt
# ignore all .txt files in the doc/ directory  
doc/**/*.txt


alias
config --global alias.co checkout
config --global alias.br branch
config --global alias.ci commit
config --global alias.st status
config --global alias.visual "!gitk» # запустит команду gitk

config alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
config alias.spush 'push --recurse-submodules=on-demand'
config alias.supdate 'submodule update --remote --merge'

ORIGIN” — ЭТО НЕ СПЕЦИАЛЬНОЕ НАЗВАНИЕ
Подобное название ветки “master” не имеет какого-либо назначения в Git, также и название “origin”. В то время как “master” — это название по умолчанию для исходной ветки, когда вы запускаете git init, по единственной причине, что широко используется, “origin” — это название по умолчанию для удаленной ветки, когда вы запускаете git clone. Если вы запустите git clone -o booyah, так вы будете иметь booyah/master как вашу удаленную ветку по умолчанию.

git fetch обновляет ваши удаленные ссылки

Отслеживание веток
Получение локальной ветки из удалённой ветки автоматически создаёт то,что называется“отслеживаемой веткой” (или иногда“up- stream branch”).Отслеживаемые ветки—это локальные ветки,которые напрямую связаны с удалённой веткой.Если,находясь на отслеживаемой ветке,вы наберётеgit pull, Gitуже будет знать,с какого сервера получить все удалённые ссылки и сделает слияние с соответствующей удалённой веткой.Аналогично выполнениеgit pullна одной из таких веток,сначала получает все удалённые ссылки,а затем автоматически делает слияние с соответствующей удалённой веткой.
При клонировании репозитория, как правило, автоматически создаётся ветка master, которая отслеживает origin/master. Однако, вы можете настроить отслеживание и других веток, допустим если вы хотите, чтобы одни ветки отслеживались с другого удаленного репозитория или не хотите отслеживать ветку master. Простой пример, как это сделать, вы увидели только что — git checkout -b [ветка] [удал. сервер]/[ветка]. Существует общепринятая операция, которую git предоставляет, --track:

Simple divergent history

Merging to integrate diverged work history

Rebasing the change introduced in C4 onto C3

fast-forward (перемотка) - из-за того, что коммит, на который указывала ветка, которую вы слили, был прямым потомком того коммита, на котором вы находились, Git просто перемещает указатель ветки вперед.

СОКРАЩЕНИЕ UPSTREAM
Если у вас есть установленная отслеживаемая ветка, вы можете ссылаться на нее с помощью @{upstream} или @{u} сокращенно. Итак, если вы находитесь на master-ветке, а отслеживается origin/master, вы можете вызвать что-то вроде git merge @{u} вместо git merge origin/master если хотите.

Rebasing after force push from outside
If someone on your team force pushes changes that over- write work that you’ve based work on, your challenge is to figure out what is yours and what they’ve rewritten.
It turns out that in addition to the commit SHA-1 checksum, Git also calcu- lates a checksum that is based just on the patch introduced with the commit. This is called a “patch-id”.
If you pull down work that was rewritten and rebase it on top of the new commits from your partner, Git can often successfully figure out what is unique- ly yours and apply them back on top of the new branch.

Fetch more commits, and merge them into your work

You merge in the same work again into a new merge commit

Rebase on top of force-pushed rebase work.

Различия checkout и reset
Важно отличие заключается в том, как эти команды обновляют HEAD. В то время как reset перемещает ветку, на которую указывает HEAD, команда checkout перемещает сам HEAD так, чтобы он указывал на другую ветку.


Доступ к git через ssh
Пример доступа через NAT (пробрасывать 2222 порт на ssh компьютера в локальной сети)

Настройка на клиенте
ssh-keygen - генерирует приватные и публичный ключи
cat ~/.ssh/id_rsa.pub - возвращает публичный ключ
cat ~/.ssh/id_rsa - возвращает приватный ключ

На сервере
cat key >> ~/.ssh/authorized_keys - перет публичный ключ из файла key и добавляет для входа без пароля

Настройка для sendmail
[sendemail]
     from = soniccat2@gmail.com
     smtpencryption = tls
     smtpserver = smtp.gmail.com
     smtpuser =
     smtppass =
     smtpserverport = 587
[imap]
     folder = "[Gmail]/Drafts"
     host = imaps://imap.gmail.com
     user =
     pass =
     port = 993
     sslverify = true

Работа с gpg
gpg --list-keys - генерация ключа
gpg --list-keys - список ключей
gpg --import blake.gpg - импорт публичного ключа
gpg --output alice.gpg --export alice@cyb.org - экспорт публичного ключа

Настройка работы fetch-а
git fetch помещает загружает ветки вид refs/head, refs/tag. Если на сервере создаются другие ссылки (их можно увидеть git ls-remote server) то можно расширить правила работы fetch-а

[remote "origin"]      
     url = https://github.com/libgit2/libgit2      
     fetch = +refs/heads/*:refs/remotes/origin/*
     fetch = +refs/pull/*/head:refs/remotes/origin/pr/*

That line that begins with fetch = is a “refspec.” It’s a way of mapping names on the remote with names in your local .git directory. This particular one tells Git, “the things on the remote that are under refs/heads should go in my local repository under refs/remotes/origin.”

Git caret and tilde
ref~ is shorthand for ref~1 and means the commit's first parent. ref~2 means the commit's first parent's first parent. ref~3 means the commit's first parent's first parent's first parent. And so on.

ref^ is shorthand for ref^1 and means the commit's first parent. But where the two differ is that ref^2 means the commit's second parent (remember, commits can have two parents when they are a merge).

The ^ and ~ operators can be combined.

gitattributes
Для помощи определения к какому языку какой файл относится нужно создать файл .gitattributes и прописать
*.java diff=java

после включения опция -p для git grep, git diff, git log будет печатать имя функции сверху  результатов

Три дерева
HEAD, Индекс, Рабочий каталог

Получение разных версий файла при конфликте
Получить эти три версии файла, на самом деле, довольно легко. Git хранит все эти версии в индексе в разных “состояниях”, каждое из которых имеет ассоциированный с ним номер. Состояние 1 – это общий предок, состояние 2 – ваша версия и состояния 3 взято из MERGE_HEAD – версия, которую вы сливаете (“их” версия).
Вы можете извлечь копию каждой из этих версий конфликтующего файла с помощью команды git show и специального синтаксиса.

git show :1:hello.rb > hello.common.rb
git show :2:hello.rb > hello.ours.rb
git show :3:hello.rb > hello.theirs.rb

Работа с субдеревьями
Добавляем в remote совершенно другой проект и создаем ветку от него

git remote add rack_remote https://github.com/rack/rack
git fetch rack_remote
git checkout -b rack_branch rack_remote/master

выгружаем ее в проект
git read-tree --prefix=rack/ -u rack_branch

обновляем в проекте после обновления нашей ветки rack_branch
git merge --squash -s recursive -Xsubtree=rack --no-commit rack_branch

просмотр изменений 
git diff-tree -p rack_branch

Git Attributes
Файл .gitattributes
*.pbxproj binary - говорит что файл бинарный
*.png diff=myfrmt - указывает атрибут для сравнения .png файлам
git config diff.myfrmt.textconv mdls - textconv указывает какой программой конвертировать бинарные файлы в текстовые

*.c filter=code - указываем атрибут-фильтр для .c файлов
git config filter.code.smudge "sed -e 's/DEBUG=0/DEBUG=1/g’" - при checkout выполнит замену на DEBUG=1
git config filter.code.clean "sed -e 's/DEBUG=1/DEBUG=0/g’" - после commit выполнит заменту на DEBUG=0

The “smudge” filter is run on checkout.

The “clean” filter is run when files are staged.

Общие команды

status
git status -s #статус в короткой форме

add
git add -i - интерактивный режим работы с файлами
git add -p file - позволяет перенести в состояние staged только часть изменений

config
git config --list #чтобы показать все настройки
git config --global credential.helper cache - включает кэш учетных данных
git config --global credential.helper osxkeychain - в качестве кеша используется keychain
git config --global rerere.enabled true - включает локирование резловинга конфликтов
git config --global user.signingkey 0A46826A - указывает каким ключем подписывать коммиты и теги
git config -f .gitmodules submodule.DbConnector.branch stable - глобальная настройка отслеживания ветки stable для сабмодуля DbConnector
git config status.submodulesummary 1 - нужно показывать изменения в submodule на git diff, git st
git config --global commit.template ~/.gitmessage.txt - указывает начальный текст для каждого коммита
git config --global core.excludesfile ~/.gitignore_global - глобальный gitignore файл
git config --global help.autocorrect 50 - корректирует ввод если git однозначно нашел что вы иммели ввиду. Даст 5 секунд на раздумие.
git config --global core.autocrlf true - автозамена LF на CRLF
git config --global core.autocrlf input - для коммитов
git config --global core.whitespace trailing-space,space-before-tab,indent-with-non-tab - указывает ошибочные пробелы
     blank-at-eol, which looks for spaces at the end of a line;
     blank-at-eof, which notices blank lines at the end of a file;
     space-before-tab, which looks for spaces before tabs at the beginning of a line
     indent- with-non-tab, which looks for lines that begin with spaces instead of tabs (and is controlled by the tabwidth option);
     tab-in-indent, which watches for tabs in the indentation portion of a line;
     cr-at-eol, which tells Git that carriage returns at the end of lines are OK.

—system записывает в /etc/gitconfig
—global записывает в ~/.gitconfig
иначе запишет в .git/config

clone
git clone --bare my_project my_project.git - делает голую копию проекта без working directory для размещения на сервере
git clone --recursive https://github.com/chaconinc/MainProject - возьмет проект и обновит все подмодули

diff
git diff --staged (or --cached) #изменения между последним коммитом и добавленными файлами
git difftool --tool-help #доступные программы для визуализации diff
git config --global diff.tool opendiff
git difftool -d 2105e0 #сравнивать все изменения между собой как директории
git diff 2105e | gitx #красивое отображение diff-а
git diff --check - проверят наличие лишних пробелов
git diff --ours -  наши изменения
git diff --heirs -  вливаемые изменения
git diff --base - общие изменения
rm
git rm --cached README #убирает файл из состояния staged
git rm log/\*.log #This command removes all files that have the .log extension in the log/ directory.

log
-p #показывает разницу, внесенную в каждый коммит
- #кол-во коммитов
--stat #показывает список измененных файлов
—shortstat #Отображает только строку с количеством изменений/ вставок/удалений для команды --stat.
--name-only
--name-status
--abbrev-commit #Показывает только несколько символом SHA-1 чек-суммы вместо всех 40.
--relative-date
--pretty= #стиль вывода
     oneline #в одну строчку
     short/full/fuller #кол-во данных на комит
     format
--decorate - показывает на какие ветки, теги ссылаются коммиты
git log --pretty=format:"%h - %an, %ar : %s"
%H Хеш коммита
%h Сокращенный хеш коммита
%T Хеш дерева
%t Сокращенный хеш дерева
%P Хеш родителей
%p Сокращенный хеш родителей %an Имя автора
%ae Электронная почта автора
%ad Дата автора (формат даты можно задать опцией -- date=option)
%ar Относительная дата автора
%cn Имя коммитера
%ce Электронная почта коммитера
%cd Дата коммитера
%cr Относительная дата коммитера
%s Содержание
%G подписан коммит или нет

--graph #показывает текущую ветку и историю слияний
--since= #Show commits more recent than a specific date.
--until= #Show commits older than a specific date.
--since=2.weeks
2008-01-15
2 years 1 day 3 minutes ago
--author= #дает возможность фильтровать по автору коммита
--G= #искать по ключевым словам в сообщении коммита
--all-match #искать по всем
-S #принимает строку и показывает только те коммиты, в которых изменение в коде повлекло за собой добавление или удаление этой строки
git log branch1..branch2 - показывает результаты сделанные в ветке branch2 но не содержат изменения branch1 и его предков
git log branch2 --not branch1- делает тоже самое
git log refA refB --not refC - показвыает лог по веткам A и B без коммитов которые есть в C
git log --left-right master…experiment - покажет еще в какой ветке какой коммит
git log --oneline --left-right HEAD…MERGE_HEAD - покажет все уникальные коммиты в текущей и сливаемой ветках
git log --oneline --left-right —merge HEAD…MERGE_HEAD - покажет только файлы с конфликтами

git log --show-signature - показывает кем подписан коммит
git log -g - выводит лог по истории изменения указателя на HEAD
git log -L :funcName:filePath - показывает историю изменения кода функции, нужно настроить .gitattributes чтобы заработало

shortlog
git shortlog --no-merges master...HEAD - группирует коммиты по автору

commit
git commit —amend #добавляет последние изменения в последний коммит, если ничего не было изменено то поправится только текст коммита
git commit -a -S -m 'signed commit’ - создает подписанный коммит

submodule
git submodule update --init —recursive - обновляет все сабмодули во всех репоизиториях
git submodule add https://github.com/chaconinc/DbConnector - добавляет подмодуль в папку DbConnector
git submodule init - подготавливает подмодули
git submodule update - загружает подмодули добавленной версии
git submodule update —remote module - берет последнюю версию submodule по урлу
git submodule update --remote —merge - сливает изменения сабмодуля в существующую ветку
git push --recurse-submodules=check - проверяет все ли локальные сабмодули доступны на сервере
git push --recurse-submodules=on-demand - обновляет измененные сабмодули на сервере
git submodule foreach 'git diff’ - выполняет комманду для всех сабмодулей

reset
git reset HEAD - чтобы сделать unstage для файла и перенести его в working статус, берет файл из указанного дерева (не обязательно HEAD) и кладет в индекс
git reset --soft HEAD~ - не меняет изменения в индексе, перемещает только указатель на HEAD
git reset —hard HEAD~ - затирает и изменения сделанные в рабочем каталоге

revert
git revert -m 1 HEAD - создает коммит обратный последним изменениям, указывается parent версию файлов которого нужно оставить

remote
git remote -v #показывает разыменованный путь до сервера
git remote add [shortname] [url] #добавляет новый сервер
git remote show [remote-name] # показывает дополнительную информацию по выбранному серверу
git remote prune #удаляет бранчи удаленные на сервере
git remote rename #изменяет короткое название сервера
git remote rm name #удаляет сервер из писка

tag
git tag -l  #покацивает теги соотв. паттерну

Легковесная метка — это что-то весьма похожее на ветку, которая не меняется — это просто указатель на определённый коммит.
А вот аннотированные метки хранятся в базе данных Git’а как полноценные объекты. Они имеют контрольную сумму, содержат имя поставившего метку, e-mail и дату, имеют комментарий и могут быть подписаны и проверены с помощью GNU Privacy Guard (GPG)

git tag -a -m 'my version 1.4’ #создает аннотированную метку
git tag #создает легковесную метку
git show v1.4 #показывает информацию о теге
git push origin #отправляет метку на сервер
git push origin —tags #отправляет все метки
git tag -s v1.5 -m 'my signed 1.5 tag'- создает подписанный тег
git tag -v - проверяет тег, у проверяющего должен быть импортирован публичный ключ

pull
git pull --rebase = git fetch && git rebase server/branch

push
git push server branch - отправка зименения на branch на сервере
git push origin serverfix = git push origin server fix:serverfix - имя локального бранча : имя удаленного
git push --set-upstream origin singlebeanch - отправляет изменения в remote branch и настраивает tracking
git push -u origin br = git push --set-upstream origin br
git push origin --delete serverfix - удляет ветку serverfix на сервере origin
git push --force  - отправляет изменения переписывая историю на сервере

branch
git branch -v #список веток с последним коммитом
git branch -vv #список веток с последним коммитом и tracking веткой
git branch --no-merged #неслитые ветки
git branch -d #удалить бранч
git branch -u server/branch - настраивает tracking
git branch newBranch baseBranch - создает ветку отведенную от baseBranch

fetch
git fetch teamone - извлекает что есть на сервере а у вас нет

merge
git merge origin/serverfix - слить изменения с удаленного бранча
git merge —squash branch - слить изменения в один коммит
git merge —no-commit - слить изменения но не делать коммит
git merge --verify-signatures signed-branch - мерджит только подписанные коммиты
git merge --verify-signatures -S signed-branch - тоже самое но еще и подписывает merge commit
git merge —abort - откатывает состояние до начала слияния

checkout
git checkout -b serverfix origin/serverfix - создает ветку на основе удаленной и переключается на нее
git checkout --track origin/serverfix - создает ветку на основе удаленной и настраивает tracking
git checkout commit file - заменяет индекс и рабочую директорию на версию файла в указанном коммите

rebase
git rebase --onto master server client - переходит на ветку client смотрит изменения сделанные после отведения этой ветки от ветки server. Применяет изменения к мастеру.
git rebase --onto 622e88 9c68fdc - перекладывает коммиты идущие от 9c68fdc так чтобы они шли от 622e88
git rebase master server - переходит на ветку server делает там rebase master
git rebase -i branch - позволяет отредактировать историю, поменять коммиты местами, удалить, изменить сообщение и/или слить их в один. Если нужно изменить коммит то после возврата в консоль нужно набрать reset HEAD^ чтобы изменения перенеслись в unstaged состояние

blame
git blame -L 12,22 simplegit.rb - показывает авторство для указанного диапазона строк
git blame -M  - при перемещении кода в файле, покажет коммит где она первый раз появилась
git blame -C -L 141,153 GITPackUpload.m - покажет первое место где были написанны указанные строки. Ищет в рамках коммита. -CC ищет еще в коммите где создавался файл, -ССС ищет по всем коммитам

describe
git describe commit —long - возвращает описание состоящее из тега, хеша (v1.0-0-g49db12e)

merge-base
git merge-base contrib master - возвращет общий коммит

pull-request
git request-pull startCommint remoteName endCommit - создает описания для пуллреквеста

format-patch
git format-patch branch - формирует патчи по одному на коммит

cherry-pick
git cherry-pick commit - вливает коммит в текущий бранч

reflog
git reflog - локальная история перемещения указателя на HEAD

stash
git stash list - список сохраненных изменений
git stash apply  --index - восстановит  файлы и если они были staged то проиндексирует их
git stash drop stash@{0} - удалит изменение
git stash pop - применяет и удаляет изменение
git stash --keep-index - сохранит только непроиндексированные изменения
git stash -u - сохранит незатреканные файлы
git stash --patch - для указания какие изменения в файле нужно сохранить
git stash branch testchanges  stash@{0} - создает ветку из выбранного изменения

clean
git clean -f -d - удаляет незатреканные файлы и деректории
git clean -d -n - холостой запуск, покажет что будет удалено
git clean -n -d -x - удалит и файлы попадающие под .gitignore
git clean -x -i - интерактивное удаление

grep
git grep -т gmtime_r - номера строк
git grep -с gmtime_r - покажет кол-во совпадений в файле
git grep Window -- *.java - поиск по файлам java
git grep -np --break --heading Window 1.0 - ищет в теге 1.0, группирует по файлам и отображает заголовок
git grep --break --heading -n -e '#define' --and \( -e LINK -e BUF_MAX \) v1.8.0 - поис со сложным правилом

archive
git archive master --prefix='project/' --format=zip > `git describe master`.zip - сохраняет дерево файлов в архив в папку project

rerere
git rerere - пытается решить конфликт на основе истории мерджей

bisect
git bisect start badCommit goodCommit - запускает бинарный поиск на указанной границе
git bisect bad - говорит что текущее состояние сломано
git bisect good - говорит что текущее состояние в норме
git bisect run test-error.sh - выполняет поиск используя скрипт возвращающий 0 если все в норме или другое число если ошибка
git bisect reset - возвращает HEAD туда где он был до начала поиска

send-email
git send-email *.patch - отправляет созданные патчи, на каждое успешно отправленное письмо должно быть сообщение в консоли вида
Result: 250 2.0.0 OK 1427892387 d6sm396808lbp.16 - gsmtp

apply
git apply patchFile - применяет патч
git apply —check patchFile - проверяет можно ли применить патч

bundle
bundle можно клонировать или добавить как remote
git bundle create repo.bundle HEAD master - создает пакет с данными о коммитах в master и коммитах до HEAD-а
git bundle verify commits.bundle - проверяет что есть все чтобы импортировать пакет
git bundle list-heads commits.bundle - показывает ветки находящиеся в пакете
git fetch commits.bundle master:other-master - берем ветку master из пакета и копируем ее содержимое себе в ветку other-master

replace
git replace 81a708d c6e1e95 - заменяет коммит 81a708d коммитом c6e1e95, хеш остается прежним но внутренности (дерево, parent, autor) заменяются. По сути создается ссылка которую можно будет увидеть в for-each-ref

am
git am patchFile - применяет патч созданные с помощью format-patch
git am -3 patchFIle - применяет патч с учетом того что он было создан на основе вашего репозитория

ls-remote
git ls-remote https://github.com/schacon/blink - показвает все удаленные ветки на сервере

cat-file
git cat-file -p HEAD - показывает адрес на дерево

ls-tree
git ls-tree -r HEAD - показывает список файлов в дереве

ls-files
git ls-files -s - показывает файлы в индексе
git ls-files -u - показывает 3 версии файла при конфликте

rev-list
возвращает список хешей для указанного диапазона в обратном порядке (сверху последний созданный коммит)

commit-tree
echo 'get history from blah blah blah' | git commit-tree 9c68fdc^{tree} - создает коммит на базе дерева файлов

for-each-ref
показывает список всех ссылок и их адреса

среда, 4 января 2012 г.

MemCheck for iOS v 2.0 in english

Memory leaks are frequent problem in iOS projects. I have developed a lib which catches leaked objects and visualizes their relationships between each other.

All interactions occur through a Xcode console. Press pause in a Xcode debugger and input something like that:  

po [parser run:@"leaks saveGraph /Users/yourName/Documents/dot/mem.txt"]

The command generate mem.txt file for a dot tool (http://www.graphviz.org/). A dot must be installed. Then we can convert mem.txt to dot file by:

dot -Tdot mem.txt -o mem.dot && open ./mem.dot 

That will show graph with live and died objects from your application at current moment. Some examples:



An arrow from the DetailViewController to the UILabel means that an object of DetailViewController contains an object of UILabel. A dotted arrow shows when an object dead. Live objects have a white background, dead objects have a gray background.

About a parser command format and special filter (fromList) which remove all unnecessary objects you can read on memCheck-for-iOS git hub page.

Happy Using :)

воскресенье, 6 марта 2011 г.

MemCheck description in english

MemCheck helps to find memory leaks in your iOS applications. It can register alloc, retain and release calls for NS-objects and show detail information in console.

Sources

https://github.com/soniccat/memCheck-for-iOS

Connect the library

Add 6 files to your project:

NSMemCheckObject.h
NSMemCheckObject.m
NSMutableArray+MemCheck.h
NSMutableArray+MemCheck.m
NSObject+MemCheck.h
NSObject+MemCheck.m
add in the tail of your pch file

#define MEMTEST_ON

add in the top of the application: didFinishLaunchingWithOptions: function

#ifdef MEMTEST_ON
[NSObject turnMemCheckOn];
#endif

Using

When you app is running in any moment you can press the pause button and print in the Xcode's console:

po [memData allMem]

14 items

(
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b45840 object 0x4b45530 stack 0x4b45a00 NSCountedSet",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e16040 object 0x4e15d00 stack 0x4e15c40 NSAutoreleasePool",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e10cc0 object 0x4b0de50 stack 0x4e14ef0 __NSPlaceholderDictionary",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b44ab0 object 0x4b44a90 stack 0x4b42d10 CALayerArray",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e16cd0 object 0x4e02b80 stack 0x4e16f40 __NSPlaceholderArray",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e151d0 object 0x4e14ec0 stack 0x4e16380 NSKeyValueMethodSetter",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e15040 object 0x4e15190 stack 0x4e16060 CALayer",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e0faf0 object 0x4e08960 stack 0x4e15d20 __NSPlaceholderSet",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e14de0 object 0x4b10790 stack 0x4e14f60 NSPlaceholderString",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e06c60 object 0x4b3fdc0 stack 0x4e106f0 NSAutoreleasePool",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b41cd0 object 0x4b41750 stack 0x4b43450 __NSPlaceholderDate",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b44100 object 0x4b440f0 stack 0x4b440a0 __NSPlaceholderTimeZone",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b43680 object 0x4e02b70 stack 0x4b438f0 __NSPlaceholderArray",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b41960 object 0x4b02420 stack 0x4b43750 NSObject"

)

14 is all registered objects, which exist in memory at this moment. The list starts with last allocated object.
an address after memCheckObject point to wrapper object
an address after object point to allocated object
an address after stack point to alloc callstack
in end of line allocated an object's class name is displayed

po [memData top:N]

return only N last allocated objects

po address_of_object

display a defaul object's description

po address_of_callstack

display a stack like that

<_nscallstackarray>(
0 CoreFoundation 0x00da9be9 __exceptionPreprocess + 185,
1 libobjc.A.dylib 0x00efe5c2 objc_exception_throw + 47,
2 inFoundation 0x00002796 +[NSObject(memCheck) myAllocFunc] + 918,
3 inFoundation 0x00001c81 -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 161,
4 UIKit 0x002b31fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163,
...
)

po [address_of_wrapper_object history]

display a callstacks for alloc, retain and release functions, ordered by date. It shows like that:

ALLOC:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x0000310f +[NSObject(memCheck) myAllocFunc] + 831

3 memCheck 0x000024d2 -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 98

4 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

RETAIN:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x000034cf -[NSObject(memCheck) myRetainFunc] + 335

3 CoreFoundation 0x00cbf0bc CFRetain + 92

4 CoreFoundation 0x00da5db5 +[__NSArrayI __new::] + 117

5 CoreFoundation 0x00d188a3 +[NSArray arrayWithObject:] + 67

6 memCheck 0x00002508 -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 152

7 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

RETAIN:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x000034cf -[NSObject(memCheck) myRetainFunc] + 335

3 memCheck 0x00002538 -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 200

4 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

RELEASE:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x000037ee -[NSObject(memCheck) myReleaseFunc] + 302

3 memCheck 0x0000258d -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 285

4 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

RETAIN:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x000034cf -[NSObject(memCheck) myRetainFunc] + 335

3 CoreFoundation 0x00cbf0bc CFRetain + 92

4 CoreFoundation 0x00da5db5 +[__NSArrayI __new::] + 117

5 CoreFoundation 0x00d188a3 +[NSArray arrayWithObject:] + 67

6 memCheck 0x000025ae -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 318

7 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

po [address_of_wrapper_object retains] and po [address_of_wrapper_object releases]

return callstacks for all retain or release calls, started with last.

po [memData markHeap]

save a current date which divide the allocated object list on earlier and later groups

po [memData showHeaps]

show allocated groups divided by saved dates

po [memData objectsForHeap:n]

return an allocated object list in selected group

Обновление memCheck for iOS v 1.1

Добавил команды для работы c heap-ами на подобии Allocations Instrument

po [memData markHeap]
сохраняет текущую дату разбивая тем самым список объектов на созданных до и после

po [memData showHeaps]
выводит список групп из выделенных объектов разбитый с помощью markHeap

po [memData objectsForHeap:n]выводит список созданных объектов для указанной группы

Обновленное описание - http://gaolife.blogspot.com/2011/02/iphone.html

github репозиторий - https://github.com/soniccat/memCheck-for-iOS

вторник, 22 февраля 2011 г.

Обновление memCheck for iOS

Теперь можно запросить callstak-и до всех release и retain вызовов с помощью po [0xaddress retains] и po [0xaddress releases] соответственно. Для отображения всех вызово в хронологическом порядке для одного объекта добавил команду po [0xaddress history].

Обновленное описание - http://gaolife.blogspot.com/2011/02/iphone.html

github репозиторий - https://github.com/soniccat/memCheck-for-iOS

воскресенье, 20 февраля 2011 г.

Мультитекстурирование в OpenGL ES на iPhone


Пример мультитекстурирования на OpenGL ES в iPhone. Уже не помню как там что работает, главное что работает :)

Новая версия библиотеки для поиска утечек памяти в iOS приложениях

Краткое описание

В процессе разработки приложений для iPhone очень часто возникают проблемы с утечками памяти, увы стандартные инструменты не сильно помогают в их устранении, поэтому я написал эту библиотеку. С ее помощью можно узнать сколько объектов в текущий момент находится в памяти, какого они класса и в где были созданы.

Исходники

https://github.com/soniccat/memCheck-for-iOS

Подключение

добавить все 6 файлов к проекту
NSMemCheckObject.h
NSMemCheckObject.m
NSMutableArray+MemCheck.h
NSMutableArray+MemCheck.m
NSObject+MemCheck.h
NSObject+MemCheck.m

добавить в метод application: didFinishLaunchingWithOptions:

#ifdef MEMTEST_ON

[NSObject turnMemCheckOn];

#endif

вниз вашего .pch

#define MEMTEST_ON

Использование

В момент когда нужно отобразить занимаемую память нажать на паузу в Debugger-е и ввести в gdb

po [memData allMem]
будет ответ вида

14 items

(
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b45840 object 0x4b45530 stack 0x4b45a00 NSCountedSet",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e16040 object 0x4e15d00 stack 0x4e15c40 NSAutoreleasePool",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e10cc0 object 0x4b0de50 stack 0x4e14ef0 __NSPlaceholderDictionary",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b44ab0 object 0x4b44a90 stack 0x4b42d10 CALayerArray",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e16cd0 object 0x4e02b80 stack 0x4e16f40 __NSPlaceholderArray",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e151d0 object 0x4e14ec0 stack 0x4e16380 NSKeyValueMethodSetter",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e15040 object 0x4e15190 stack 0x4e16060 CALayer",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e0faf0 object 0x4e08960 stack 0x4e15d20 __NSPlaceholderSet",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e14de0 object 0x4b10790 stack 0x4e14f60 NSPlaceholderString",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4e06c60 object 0x4b3fdc0 stack 0x4e106f0 NSAutoreleasePool",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b41cd0 object 0x4b41750 stack 0x4b43450 __NSPlaceholderDate",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b44100 object 0x4b440f0 stack 0x4b440a0 __NSPlaceholderTimeZone",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b43680 object 0x4e02b70 stack 0x4b438f0 __NSPlaceholderArray",
"2011-02-23 06:47:04 +0000 memCheckObject 0x4b41960 object 0x4b02420 stack 0x4b43750 NSObject"

)

где 14 это кол-во объектов создание которых было зарегистрировано (вверху самый новый), после memCheckObject идет адрес на объект который сохраняет данные о выделенных объектах, после object идет адрус объекта, после stack адрес на получение стека до места где объект был создан и в конце имя класса самого объекта

po [memData top:10]
возвращает список из последних 10 выделенных объектов, число можно менять на какое вздумается

po 0x4b02420
вернет описание объекта

po 0x4b43750
Вернет стек до места создания:

<_nscallstackarray>(
0 CoreFoundation 0x00da9be9 __exceptionPreprocess + 185,
1 libobjc.A.dylib 0x00efe5c2 objc_exception_throw + 47,
2 inFoundation 0x00002796 +[NSObject(memCheck) myAllocFunc] + 918,
3 inFoundation 0x00001c81 -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 161,
4 UIKit 0x002b31fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163,
...
)

значимая строчка для нас это 4ая, именно в application: didFinishLaunchingWithOptions объект и был создан

Самый простой алгоритм поиска утечек памяти следующий. Запоминаем количество выделенных объектов, заходим в контроллер, жмем back, смотрим сколько объектов теперь, если их стало больше то уже внимательнее смотрим на callstack n верхних элементов, где n это количество новых объектов.

po [0x4b41960 history]
возвращает callstack-и до вызовов alloc, retain и release начиная с самого старого
ALLOC:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x0000310f +[NSObject(memCheck) myAllocFunc] + 831

3 memCheck 0x000024d2 -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 98

4 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

RETAIN:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x000034cf -[NSObject(memCheck) myRetainFunc] + 335

3 CoreFoundation 0x00cbf0bc CFRetain + 92

4 CoreFoundation 0x00da5db5 +[__NSArrayI __new::] + 117

5 CoreFoundation 0x00d188a3 +[NSArray arrayWithObject:] + 67

6 memCheck 0x00002508 -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 152

7 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

RETAIN:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x000034cf -[NSObject(memCheck) myRetainFunc] + 335

3 memCheck 0x00002538 -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 200

4 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

RELEASE:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x000037ee -[NSObject(memCheck) myReleaseFunc] + 302

3 memCheck 0x0000258d -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 285

4 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

RETAIN:

2011-02-23 06:26:56 +0000

(

0 CoreFoundation 0x00dacbe9 __exceptionPreprocess + 185

1 libobjc.A.dylib 0x00f015c2 objc_exception_throw + 47

2 memCheck 0x000034cf -[NSObject(memCheck) myRetainFunc] + 335

3 CoreFoundation 0x00cbf0bc CFRetain + 92

4 CoreFoundation 0x00da5db5 +[__NSArrayI __new::] + 117

5 CoreFoundation 0x00d188a3 +[NSArray arrayWithObject:] + 67

6 memCheck 0x000025ae -[inFoundationAppDelegate application:didFinishLaunchingWithOptions:] + 318

7 UIKit 0x002b61fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163

...

)

po [0x4b41960 retains] и po [0x4b41960 releases]
возвращают callstack-и для вызовов retain и release соответственно, начиная с самого раннего

po [memData markHeap]
сохраняет текущую дату разбивая тем самым список объектов на созданных до и после

po [memData showHeaps]
выводит список групп из выделенных объектов разбитый с помощью markHeap

po [memData objectsForHeap:n]
выводит список созданных объектов для указанной группы

Ограничения

работают только с iOS 4.0 и выше ( из за доступа к стеку )

Описание устройства

По прочтению умных статей c NSBlog :
http://www.mikeash.com/pyblog/friday-qa-2009-01-23.html
http://www.mikeash.com/pyblog/friday-qa-2009-03-13-intro-to-the-objective-c-runtime.html
http://www.mikeash.com/pyblog/friday-qa-2009-03-27-objective-c-message-forwarding.html

я понял как работает obj_msgSend, и что IMP это обычный указатель на функцию который можно подменить у любого метода NS объекта. В итоге сохранив старый IMP мы можем в вызове собственного метода вызвать базовый, что нельзя было сделать с помощью категорий.

При инициализации я подменяю базовые методы своими с помощью method_exchangeImplementations:

typedef id (*OverrideMemCheckPrototipe)(id,SEL);

#define ALLOC_METHOD_EXCHANGE method_exchangeImplementations(classAllocMethod, classMyAllocMethod)

#define RETAIN_METHOD_EXCHANGE method_exchangeImplementations(classRetainMethod, classMyRetainMethod)

+ (void)turnMemCheckOn

{

if( memData == nil )

memData = [[NSMutableArray allocWithZone:nil] init];

//alloc

classAllocMethod = class_getClassMethod([NSObject class], @selector(alloc) );

classAllocImp = method_getImplementation(classAllocMethod);

classMyAllocMethod = class_getClassMethod([NSObject class], @selector(myAllocFunc) );

classMyAllocImp = method_getImplementation(classMyAllocMethod);

//dealloc

classDeallocMethod = class_getInstanceMethod([NSObject class], @selector(dealloc) );

classDeallocImp = method_getImplementation(classDeallocMethod);

classMyDeallocMethod = class_getInstanceMethod([NSObject class], @selector(myDeallocFunc) );

classMyDeallocImp = method_getImplementation(classMyDeallocMethod);

//retain

classRetainMethod = class_getInstanceMethod([NSObject class], @selector(retain) );

classRetainImp = method_getImplementation(classRetainMethod);

classMyRetainMethod = class_getInstanceMethod([NSObject class], @selector(myRetainFunc) );

classMyRetainImp = method_getImplementation(classMyRetainMethod);

//release

classReleaseMethod = class_getInstanceMethod([NSObject class], @selector(release) );

classReleaseImp = method_getImplementation(classReleaseMethod);

classMyReleaseMethod = class_getInstanceMethod([NSObject class], @selector(myReleaseFunc) );

classMyReleaseImp = method_getImplementation(classMyReleaseMethod);

ALLOC_METHOD_EXCHANGE;

method_exchangeImplementations(classDeallocMethod, classMyDeallocMethod);

RETAIN_METHOD_EXCHANGE;

method_exchangeImplementations(classReleaseMethod, classMyReleaseMethod);

}

В своем методе я вызываю базовый передавая как и obj_msgSend первые два параметра id и SEL чтобы получить новый объект, если он новый то сохраняю его в глобальный массив memData в самое начало не увеличивая счетчика ссылок. Для получения callstack-а генерируется исключение и тут же ловится. На производительности в симуляторе это сказывается не сильно. ALLOC_METHOD_EXCHANGE вызывается для того чтобы при создании NSMemCheckObject и NSException не уйти в рекурсию.

+ (id) myAllocFunc

{

//call base implement

OverrideMemCheckPrototipe f = (OverrideMemCheckPrototipe)classAllocImp;

id newPt = f(self,@selector(myAllocFunc));

@synchronized( [NSObject class] )

{

if( ![newPt isKindOfClass:[NSMemCheckObject class]] )

{

BOOL found = NO;

for( NSMemCheckObject* obj in memData )

if( obj.pointerValue == newPt )

{

found = YES;

break;

}

if( !found )

{

ALLOC_METHOD_EXCHANGE;

NSMemCheckObject* addObj = [[[NSMemCheckObject alloc] initWithPointer:newPt] autorelease];

[memData insertObject:addObj atIndex:0];

//hack to get call stack

@try

{

@throw [NSException exceptionWithName:@"memTestException"

reason:@"get call stack"

userInfo:nil];

}

@catch (NSException * e)

{

addObj.callStack = [e callStackSymbols];

}

ALLOC_METHOD_EXCHANGE;

}

}

}

return newPt;

}

В реализации myDeallocFunc происходит простое удаление элемента из массива

- (void)myDeallocFunc

{

@synchronized( [NSObject class] )

{

int i = [memData count]-1;

while( i>=0 )

{

if( ((NSMemCheckObject*)[memData objectAtIndex:i]).pointerValue == self )

{

[memData removeObjectAtIndex:i];

//ShowMemData();

break;

}

--i;

}

}

//call base implement

OverrideMemCheckPrototipe f = (OverrideMemCheckPrototipe)classDeallocImp;

f(self,@selector(myDeallocFunc));

}

В myRetainFunc необходимо проверять какая имплементация находится в alloc, чтобы правильно подменить на оригинальную:
- (id)myRetainFunc

{

@synchronized( [NSObject class] )

{

BOOL needAllocExchange = ( method_getImplementation(classAllocMethod) == classMyAllocImp );


if( needAllocExchange )

ALLOC_METHOD_EXCHANGE;

RETAIN_METHOD_EXCHANGE;


NSMemCheckObject* addObj = [memData memCheckObjectByPointer:self];

//hack to get call stack

if( addObj )

{

@try

{

@throw [NSException exceptionWithName:@"memTestException"

reason:@"get call stack"

userInfo:nil];

}

@catch (NSException * e)

{

NSMemCheckRetainReleaseInfo* info = [[NSMemCheckRetainReleaseInfo alloc] init];

info.date = [NSDate date];

info.callStack = [e callStackSymbols];

[addObj.retainCallStackArray addObject:info];

[info release];

}

}

if( needAllocExchange )

ALLOC_METHOD_EXCHANGE;

RETAIN_METHOD_EXCHANGE;

}

//call base implement

OverrideMemCheckPrototipe f = (OverrideMemCheckPrototipe)classRetainImp;

return f(self,@selector(myRetainFunc));

}