用于生成/合成属性的Xcode脚本
有没有人有一个Xcode脚本来为类中的实例变量生成@property和@synthsize指令?这是我在很久以前发现的脚本的基础上提出的一个脚本,用Python重写,改进后可以一次生成多个属性,等等 它将使用(copy)作为属性为所有选定实例变量生成属性 仍然有一些边缘案例在一个文件中有多个@interface或@implementation,还有一些具有不寻常的标识符或星号位置(如*const),但它应该涵盖大多数典型的编码样式。如果您修复了这些情况,请随意编辑/发布修改用于生成/合成属性的Xcode脚本,xcode,scripting,Xcode,Scripting,有没有人有一个Xcode脚本来为类中的实例变量生成@property和@synthsize指令?这是我在很久以前发现的脚本的基础上提出的一个脚本,用Python重写,改进后可以一次生成多个属性,等等 它将使用(copy)作为属性为所有选定实例变量生成属性 仍然有一些边缘案例在一个文件中有多个@interface或@implementation,还有一些具有不寻常的标识符或星号位置(如*const),但它应该涵盖大多数典型的编码样式。如果您修复了这些情况,请随意编辑/发布修改 #!/usr/bin
#!/usr/bin/python
# Takes a header file with one or more instance variables selected
# and creates properties and synthesize directives for the selected properties.
# Accepts google-style instance variables with a tailing underscore and
# creates an appropriately named property without underscore.
# Entire Document
# Home Directory
# Discard Output
# Display in Alert
import os
import re
import subprocess
# AppleScripts for altering contents of files via Xcode
setFileContentsScript = """\
on run argv
set fileAlias to POSIX file (item 1 of argv)
set newDocText to (item 2 of argv)
tell application "Xcode"
set doc to open fileAlias
set text of doc to newDocText
end tell
end run \
"""
getFileContentsScript = """\
on run argv
set fileAlias to POSIX file (item 1 of argv)
tell application "Xcode"
set doc to open fileAlias
set docText to text of doc
end tell
return docText
end run \
"""
# Get variables from Xcode
headerFileText = """%%%{PBXAllText}%%%"""
selectionStartIndex = %%%{PBXSelectionStart}%%%
selectionEndIndex = %%%{PBXSelectionEnd}%%%
selectedText = headerFileText[selectionStartIndex:selectionEndIndex]
headerFilePath = """%%%{PBXFilePath}%%%"""
# Look for an implementation file with .m or .mm extension
implementationFilePath = headerFilePath[:-1] + "m"
if not os.path.exists(implementationFilePath):
implementationFilePath += "m"
instanceVariablesRegex = re.compile(
"""^\s*((?:(?:\w+)\s+)*(?:(?:\w+)))""" + # Identifier(s)
"""([*]?)\\s*""" + # An optional asterisk
"""(\\w+?)(_?);""", # The variable name
re.M)
# Now for each instance variable in the selected section
properties = ""
synthesizes = ""
for lineMatch in instanceVariablesRegex.findall(selectedText):
types = " ".join(lineMatch[0].split()) # Clean up consequtive whitespace
asterisk = lineMatch[1]
variableName = lineMatch[2]
trailingUnderscore = lineMatch[3]
pointerPropertyAttributes = "(copy) " # Attributes if variable is pointer
if not asterisk:
pointerPropertyAttributes = ""
newProperty = "@property %s%s %s%s;\n" % (pointerPropertyAttributes,
types,
asterisk,
variableName)
# If there's a trailing underscore, we need to let the synthesize
# know which backing variable it's using
newSynthesize = "@synthesize %s%s;\n" % (variableName,
trailingUnderscore and
" = %s_" % variableName)
properties += newProperty
synthesizes += newSynthesize
# Check to make sure at least 1 properties was found to generate
if not properties:
os.sys.stderr.writelines("No properties found to generate")
exit(-1)
# We want to insert the new properties either immediately after the last
# existing property or at the end of the instance variable section
findLastPropertyRegex = re.compile("^@interface.*?{.*?}.*?\\n" +
"(?:.*^\\s*@property.*?\\n)?", re.M | re.S)
headerInsertIndex = findLastPropertyRegex.search(headerFileText).end()
# Add new lines on either side if this is the only property in the file
addedNewLine = "\n"
if re.search("^\s*@property", headerFileText, re.M):
# Not the only property, don't add
addedNewLine = ""
newHeaderFileText = "%s%s%s%s" % (headerFileText[:headerInsertIndex],
addedNewLine,
properties,
headerFileText[headerInsertIndex:])
subprocess.call(["osascript",
"-e",
setFileContentsScript,
headerFilePath,
newHeaderFileText])
if not os.path.exists(implementationFilePath):
os.sys.stdout.writelines("No implementation file found")
exit(0)
implementationFileText = subprocess.Popen(
["osascript",
"-e",
getFileContentsScript,
implementationFilePath],
stdout=subprocess.PIPE).communicate()[0]
# We want to insert the synthesizes either immediately after the last existing
# @synthesize or after the @implementation directive
lastSynthesizeRegex = re.compile("^\\s*@implementation.*?\\n" +
"(?:.*^\\s*@synthesize.*?\\n)?", re.M | re.S)
implementationInsertIndex = \
lastSynthesizeRegex.search(implementationFileText).end()
# Add new lines on either side if this is the only synthesize in the file
addedNewLine = "\n"
if re.search("^\s*@synthesize", implementationFileText, re.M):
# Not the only synthesize, don't add
addedNewLine = ""
newImplementationFileText = "%s%s%s%s" % \
(implementationFileText[:implementationInsertIndex],
addedNewLine,
synthesizes,
implementationFileText[implementationInsertIndex:])
subprocess.call(["osascript",
"-e",
setFileContentsScript,
implementationFilePath,
newImplementationFileText])
# Switch Xcode back to header file
subprocess.Popen(["osascript",
"-e",
getFileContentsScript,
headerFilePath],
stdout=subprocess.PIPE).communicate()
这里是我昨天写的一个@property指令,在几个小时后遇到这个问题之前。这是一个简单的文本过滤器,将其扩展到@synthesis指令(将适当的
when
子句添加到case
语句中,并对when block\u end
条件进行适当的添加)是很简单的,扩展它以处理一个文件中多次出现的@interface/@实现(通过跟踪它们的名称——可以通过regexp捕获来完成,就像脚本中的所有内容一样)所需的工作量也不多:
#/usr/bin/ruby
#-------基本定义-----------------------------
doc=“%%{PBXFilePath}%%”
#正则表达式
搜索[U exp=/[:space:][]*([[a-zA-Z0-9]]*)[[:space:][]\*([a-zA-Z0-9]*)/
接口_start=/@接口/
块_end=/^\}/
#初始化变量
属性列表=[]
属性_string=“”
正在读取\u接口=0
#----------------开始处理-----------------------------
file=file.open(doc,“r”).readlines
file.each do|line|
#捕获仅在中匹配的正则表达式
#接口声明并打印出匹配的
#财产声明
箱线
#开始捕捉
什么时候开始
读取接口=1
放线
#捕获并保留在属性列表中
当搜索
如果(reading_interface==1),则
数据=Regexp.last\u匹配
属性列表我使用附加器来实现这一点,还有更多
非常便宜且功能强大。这是我目前使用的用户脚本-它一次只能处理一个实例变量。它尝试使用正确的保留机制(不保留简单类型),并在实现文件中创建@synthesis语句——目前它还没有为您创建dealloc语句
#! /usr/bin/perl -w
#Input: Selection
#Directory: Selection
#Output: Display in Alert
#Errors: Display in Alert
use strict;
# Get the header file contents from Xcode user scripts
my $headerFileContents = <<'HEADERFILECONTENTS';
%%%{PBXAllText}%%%
HEADERFILECONTENTS
# Get the indices of the selection from Xcode user scripts
my $selectionStartIndex = %%%{PBXSelectionStart}%%%;
my $selectionEndIndex = %%%{PBXSelectionEnd}%%%;
# Get path of the header file
my $implementationFilePath = "%%%{PBXFilePath}%%%";
my $headerFilePath = $implementationFilePath;
# Look for an implemenation file with a ".m" or ".mm" extension
$implementationFilePath =~ s/\.[hm]*$/.m/;
if (!(-e $implementationFilePath))
{
$implementationFilePath =~ s/.m$/.mm/;
}
# Handle subroutine to trime whitespace off both ends of a string
sub trim
{
my $string = shift;
$string =~ s/^\s*(.*?)\s*$/$1/;
return $string;
}
# Get the selection out of the header file
my $selectedText = substr $headerFileContents, $selectionStartIndex, ($selectionEndIndex - $selectionStartIndex);
#my $otherText = substr $headerFileContents, $selectionStartIndex;
#my $pulledText = "";
#if ( length($otherText) && $otherText =~ /.*$(^.*;).*/ )
#{
# $pulledText = $1;
#}
#
#
#print $pulledText;
$selectedText = trim $selectedText;
my $type = "";
my $asterisk = "";
my $name = "";
my $behavior = "";
my $iboutlet = "";
# Test that the selection is:
# At series of identifiers (the type name and access specifiers)
# Possibly an asterisk
# Another identifier (the variable name)
# A semi-colon
if (length($selectedText) && ($selectedText =~ /([_A-Za-z][_A-Za-z0-9]*\s*)+([\s\*]+)([_A-Za-z][_A-Za-z0-9]*)/))
{
$type = $1;
$type = trim $type;
$asterisk = $2;
$asterisk = trim $asterisk;
$name = $3;
$behavior = "";
if (defined($asterisk) && length($asterisk) == 1)
{
$behavior = "(nonatomic, retain) ";
}
else
{
$behavior = "(nonatomic) ";
$asterisk = "";
}
}
else
{
print "Bailing, error in Regex";
exit 1;
}
# special case, see if we need to keep around an IBOUTLET declaration.
if ( length($selectedText) && ($selectedText =~ /IBOutlet/) )
{
$iboutlet = "IBOutlet ";
}
# Find the closing brace (end of the class variables section)
my $remainderOfHeader = substr $headerFileContents, $selectionEndIndex;
my $indexAfterClosingBrace = $selectionEndIndex + index($remainderOfHeader, "\n}\n") + 3;
if ($indexAfterClosingBrace == -1)
{
exit 1;
}
# Determine if we need to add a newline in front of the property declaration
my $leadingNewline = "\n";
if (substr($headerFileContents, $indexAfterClosingBrace, 1) eq "\n")
{
$indexAfterClosingBrace += 1;
$leadingNewline = "";
}
# Determine if we need to add a newline after the property declaration
my $trailingNewline = "\n";
if (substr($headerFileContents, $indexAfterClosingBrace, 9) eq "\@property")
{
$trailingNewline = "";
}
# Create and insert the proper declaration
my $propertyDeclaration = $leadingNewline . "\@property " . $behavior . $iboutlet . $type . " " . $asterisk . $name . ";\n" . $trailingNewline;
substr($headerFileContents, $indexAfterClosingBrace, 0) = $propertyDeclaration;
my $replaceFileContentsScript = <<'REPLACEFILESCRIPT';
on run argv
set fileAlias to POSIX file (item 1 of argv)
set newDocText to (item 2 of argv)
tell application "Xcode"
set doc to open fileAlias
set text of doc to newDocText
end tell
end run
REPLACEFILESCRIPT
# Use Applescript to replace the contents of the header file
# (I could have used the "Output" of the Xcode user script instead)
system 'osascript', '-e', $replaceFileContentsScript, $headerFilePath, $headerFileContents;
# Stop now if the implementation file can't be found
if (!(-e $implementationFilePath))
{
exit 1;
}
my $getFileContentsScript = <<'GETFILESCRIPT';
on run argv
set fileAlias to POSIX file (item 1 of argv)
tell application "Xcode"
set doc to open fileAlias
set docText to text of doc
end tell
return docText
end run
GETFILESCRIPT
# Get the contents of the implmentation file
open(SCRIPTFILE, '-|') || exec 'osascript', '-e', $getFileContentsScript, $implementationFilePath;
my $implementationFileContents = do {local $/; <SCRIPTFILE>};
close(SCRIPTFILE);
# Look for the class implementation statement
if (length($implementationFileContents) && ($implementationFileContents =~ /(\@implementation [_A-Za-z][_A-Za-z0-9]*\n)/))
{
my $matchString = $1;
my $indexAfterMatch = index($implementationFileContents, $matchString) + length($matchString);
# Determine if we want a newline before the synthesize statement
$leadingNewline = "\n";
if (substr($implementationFileContents, $indexAfterMatch, 1) eq "\n")
{
$indexAfterMatch += 1;
$leadingNewline = "";
}
# Determine if we want a newline after the synthesize statement
$trailingNewline = "\n";
if (substr($implementationFileContents, $indexAfterMatch, 11) eq "\@synthesize")
{
$trailingNewline = "";
}
# Create and insert the synthesize statement
my $synthesizeStatement = $leadingNewline . "\@synthesize " . $name . ";\n" . $trailingNewline;
substr($implementationFileContents, $indexAfterMatch, 0) = $synthesizeStatement;
# Use Applescript to replace the contents of the implementation file in Xcode
system 'osascript', '-e', $replaceFileContentsScript, $implementationFilePath, $implementationFileContents;
}
exit 0;
#/usr/bin/perl-w
#输入:选择
#目录:选择
#输出:在警报中显示
#错误:在警报中显示
严格使用;
#从Xcode用户脚本获取头文件内容
我的$headerFileContents=附加器可以做这些事情,还有更多。它还处理自定义前缀和后缀(后缀)。如果你想要谷歌的下划线,你就得到了。如果您想更改它,请动态更改它-无需编辑脚本。此外,还有一个defaults表,您可以根据传入的ivar类型(复制、保留、只读、分配等)定义默认属性说明符。它执行IBOutlet检测,并自动插入IBOutlet关键字,nils out your views for-viewDidUnload,执行多种样式的dealloc。它还为集合(NSMutableArray和NSSet)编写所有毛茸茸的访问器。它可以进行密钥存档、各种锁定方法,它可以对属性进行排序并合成块、编写KVO代码、单例代码、转换为选择器、生成HeaderDoc标记、NSLog()等等。。。它还有一个灵活的“样式”选项卡,用于在换行符上放大括号或不放大括号、空格、自定义参数名称等。大多数事情都是通过服务处理的,所以您只需选择ivar块,按一两下键盘就可以了。如果您将附加器最小化到dock,它的接口就不会出现在最前面,这样您就可以专注于Xcode或任何其他支持服务的编辑器。当然,Accessorizer也会写出显式的访问器(如Objective-C1.0中的那样),并允许您覆盖属性—所有这些都只需简单地切换开关即可。您甚至可以根据传入的类型自定义覆盖。观看视频以查看它的运行情况。检查:这是一个针对Xcode 3.2.4的python脚本,用于生成;接口属性、实现和解除锁定。
要安装、复制此脚本,请转到Xcode脚本菜单(从第二个到最后一个)
“编辑用户脚本…”
将其添加到“代码”下,创建一个新的脚本名称,然后将python脚本粘贴到下面
要使用,只需选择@接口下的变量,然后调用此脚本。
然后,它将在实现中添加所有@property
以及所有的@synthesis和dealoc。
它不会将IBOutlet添加到任何标签或按钮,因为它不知道这一点,但是
易于手动添加
下面脚本的缩进非常重要,所以不要更改它
#!/usr/bin/python
# Takes a header file with one or more instance variables selected
# and creates properties and synthesize directives for the selected properties.
# Accepts google-style instance variables with a tailing underscore and
# creates an appropriately named property without underscore.
# Xcode script options should be as follows:
# Entire Document
# Home Directory
# Discard Output
# Display in Alert
import os
import re
import subprocess
# AppleScripts for altering contents of files via Xcode
setFileContentsScript = """\
on run argv
set fileAlias to POSIX file (item 1 of argv)
set newDocText to (item 2 of argv)
tell application "Xcode"
set doc to open fileAlias
set text of doc to newDocText
end tell
end run \
"""
getFileContentsScript = """\
on run argv
set fileAlias to POSIX file (item 1 of argv)
tell application "Xcode"
set doc to open fileAlias
set docText to text of doc
end tell
return docText
end run \
"""
# Get variables from Xcode
headerFileText = """%%%{PBXAllText}%%%"""
selectionStartIndex = %%%{PBXSelectionStart}%%%
selectionEndIndex = %%%{PBXSelectionEnd}%%%
selectedText = headerFileText[selectionStartIndex:selectionEndIndex]
headerFilePath = """%%%{PBXFilePath}%%%"""
# Look for an implementation file with .m or .mm extension
implementationFilePath = headerFilePath[:-1] + "m"
if not os.path.exists(implementationFilePath):
implementationFilePath += "m"
instanceVariablesRegex = re.compile(
"""^\s*((?:(?:\\b\w+\\b)\s+)*(?:(?:\\b\\w+\\b)))\\s*""" + # Identifier(s)
"""([*]?)\\s*""" + # An optional asterisk
"""(\\b\\w+?)(_?\\b);""", # The variable name
re.M)
# Now for each instance variable in the selected section
properties = ""
synthesizes = ""
deallocs = ""
for lineMatch in instanceVariablesRegex.findall(selectedText):
types = " ".join(lineMatch[0].split()) # Clean up consequtive whitespace
asterisk = lineMatch[1]
variableName = lineMatch[2]
trailingUnderscore = lineMatch[3]
pointerPropertyAttributes = "(nonatomic, retain) " # Attributes if variable is pointer
if not asterisk:
pointerPropertyAttributes = "(nonatomic, assign) "
newProperty = "@property %s%s %s%s;\n" % (pointerPropertyAttributes,
types,
asterisk,
variableName)
# If there's a trailing underscore, we need to let the synthesize
# know which backing variable it's using
newSynthesize = "@synthesize %s%s;\n" % (variableName,
trailingUnderscore and
" = %s_" % variableName)
# only do the objects
if asterisk:
newDealloc = " [%s%s release];\n" % (variableName,
trailingUnderscore and
" = %s_" % variableName)
properties += newProperty
synthesizes += newSynthesize
# only add if it's an object
if asterisk:
deallocs += newDealloc
# Check to make sure at least 1 properties was found to generate
if not properties:
os.sys.stderr.writelines("No properties found to generate")
exit(-1)
# We want to insert the new properties either immediately after the last
# existing property or at the end of the instance variable section
findLastPropertyRegex = re.compile("^@interface.*?{.*?}.*?\\n" +
"(?:.*^\\s*@property.*?\\n)?", re.M | re.S)
headerInsertIndex = findLastPropertyRegex.search(headerFileText).end()
# Add new lines on either side if this is the only property in the file
addedNewLine = "\n"
if re.search("^\s*@property", headerFileText, re.M):
# Not the only property, don't add
addedNewLine = ""
newHeaderFileText = "%s%s%s%s" % (headerFileText[:headerInsertIndex],
addedNewLine,
properties,
headerFileText[headerInsertIndex:])
subprocess.call(["osascript",
"-e",
setFileContentsScript,
headerFilePath,
newHeaderFileText])
if not os.path.exists(implementationFilePath):
os.sys.stdout.writelines("No implementation file found")
exit(0)
implementationFileText = subprocess.Popen(
["osascript",
"-e",
getFileContentsScript,
implementationFilePath],
stdout=subprocess.PIPE).communicate()[0]
# We want to insert the synthesizes either immediately after the last existing
# @synthesize or after the @implementation directive
lastSynthesizeRegex = re.compile("^\\s*@implementation.*?\\n" +
"(?:.*^\\s*@synthesize.*?\\n)?", re.M | re.S)
implementationInsertIndex = \
lastSynthesizeRegex.search(implementationFileText).end()
# Add new lines on either side if this is the only synthsize in the file
addedNewLine = "\n"
if re.search("^\s*@synthesize", implementationFileText, re.M):
# Not the only synthesize, don't add
addedNewLine = ""
newImplementationFileText = "%s%s%s%s" % \
(implementationFileText[:implementationInsertIndex],
addedNewLine,
synthesizes,
implementationFileText[implementationInsertIndex:])
subprocess.call(["osascript",
"-e",
setFileContentsScript,
implementationFilePath,
newImplementationFileText])
implementationFileText = subprocess.Popen(
["osascript",
"-e",
getFileContentsScript,
implementationFilePath],
stdout=subprocess.PIPE).communicate()[0]
# We want to insert the deallocs either immediately after the last existing
# [* release] or after the [super dealloc]
lastDeallocRegex = re.compile("^\\s+\[super dealloc\];?\\n" +
"(?:.*^\\s+\[\w release\];?\\n)?", re.M | re.S)
deallocInsertIndex = \
lastDeallocRegex.search(implementationFileText).end()
addedNewDeallocLine = "\n"
if re.search("^\s*\[\w release\];?", implementationFileText, re.M):
# Not the only dealloc, don't add
addedNewDeallocLine = ""
newImplementationFileText = "%s%s%s%s" % \
(implementationFileText[:deallocInsertIndex],
addedNewDeallocLine,
deallocs,
implementationFileText[deallocInsertIndex:])
subprocess.call(["osascript",
"-e",
setFileContentsScript,
implementationFilePath,
newImplementationFileText])
# Switch Xcode back to header file
subprocess.Popen(["osascript",
"-e",
getFileContentsScript,
headerFilePath],
stdout=subprocess.PIPE).communicate()
哇,这里有很多疯狂的脚本
从Xcode 4.4开始(可能在之前)。。。您的IVAR
s将自动合成。。例如
@property (assign) BOOL automatically;
@property (strong) NSArray *believeDat;
可以通过
并通过自动生成的前导下划线直接编辑实例变量,如
_believeDat = @["thank you, jesus", @"mary poopins"];
不需要@synthesis
对于此类@属性的快速轻松输入
。。。将以下内容一次拖一个到“代码段”库中。。您可以指定键盘快捷键来插入这些跳转点,以便更快地输入属性。我对对象使用rrr,对原语使用aaa。。但那只是我
@属性(非原子,赋值)代码>
@属性(非原子,保留)*代码>
最后但并非最不重要的一点,有些人可能会说我疯了。。但是我在我的.pch
中加入了以下宏,以进一步加快、澄清并使过程更加简洁。。所有常见的宏免责声明均适用。
self.automatically = YES;
_believeDat = @["thank you, jesus", @"mary poopins"];
#define RONLY readonly
#define RDWRT readwrite
#define NATOM nonatomic
#define STRNG strong
#define ASS assign
#define CP copy
#define SET setter
#define GET getter
@property (NATOM, STRNG) NSA* fonts;
@property (NATOM, STRNG) NSS* cachedPath;